From: Norbert Bizet Date: Thu, 2 Nov 2017 14:26:40 +0000 (+0100) Subject: Add restore wizard to the tray monitor. X-Git-Tag: Release-9.0.6~47 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=9bdcb39174ab72b24606092a1a0c541c631d2dd3;p=bacula%2Fbacula Add restore wizard to the tray monitor. --- diff --git a/bacula/src/qt-console/bat.pro.mingw64 b/bacula/src/qt-console/bat.pro.mingw64 index 1765985f8a..4812fecaac 100644 --- a/bacula/src/qt-console/bat.pro.mingw64 +++ b/bacula/src/qt-console/bat.pro.mingw64 @@ -24,7 +24,7 @@ cross-win32 { LIBS += -mwindows -L../win32/release64 -lbacula } !cross-win32 { - LIBS += -L../lib -lbac -L../findlib -lbacfind -L/opt/local/lib -lssl -lcrypto + LIBS += -L../lib -lbac -L../findlib -lbacfind -lssl -lcrypto } qwt { diff --git a/bacula/src/qt-console/tray-monitor/clientselectwizardpage.cpp b/bacula/src/qt-console/tray-monitor/clientselectwizardpage.cpp new file mode 100644 index 0000000000..4b06c297db --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/clientselectwizardpage.cpp @@ -0,0 +1,50 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#include "clientselectwizardpage.h" +#include "ui_clientselectwizardpage.h" +#include "common.h" +ClientSelectWizardPage::ClientSelectWizardPage(QWidget *parent) : + QWizardPage(parent), + ui(new Ui::ClientSelectWizardPage) +{ + ui->setupUi(this); + + registerField("currentClient*", ui->backupClientComboBox); +} + +ClientSelectWizardPage::~ClientSelectWizardPage() +{ + delete ui; +} + +void ClientSelectWizardPage::setClients(alist *lst) +{ + if (lst && lst->size() > 0) { + ui->backupClientComboBox->clear(); + QStringList list; + char *str; + foreach_alist(str, lst) { + list << QString(str); + } + ui->backupClientComboBox->addItems(list); + ui->backupClientComboBox->setEnabled(true); + } else { + ui->backupClientComboBox->setEnabled(false); + } +} diff --git a/bacula/src/qt-console/tray-monitor/clientselectwizardpage.h b/bacula/src/qt-console/tray-monitor/clientselectwizardpage.h new file mode 100644 index 0000000000..c0a2fd963b --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/clientselectwizardpage.h @@ -0,0 +1,44 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#ifndef CLIENTSELECTWIZARDPAGE_H +#define CLIENTSELECTWIZARDPAGE_H + +#include + +class alist; + +namespace Ui { +class ClientSelectWizardPage; +} + +class ClientSelectWizardPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit ClientSelectWizardPage(QWidget *parent = 0); + ~ClientSelectWizardPage(); + + void setClients(alist *lst); + +private: + Ui::ClientSelectWizardPage *ui; +}; + +#endif // CLIENTSELECTWIZARDPAGE_H diff --git a/bacula/src/qt-console/tray-monitor/clientselectwizardpage.ui b/bacula/src/qt-console/tray-monitor/clientselectwizardpage.ui new file mode 100644 index 0000000000..484d0d96c2 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/clientselectwizardpage.ui @@ -0,0 +1,68 @@ + + + ClientSelectWizardPage + + + + 0 + 0 + 714 + 330 + + + + WizardPage + + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + Qt::DefaultContextMenu + + + Backup Client: + + + + + + + + 0 + 0 + + + + + 595 + 0 + + + + + + + -1 + + + + + + + + diff --git a/bacula/src/qt-console/tray-monitor/fileselectwizardpage.cpp b/bacula/src/qt-console/tray-monitor/fileselectwizardpage.cpp new file mode 100644 index 0000000000..746b070c28 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/fileselectwizardpage.cpp @@ -0,0 +1,82 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#include "fileselectwizardpage.h" +#include "ui_fileselectwizardpage.h" +#include "task.h" +#include "filesmodel.h" +#include + +FileSelectWizardPage::FileSelectWizardPage(QWidget *parent) : + QWizardPage(parent), + ui(new Ui::FileSelectWizardPage), + currentPathId(0) +{ + ui->setupUi(this); + registerField("currentFile", this, "currentFile", "currentFileChanged"); + + connect(ui->sourceListView, SIGNAL(activated(const QModelIndex&)), this, SLOT(changeCurrentFolder(const QModelIndex&))); + ui->sourceListView->setLayoutMode(QListView::Batched); + ui->sourceListView->setBatchSize(BATCH_SIZE); + + /* FIXME : need context menu + shortcuts for del */ + FileDestModel *destModel = new FileDestModel(); + connect(destModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(completeChanged())); + connect(destModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(completeChanged())); + ui->destListView->setModel(destModel); + + +} + +FileSelectWizardPage::~FileSelectWizardPage() +{ + delete ui; +} + +void FileSelectWizardPage::setModels(QStandardItemModel *src_model, QStandardItemModel *dest_model) +{ + ui->sourceListView->setModel(src_model); + src_model->clear(); + currentPathId = 0; + + ui->destListView->setModel(dest_model); + connect(dest_model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SIGNAL(completeChanged())); + connect(dest_model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SIGNAL(completeChanged())); + +} + +qulonglong FileSelectWizardPage::currentFile() const +{ + return currentPathId; +} + +void FileSelectWizardPage::changeCurrentFolder(const QModelIndex& current) +{ + if (current.isValid() && ui->sourceListView->model()) { + QStandardItem *item = ((QStandardItemModel *)ui->sourceListView->model())->itemFromIndex(current); + if (item && item->data(TypeRole) == TYPEROLE_DIRECTORY) { + currentPathId = item->data(PathIdRole).toULongLong(); + emit currentFileChanged(); + } + } +} + +bool FileSelectWizardPage::isComplete() const +{ + return ui->destListView->model() && ui->destListView->model()->rowCount() != 0; +} diff --git a/bacula/src/qt-console/tray-monitor/fileselectwizardpage.h b/bacula/src/qt-console/tray-monitor/fileselectwizardpage.h new file mode 100644 index 0000000000..c054b65840 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/fileselectwizardpage.h @@ -0,0 +1,57 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#ifndef FILESELECTWIZARDPAGE_H +#define FILESELECTWIZARDPAGE_H + +#include + +class QStandardItemModel; +class QModelIndex; + +namespace Ui { +class FileSelectWizardPage; +} + +class FileSelectWizardPage : public QWizardPage +{ + Q_OBJECT + Q_PROPERTY(qulonglong currentFile READ currentFile NOTIFY currentFileChanged) + +public: + explicit FileSelectWizardPage(QWidget *parent = 0); + ~FileSelectWizardPage(); + + void setModels(QStandardItemModel *src_model, QStandardItemModel *dest_model); + + qulonglong currentFile() const; + + bool isComplete() const; + +signals: + void currentFileChanged(); + +protected slots: + void changeCurrentFolder(const QModelIndex& current); + +private: + Ui::FileSelectWizardPage *ui; + qulonglong currentPathId; +}; + +#endif // FILESELECTWIZARDPAGE_H diff --git a/bacula/src/qt-console/tray-monitor/fileselectwizardpage.ui b/bacula/src/qt-console/tray-monitor/fileselectwizardpage.ui new file mode 100644 index 0000000000..4413c9ff85 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/fileselectwizardpage.ui @@ -0,0 +1,89 @@ + + + FileSelectWizardPage + + + + 0 + 0 + 739 + 437 + + + + WizardPage + + + + + + Drag from left to right + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + true + + + QAbstractItemView::DragOnly + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + QListView::Batched + + + + + + + false + + + QAbstractItemView::NoEditTriggers + + + true + + + false + + + QAbstractItemView::DropOnly + + + Qt::CopyAction + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + QListView::Batched + + + + + + + + + + + diff --git a/bacula/src/qt-console/tray-monitor/filesmodel.h b/bacula/src/qt-console/tray-monitor/filesmodel.h new file mode 100644 index 0000000000..1940fcfda9 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/filesmodel.h @@ -0,0 +1,197 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#ifndef FILESMODEL_H +#define FILESMODEL_H +#include +#include +#include +#include "task.h" +enum { + PathIdRole = Qt::UserRole+1, + FilenameIdRole = Qt::UserRole+2, + FileIdRole = Qt::UserRole+3, + JobIdRole = Qt::UserRole+4, + LStatRole = Qt::UserRole+5, + PathRole = Qt::UserRole+6, + TypeRole = Qt::UserRole+7 +}; + +enum { + TYPEROLE_DIRECTORY = 0, + TYPEROLE_FILE +}; + +class DirectoryItem : public QStandardItem +{ +public: + DirectoryItem() : QStandardItem() + { + /* explicit set the data, so it can be serialized in Mime Data for D&D */ + setData(QVariant(TYPEROLE_DIRECTORY), TypeRole); + } + QVariant data(int role = Qt::UserRole + 1) const + { + if (role == Qt::DecorationRole) { + QFileIconProvider provider; + return provider.icon(QFileIconProvider::Folder); + } + + return QStandardItem::data(role); + } +}; + +class FileItem : public QStandardItem +{ +public: + FileItem() : QStandardItem() + { + /* explicit set the data, so it can be serialized in Mime Data for D&D */ + setData(QVariant(TYPEROLE_FILE), TypeRole); + } + enum { + FILE_TYPE = UserType +2 + }; + + QVariant data(int role = Qt::UserRole + 1) const + { + if (role == Qt::DecorationRole) { + QFileIconProvider provider; + return provider.icon(QFileIconProvider::File); + } + + return QStandardItem::data(role); + } +}; + +#define BATCH_SIZE 100 +class FileSourceModel : public QStandardItemModel +{ +public: + FileSourceModel() : QStandardItemModel(), + m_cursor(0), + m_batchSize(BATCH_SIZE), + m_canFetchMore(true) + {} + + bool canFetchMore(const QModelIndex &parent) const + { + Q_UNUSED(parent) + return false/*m_canFetchMore*/; + } + void fetchMore(const QModelIndex &parent) + { + Q_UNUSED(parent) + } + +public slots: + void taskComplete(task *t) + { + // increase cursor + // update canfetch + // deletelater + t->deleteLater(); + } + +private: + u_int64_t m_cursor; + u_int64_t m_batchSize; + bool m_canFetchMore; +}; + +class FileDestModel : public QStandardItemModel +{ + bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const + { + Q_UNUSED(action) + Q_UNUSED(row) + Q_UNUSED(column) + Q_UNUSED(parent) + + if (data->hasFormat("application/x-qstandarditemmodeldatalist")) { + QByteArray encoded = data->data("application/x-qabstractitemmodeldatalist"); + QDataStream stream(&encoded, QIODevice::ReadOnly); + + while (!stream.atEnd()) + { + int row, col; + QMap roleDataMap; + stream >> row >> col >> roleDataMap; + + /* do something with the data */ + int type = roleDataMap[TypeRole].toInt(); + + switch(type) { + case TYPEROLE_DIRECTORY: + case TYPEROLE_FILE: + break; + default: + return false; + } + } + return true; + } + return false; + } + + bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) + { + Q_UNUSED(action) + Q_UNUSED(row) + Q_UNUSED(column) + Q_UNUSED(parent) + + QByteArray encoded = data->data("application/x-qabstractitemmodeldatalist"); + QDataStream stream(&encoded, QIODevice::ReadOnly); + + while (!stream.atEnd()) + { + int row, col; + QMap roleDataMap; + stream >> row >> col >> roleDataMap; + + QStandardItem *item; + /* do something with the data */ + int type = roleDataMap[TypeRole].toInt(); + + switch(type) { + case TYPEROLE_DIRECTORY: + item = new DirectoryItem(); + break; + case TYPEROLE_FILE: + item = new FileItem(); + break; + default: + return false; + } + + item->setData(roleDataMap[PathIdRole], PathIdRole); + item->setData(roleDataMap[FilenameIdRole], FilenameIdRole); + item->setData(roleDataMap[FileIdRole], FileIdRole); + item->setData(roleDataMap[JobIdRole], JobIdRole); + item->setData(roleDataMap[LStatRole], LStatRole); + item->setData(roleDataMap[PathRole], PathRole); + item->setData(roleDataMap[PathRole], Qt::DisplayRole); + + appendRow(item); + } + return true; + } +}; + +#endif // FILESMODEL_H diff --git a/bacula/src/qt-console/tray-monitor/jobselectwizardpage.cpp b/bacula/src/qt-console/tray-monitor/jobselectwizardpage.cpp new file mode 100644 index 0000000000..66302c7580 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/jobselectwizardpage.cpp @@ -0,0 +1,69 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#include "jobselectwizardpage.h" +#include "ui_jobselectwizardpage.h" +#include + +JobSelectWizardPage::JobSelectWizardPage(QWidget *parent) : + QWizardPage(parent), + ui(new Ui::JobSelectWizardPage) +{ + ui->setupUi(this); + registerField("currentJob*", this, "currentJob", SIGNAL(currentJobChanged())); +} + +JobSelectWizardPage::~JobSelectWizardPage() +{ + delete ui; +} + +void JobSelectWizardPage::setModel(QStandardItemModel *model) +{ + ui->BackupTableView->setModel(model); + + connect(ui->BackupTableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SIGNAL(completeChanged())); + connect(ui->BackupTableView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SIGNAL(currentJobChanged())); +} + +bool JobSelectWizardPage::isComplete() const +{ + int s = ui->BackupTableView->selectionModel() ? + ui->BackupTableView->selectionModel()->selectedRows().size() : 0; + + return (ui->BackupTableView->selectionModel() && s>0); +} + +void JobSelectWizardPage::optimizeSize() +{ + ui->BackupTableView->resizeColumnsToContents(); + ui->BackupTableView->horizontalHeader()->setStretchLastSection(true); + ui->BackupTableView->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder); +} + +u_int64_t JobSelectWizardPage::currentJob() const +{ + if (ui->BackupTableView->selectionModel()) + { + QModelIndex idx = ui->BackupTableView->selectionModel()->currentIndex(); + /* return the JobId (column 0) */ + QModelIndex idIdx = idx.sibling(idx.row(), 0); + return idIdx.data().toULongLong(); + } + return -1; +} diff --git a/bacula/src/qt-console/tray-monitor/jobselectwizardpage.h b/bacula/src/qt-console/tray-monitor/jobselectwizardpage.h new file mode 100644 index 0000000000..d299fa676b --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/jobselectwizardpage.h @@ -0,0 +1,57 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#ifndef JOBSELECTWIZARDPAGE_H +#define JOBSELECTWIZARDPAGE_H + +#include + +class QStandardItemModel; +class QItemSelection; + +namespace Ui { +class JobSelectWizardPage; +} + +class JobSelectWizardPage : public QWizardPage +{ + Q_OBJECT + Q_PROPERTY(qlonglong currentJob READ currentJob NOTIFY currentJobChanged) + +public: + explicit JobSelectWizardPage(QWidget *parent = 0); + ~JobSelectWizardPage(); + + void setModel(QStandardItemModel *model); + + u_int64_t currentJob() const; + + bool isComplete() const; + +signals: + void currentJobChanged(); + +public slots: + void optimizeSize(); + +private: + Ui::JobSelectWizardPage *ui; + qlonglong m_jobId; +}; + +#endif // JOBSELECTWIZARDPAGE_H diff --git a/bacula/src/qt-console/tray-monitor/jobselectwizardpage.ui b/bacula/src/qt-console/tray-monitor/jobselectwizardpage.ui new file mode 100644 index 0000000000..e45c3c80cc --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/jobselectwizardpage.ui @@ -0,0 +1,52 @@ + + + JobSelectWizardPage + + + + 0 + 0 + 706 + 360 + + + + WizardPage + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + false + + + false + + + true + + + false + + + + + + + + diff --git a/bacula/src/qt-console/tray-monitor/jobsmodel.cpp b/bacula/src/qt-console/tray-monitor/jobsmodel.cpp new file mode 100644 index 0000000000..1b8610a189 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/jobsmodel.cpp @@ -0,0 +1,98 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#include "jobsmodel.h" + +JobsModel::JobsModel(const QList& t, QObject *parent) + : QAbstractTableModel(parent) + , table(t) +{ +} + +QVariant JobsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) { + switch (section) { + case ID_COLUMN: + return tr("Id"); + case TDATE_COLUMN: + return tr("Date"); + case HASCACHE_COLUMN: + return tr("Cache"); + case NAME_COLUMN: + return tr("Name"); + default: + return QVariant(); + } + } + return QVariant(); + + + return QVariant(); +} + +int JobsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return table.size(); +} + +int JobsModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return NUM_COLUMN; +} + +QVariant JobsModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= table.size() || index.row() < 0) + return QVariant(); + + if (index.column() >= NUM_COLUMN || index.column() < 0) + return QVariant(); + + if (role == Qt::DisplayRole) { + row_struct row = table.at(index.row()); + switch(index.column()) + { + case 0: + return quint64(row.id); + break; + case 1: + return row.tdate; + break; + case 2: + return row.hasCache; + break; + case 3: + return row.name; + break; + } + } + return QVariant(); +} diff --git a/bacula/src/qt-console/tray-monitor/jobsmodel.h b/bacula/src/qt-console/tray-monitor/jobsmodel.h new file mode 100644 index 0000000000..e47fccabd8 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/jobsmodel.h @@ -0,0 +1,59 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#ifndef JOBSMODEL_H +#define JOBSMODEL_H + +#include +#include "common.h" + +class JobsModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + enum { + ID_COLUMN =0, + TDATE_COLUMN, + HASCACHE_COLUMN, + NAME_COLUMN, + NUM_COLUMN + }; + + struct row_struct { + uint64_t id; + QDateTime tdate; + QString hasCache; + QString name; + }; + + explicit JobsModel(const QList& t, QObject *parent = NULL); + // Header: + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +private: + QList table; +}; + +#endif // JOBSMODEL_H diff --git a/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.cpp b/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.cpp new file mode 100644 index 0000000000..ffd01f8bf0 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.cpp @@ -0,0 +1,112 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#include "restoreoptionswizardpage.h" +#include "ui_restoreoptionswizardpage.h" +#include "common.h" +#include "filesmodel.h" +#include "task.h" + +RestoreOptionsWizardPage::RestoreOptionsWizardPage(QWidget *parent) : + QWizardPage(parent), + ui(new Ui::RestoreOptionsWizardPage), + dest_model(0), + m_res(0) + +{ + ui->setupUi(this); + + registerField("restoreClient*", ui->restoreClientComboxBox); + registerField("restoreWhere", ui->whereLineEdit); + registerField("restoreReplace*", ui->replaceComboBox); + registerField("restoreComment", ui->commentLineEdit); +} + +RestoreOptionsWizardPage::~RestoreOptionsWizardPage() +{ + delete ui; +} + + +void RestoreOptionsWizardPage::fill_defaults(task *t) +{ + if (ui->restoreClientComboxBox->isEnabled()) { + ui->restoreClientComboxBox->setCurrentIndex(ui->restoreClientComboxBox->findData(t->res->defaults.client)); + } + if (ui->whereLineEdit->isEnabled()) { + ui->whereLineEdit->setText(t->res->defaults.where); + } +} + +void RestoreOptionsWizardPage::setClients(alist *lst) +{ + if (lst && lst->size() > 0) { + ui->restoreClientComboxBox->clear(); + QStringList list; + char *str; + foreach_alist(str, lst) { + list << QString(str); + } + ui->restoreClientComboxBox->addItems(list); + ui->restoreClientComboxBox->setEnabled(true); + } else { + ui->restoreClientComboxBox->setEnabled(false); + } + + /* by default, select the same client as in clientselectwizardPage */ + /* this is assuming lists are identical and in the same order. It should be the case */ + ui->restoreClientComboxBox->setCurrentIndex(field("currentClient").toInt()); +} + +void RestoreOptionsWizardPage::setModel(QStandardItemModel *model) +{ + dest_model = model; +} + +void RestoreOptionsWizardPage::setResmon(RESMON *res) +{ + m_res = res; +} +extern int decode_stat(char *buf, struct stat *statp, int stat_size, int32_t *LinkFI); + +bool RestoreOptionsWizardPage::validatePage() +{ + task *t = new task(); + connect(t, SIGNAL(done(task*)), t, SLOT(deleteLater())); + + t->init(m_res, TASK_RESTORE); + + t->arg = (char*) m_res->clients->get(field("restoreClient").toInt()); + + if (!field("restoreWhere").isNull()) { + t->arg2 = field("restoreWhere").toString().toUtf8(); + } else { + t->arg2 = NULL; + } + + if (!field("restoreComment").isNull()) { + t->arg3 = field("restoreComment").toString().toUtf8(); + } else { + t->arg3 = NULL; + } + + t->model = dest_model; + + m_res->wrk->queue(t); + return true; +} diff --git a/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.h b/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.h new file mode 100644 index 0000000000..e503f866dc --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.h @@ -0,0 +1,56 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#ifndef RESTOREOPTIONSWIZARDPAGE_H +#define RESTOREOPTIONSWIZARDPAGE_H + +#include + +class alist; +class QStandardItemModel; +class RESMON; +class task; + +namespace Ui { +class RestoreOptionsWizardPage; +} + +class RestoreOptionsWizardPage : public QWizardPage +{ + Q_OBJECT + +public: + explicit RestoreOptionsWizardPage(QWidget *parent = 0); + ~RestoreOptionsWizardPage(); + + void setClients(alist *lst); + void setModel(QStandardItemModel *model); + void setResmon(RESMON *res); + + bool validatePage(); + +public slots: + void fill_defaults(task *t); + +private: + Ui::RestoreOptionsWizardPage *ui; + QStandardItemModel *dest_model; + RESMON *m_res; +}; + +#endif // RESTOREOPTIONSWIZARDPAGE_H diff --git a/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.ui b/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.ui new file mode 100644 index 0000000000..54379c18f5 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/restoreoptionswizardpage.ui @@ -0,0 +1,82 @@ + + + RestoreOptionsWizardPage + + + + 0 + 0 + 419 + 304 + + + + WizardPage + + + + + + Restore Client: + + + + + + + + + + Where : + + + + + + + + + + Replace : + + + + + + + + never + + + + + always + + + + + if newer + + + + + if older + + + + + + + + Comment : + + + + + + + + + + + diff --git a/bacula/src/qt-console/tray-monitor/restorewizard.cpp b/bacula/src/qt-console/tray-monitor/restorewizard.cpp new file mode 100644 index 0000000000..e25bfdddc0 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/restorewizard.cpp @@ -0,0 +1,123 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#include "restorewizard.h" +#include "ui_restorewizard.h" +#include "filesmodel.h" +#include "task.h" +#include + +RestoreWizard::RestoreWizard(RESMON *r, QWidget *parent) : + QWizard(parent), + res(r), + ui(new Ui::RestoreWizard), + jobs_model(new QStandardItemModel), + src_files_model(new FileSourceModel), + dest_files_model(new FileDestModel) +{ + ui->setupUi(this); + + connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(pageChanged(int))); + + ui->RestWizBackupSelectPage->setModel(jobs_model); + ui->RestWiFileSelectionPage->setModels(src_files_model, dest_files_model); + connect(ui->RestWiFileSelectionPage, SIGNAL(currentFileChanged()), this, SLOT(fillFilePage())); + ui->RestWizAdvancedOptionsPage->setModel(dest_files_model); + ui->RestWizAdvancedOptionsPage->setResmon(res); + +} + +void RestoreWizard::pageChanged(int index) +{ + switch(index) { + case RW_CLIENT_PAGE: + ui->RestWizClientPage->setClients(res->clients); + break; + case RW_JOB_PAGE: + fillJobPage(); + break; + case RW_FILE_PAGE: + fillFilePage(); + break; + case RW_ADVANCEDOPTIONS_PAGE: + fillOptionsPage(); + break; + default: + qDebug( "pageChanged default: %d", index ); + break; + } +} +RestoreWizard::~RestoreWizard() +{ + delete ui; +} + +void RestoreWizard::fillJobPage() +{ + task *t = new task(); + connect(t, SIGNAL(done(task*)), ui->RestWizBackupSelectPage, SLOT(optimizeSize()), Qt::QueuedConnection); + connect(t, SIGNAL(done(task*)), t, SLOT(deleteLater())); + + t->init(res, TASK_LIST_CLIENT_JOBS); + + int idx = field("currentClient").toInt(); + char *p = (char*) res->clients->get(idx); + POOL_MEM info; + pm_strcpy(info, p); + t->arg = info.c_str(); + t->model = jobs_model; + res->wrk->queue(t); +} + +void RestoreWizard::fillFilePage() +{ + task *t = new task(); + connect(t, SIGNAL(done(task*)), t, SLOT(deleteLater())); + + t->init(res, TASK_LIST_JOB_FILES); + + const char *p = field("currentJob").toString().toUtf8(); + POOL_MEM info; + pm_strcpy(info, p); + t->arg = info.c_str(); + + t->pathId = field("currentFile").toULongLong(); + t->model = src_files_model; + + res->wrk->queue(t); +} + +void RestoreWizard::fillOptionsPage() +{ + ui->RestWizAdvancedOptionsPage->setClients(res->clients); + + task *t = new task(); + connect(t, SIGNAL(done(task *)), ui->RestWizAdvancedOptionsPage, SLOT(fill_defaults(task *)), Qt::QueuedConnection); + connect(t, SIGNAL(done(task*)), t, SLOT(deleteLater())); + + const char *p = "RestoreFile";/*field("currentJob").toString().toStdString().c_str();*/ + POOL_MEM info; + pm_strcpy(info, p); + res->mutex->lock(); + bfree_and_null(res->defaults.job); + res->defaults.job = bstrdup(info.c_str()); + res->mutex->unlock(); + + t->init(res, TASK_DEFAULTS); + res->wrk->queue(t); +} diff --git a/bacula/src/qt-console/tray-monitor/restorewizard.h b/bacula/src/qt-console/tray-monitor/restorewizard.h new file mode 100644 index 0000000000..17880ab6cf --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/restorewizard.h @@ -0,0 +1,64 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2017 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ +#ifndef RESTOREWIZARD_H +#define RESTOREWIZARD_H + +#include +#include +namespace Ui { +class RestoreWizard; +} + +class task; +class RESMON; +class QStandardItemModel; + +class RestoreWizard : public QWizard +{ + Q_OBJECT + +public: + enum { + RW_CLIENT_PAGE = 0, + RW_JOB_PAGE = 1, + RW_FILE_PAGE = 2, + RW_ADVANCEDOPTIONS_PAGE = 3 + }; + + explicit RestoreWizard(RESMON *r, QWidget *parent = 0); + ~RestoreWizard(); + +public slots: + void pageChanged(int index); + +protected slots: + void fillJobPage(); + void fillFilePage(); + void fillOptionsPage(); + +private: + RESMON *res; + Ui::RestoreWizard *ui; + + QStandardItemModel *jobs_model; + QStandardItemModel *src_files_model; + QStandardItemModel *dest_files_model; +}; + +#endif // RESTOREWIZARD_H diff --git a/bacula/src/qt-console/tray-monitor/restorewizard.ui b/bacula/src/qt-console/tray-monitor/restorewizard.ui new file mode 100644 index 0000000000..5c24c032f5 --- /dev/null +++ b/bacula/src/qt-console/tray-monitor/restorewizard.ui @@ -0,0 +1,109 @@ + + + RestoreWizard + + + + 0 + 0 + 853 + 444 + + + + Wizard + + + false + + + false + + + + + 2 + 0 + + + + false + + + Restore + + + Select a Client. + + + + + Restore + + + Select Backup to restore. + + + + + Restore + + + Files Selection + + + + + Restore Options + + + Select advanced options for restore and plugins + + + + + + ClientSelectWizardPage + QWizardPage +
clientselectwizardpage.h
+ 1 +
+ + JobSelectWizardPage + QWizardPage +
jobselectwizardpage.h
+ 1 +
+ + FileSelectWizardPage + QWizardPage +
fileselectwizardpage.h
+ 1 +
+ + RestoreOptionsWizardPage + QWizardPage +
restoreoptionswizardpage.h
+ 1 +
+
+ + + + RestWizClientPage + completeChanged() + RestoreWizard + setFocus() + + + 424 + 288 + + + 424 + 274 + + + + +
diff --git a/bacula/src/qt-console/tray-monitor/task.cpp b/bacula/src/qt-console/tray-monitor/task.cpp index 911ba2f3df..1a53ec3b45 100644 --- a/bacula/src/qt-console/tray-monitor/task.cpp +++ b/bacula/src/qt-console/tray-monitor/task.cpp @@ -19,60 +19,71 @@ #include "task.h" #include "jcr.h" +#include "filesmodel.h" + #define dbglvl 10 int authenticate_daemon(JCR *jcr, MONITOR *monitor, RESMON *res); static void *handle_task(void *data) { - task *t; - worker *wrk = (worker *)data; - lmgr_init_thread(); - - wrk->set_running(); - Dmsg0(dbglvl, "Worker started\n"); - - while (!wrk->is_quit_state()) { - if (wrk->is_wait_state()) { - wrk->wait(); - continue; - } - t = (task *)wrk->dequeue(); - if (!t) { - continue; - } - /* Do the work */ - switch(t->type) { - case TASK_STATUS: - t->do_status(); - break; - case TASK_RESOURCES: - t->get_resources(); - break; - case TASK_DEFAULTS: - t->get_job_defaults(); - break; - case TASK_RUN: - t->run_job(); - break; - case TASK_BWLIMIT: - t->set_bandwidth(); - break; - case TASK_INFO: - t->get_job_info(t->arg2); - break; - case TASK_DISCONNECT: - t->disconnect_bacula(); - t->mark_as_done(); - break; - default: - Mmsg(t->errmsg, "Unknown task"); - t->mark_as_failed(); - break; - } - } - Dmsg0(dbglvl, "Worker stoped\n"); - lmgr_cleanup_thread(); - return NULL; + task *t; + worker *wrk = (worker *)data; + lmgr_init_thread(); + + wrk->set_running(); + Dmsg0(dbglvl, "Worker started\n"); + + while (!wrk->is_quit_state()) { + if (wrk->is_wait_state()) { + wrk->wait(); + continue; + } + t = (task *)wrk->dequeue(); + if (!t) { + continue; + } + /* Do the work */ + switch(t->type) { + case TASK_STATUS: + t->do_status(); + break; + case TASK_RESOURCES: + t->get_resources(); + break; + case TASK_DEFAULTS: + t->get_job_defaults(); + break; + case TASK_RUN: + t->run_job(); + break; + case TASK_BWLIMIT: + t->set_bandwidth(); + break; + case TASK_INFO: + t->get_job_info(t->arg2); + break; + case TASK_DISCONNECT: + t->disconnect_bacula(); + t->mark_as_done(); + break; + case TASK_LIST_CLIENT_JOBS: + t->get_client_jobs(t->arg); + break; + case TASK_LIST_JOB_FILES: + t->get_job_files(t->arg, t->pathId); + break; + case TASK_RESTORE: + t->restore(QString("b21234")); + break; + default: + Mmsg(t->errmsg, "Unknown task"); + t->mark_as_failed(); + break; + } + } + Dmsg0(dbglvl, "Worker stoped\n"); + lmgr_cleanup_thread(); + return NULL; } bool task::set_bandwidth() @@ -95,7 +106,7 @@ bool task::set_bandwidth() } if (!res->bs || !res->bs->is_open() || res->bs->is_error()) { - if (!connect_bacula()) { + if (!connect_bacula()) { mark_as_failed(); return false; } @@ -247,7 +258,7 @@ bool task::read_status_running(RESMON *r) char *start, *end; struct s_running_job *item = NULL; alist *running_jobs = New(alist(10, owned_by_alist)); - + while (r->bs->recv() >= -1) { if (r->bs->msglen < 0 && r->bs->msglen != BNET_CMD_BEGIN && @@ -335,7 +346,7 @@ bool task::read_status_running(RESMON *r) if (r->bs->is_error()) { Mmsg(errmsg, "Got error on the socket communication line"); goto bail_out; - } + } } if (item) { Dmsg1(dbglvl, "Append item %ld\n", item->JobId); @@ -491,7 +502,7 @@ bool task::read_status_header(RESMON *r) Mmsg(errmsg, "Got error on the socket communication line"); goto bail_out; - } + } r->last_update = time(NULL); r->mutex->unlock(); } @@ -625,7 +636,7 @@ bool task::get_job_defaults() char *p; if (!res->bs || !res->bs->is_open() || res->bs->is_error()) { - if (!connect_bacula()) { + if (!connect_bacula()) { goto bail_out; } } @@ -638,6 +649,7 @@ bool task::get_job_defaults() bfree_and_null(res->defaults.type); bfree_and_null(res->defaults.fileset); bfree_and_null(res->defaults.catalog); + bfree_and_null(res->defaults.where); tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120); if (res->type == R_CLIENT && !res->proxy_sent) { @@ -664,29 +676,33 @@ bool task::get_job_defaults() } *p++ = 0; if (strcasecmp(curline, "client") == 0) { - res->defaults.client = bstrdup(p); + res->defaults.client = bstrdup(p); } else if (strcasecmp(curline, "pool") == 0) { - res->defaults.pool = bstrdup(p); + res->defaults.pool = bstrdup(p); } else if (strcasecmp(curline, "storage") == 0) { - res->defaults.storage = bstrdup(p); + res->defaults.storage = bstrdup(p); } else if (strcasecmp(curline, "level") == 0) { - res->defaults.level = bstrdup(p); + res->defaults.level = bstrdup(p); } else if (strcasecmp(curline, "type") == 0) { - res->defaults.type = bstrdup(p); + res->defaults.type = bstrdup(p); } else if (strcasecmp(curline, "fileset") == 0) { - res->defaults.fileset = bstrdup(p); + res->defaults.fileset = bstrdup(p); } else if (strcasecmp(curline, "catalog") == 0) { - res->defaults.catalog = bstrdup(p); + res->defaults.catalog = bstrdup(p); } else if (strcasecmp(curline, "priority") == 0) { - res->defaults.priority = str_to_uint64(p); + res->defaults.priority = str_to_uint64(p); + + } else if (strcasecmp(curline, "where") == 0) { + res->defaults.where = bstrdup(p); } + } ret = true; bail_out: @@ -698,7 +714,7 @@ bail_out: } else { mark_as_failed(); } - res->mutex->unlock(); + res->mutex->unlock(); return ret; } @@ -709,7 +725,7 @@ bool task::get_job_info(const char *level) char *p; if (!res->bs || !res->bs->is_open() || res->bs->is_error()) { - if (!connect_bacula()) { + if (!connect_bacula()) { goto bail_out; } } @@ -784,7 +800,7 @@ bool task::run_job() btimer_t *tid = NULL; if (!res->bs || !res->bs->is_open() || res->bs->is_error()) { - if (!connect_bacula()) { + if (!connect_bacula()) { goto bail_out; } } @@ -820,7 +836,98 @@ bool task::run_job() } // Close the socket, it's over or we don't want to reuse it disconnect_bacula(); - + +bail_out: + + if (tid) { + stop_thread_timer(tid); + } + if (ret) { + mark_as_done(); + } else { + mark_as_failed(); + } + return ret; +} + +bool task::get_client_jobs(const char* client) +{ + bool ret = false; + btimer_t *tid = NULL; + int row=0; + QStringList headers; + headers << tr("JobId") << tr("Job") << tr("Level") << tr("Date") << tr("Files") << tr("Bytes"); + + if (!model) { + goto bail_out; + } + + model->clear(); + model->setHorizontalHeaderLabels(headers); + + if (!res->bs || !res->bs->is_open() || res->bs->is_error()) { + if (!connect_bacula()) { + goto bail_out; + } + } + + tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120); + + if (res->type == R_CLIENT && !res->proxy_sent) { + res->proxy_sent = true; + res->bs->fsend("proxy\n"); + while (get_next_line(res)) { + if (strncmp(curline, "2000", 4) != 0) { + pm_strcpy(errmsg, curline); + goto bail_out; + } + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + } + + res->bs->fsend(".api 2\n"); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + + if (res->type == R_DIRECTOR && res->use_setip) { + res->bs->fsend("setip\n"); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + } + + res->bs->fsend(".bvfs_get_jobs client=%s\n", client); + while (get_next_line(res)) { + QString line(curline); + QStringList line_lst = line.split(" ", QString::SkipEmptyParts); + + model->setItem(row, 0, new QStandardItem(line_lst[0])); + + model->setItem(row, 1, new QStandardItem(line_lst[3])); + + QDateTime date; + date.setTime_t(line_lst[1].toUInt()); + QStandardItem *dateItem = new QStandardItem(); + dateItem->setData(date, Qt::DisplayRole); + model->setItem(row, 3, dateItem); + + ret = true; + ++row; + } + + // Close the socket, it's over or we don't want to reuse it + disconnect_bacula(); + +// // fill extra job info +// for (int r=0; rrowCount(); ++r) { +// arg = model->item(r, 0)->text().toUtf8(); +// get_job_info(NULL); +// model->setItem(r, 2, new QStandardItem(res->infos.JobLevel)); +// model->setItem(r, 4, new QStandardItem(res->infos.JobFiles)); +// model->setItem(r, 5, new QStandardItem(res->infos.JobBytes)); +// } + bail_out: if (tid) { @@ -834,6 +941,381 @@ bail_out: return ret; } + +bool task::get_job_files(const char* job, uint64_t pathid) +{ + bool ret = false; + btimer_t *tid = NULL; + QString jobs; + + if (model) { + model->clear(); + } + + if (!res->bs || !res->bs->is_open() || res->bs->is_error()) { + if (!connect_bacula()) { + goto bail_out; + } + } + + tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120); + + if (res->type == R_CLIENT && !res->proxy_sent) { + res->proxy_sent = true; + res->bs->fsend("proxy\n"); + while (get_next_line(res)) { + if (strncmp(curline, "2000", 4) != 0) { + pm_strcpy(errmsg, curline); + goto bail_out; + } + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + } + + if (res->type == R_DIRECTOR && res->use_setip) { + res->bs->fsend("setip\n"); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + } + + res->bs->fsend(".api 2\n"); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + + /* retrieve all job ids*/ + res->bs->fsend(".bvfs_get_jobids jobid=%s\n", job); + while (get_next_line(res)) { + jobs = QString(curline); + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + + /* cache the file set */ + res->bs->fsend(".bvfs_update jobid=%s\n", jobs.toUtf8()); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + + if (pathid == 0) { + res->bs->fsend(".bvfs_lsdirs jobid=%s path=\"\"\n", jobs.toUtf8()); + } else { + res->bs->fsend(".bvfs_lsdirs jobid=%s pathid=%lld\n", jobs.toUtf8(), pathid); + } + + while (get_next_line(res)) { + QString line(curline); + QStringList line_lst = line.split("\t", QString::KeepEmptyParts); + if ((line_lst.size() == 6) && line_lst[5] != ".") + { + DirectoryItem *d = new DirectoryItem(); + d->setData(QVariant(line_lst[0]), PathIdRole); + d->setData(QVariant(line_lst[1]), FilenameIdRole); + d->setData(QVariant(line_lst[2]), FileIdRole); + d->setData(QVariant(line_lst[3]), JobIdRole); + d->setData(QVariant(line_lst[4]), LStatRole); + d->setData(QVariant(line_lst[5]), PathRole); + d->setData(QVariant(line_lst[5]), Qt::DisplayRole); + + model->appendRow(d); + ret = true; + } + } + + /* then, request files */ + if (pathid == 0) { + res->bs->fsend(".bvfs_lsfiles jobid=%s path=\"\"\n", jobs.toUtf8()); + } else { + res->bs->fsend(".bvfs_lsfiles jobid=%s pathid=%lld\n", jobs.toUtf8(), pathid); + } + + while (get_next_line(res)) { + QString line(curline); + QStringList line_lst = line.split("\t", QString::SkipEmptyParts); + if ((line_lst.size() == 6) && line_lst[5] != ".") + { + FileItem *f = new FileItem(); + f->setData(QVariant(line_lst[0]), PathIdRole); + f->setData(QVariant(line_lst[1]), FilenameIdRole); + f->setData(QVariant(line_lst[2]), FileIdRole); + f->setData(QVariant(line_lst[3]), JobIdRole); + f->setData(QVariant(line_lst[4]), LStatRole); + f->setData(QVariant(line_lst[5]), PathRole); + f->setData(QVariant(line_lst[5]), Qt::DisplayRole); + model->appendRow(f); + ret = true; + } + } + + // Close the socket, it's over or we don't want to reuse it + disconnect_bacula(); + +bail_out: + + if (tid) { + stop_thread_timer(tid); + } + if (ret) { + mark_as_done(); + } else { + mark_as_failed(); + } + return ret; +} + +extern int decode_stat(char *buf, struct stat *statp, int stat_size, int32_t *LinkFI); + +bool task::prepare_restore(const QString& tableName) +{ + bool ret = false; + btimer_t *tid = NULL; + QString q; + QStringList fileids, jobids, dirids, findexes; + struct stat statp; + int32_t LinkFI; + + for (int row=0; row < model->rowCount(); ++row) { + QModelIndex idx = model->index(row, 0); + if (idx.data(TypeRole) == TYPEROLE_FILE) { + fileids << idx.data(FileIdRole).toString(); + jobids << idx.data(JobIdRole).toString(); + decode_stat(idx.data(LStatRole).toString().toLocal8Bit().data(), + &statp, sizeof(statp), &LinkFI); + if (LinkFI) { + findexes << idx.data(JobIdRole).toString() + "," + QString().setNum(LinkFI); + } + } else // TYPEROLE_DIRECTORY + { + dirids << idx.data(PathIdRole).toString(); + jobids << idx.data(JobIdRole).toString().split(","); // Can have multiple jobids + } + } + + fileids.removeDuplicates(); + jobids.removeDuplicates(); + dirids.removeDuplicates(); + findexes.removeDuplicates(); + + if (!res->bs || !res->bs->is_open() || res->bs->is_error()) { + if (!connect_bacula()) { + goto bail_out; + } + } + + tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120); + + if (res->type == R_CLIENT && !res->proxy_sent) { + res->proxy_sent = true; + res->bs->fsend("proxy\n"); + while (get_next_line(res)) { + if (strncmp(curline, "2000", 4) != 0) { + pm_strcpy(errmsg, curline); + goto bail_out; + } + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + } + + if (res->type == R_DIRECTOR && res->use_setip) { + res->bs->fsend("setip\n"); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + } + + res->bs->fsend(".api 2\n"); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + + /* retrieve all job ids*/ + q = QString(".bvfs_restore path=%1 jobid=%2").arg(tableName, jobids.join(",")); + if (fileids.size() > 0) { + q += " fileid=" + fileids.join(","); + } + if (dirids.size() > 0) { + q += " dirid=" + dirids.join(","); + } + if (findexes.size() > 0) { + q += " hardlink=" + findexes.join(","); + } + + q += "\n"; + res->bs->fsend(q.toUtf8()); + while (get_next_line(res)) { + ret = true; + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + + // Close the socket, it's over or we don't want to reuse it + disconnect_bacula(); + + bail_out: + + if (tid) { + stop_thread_timer(tid); + } + if (ret) { + mark_as_done(); + } else { + mark_as_failed(); + } + return ret; +} + +bool task::run_restore(const QString& tableName) +{ + bool ret = false; + btimer_t *tid = NULL; + QString q; + + if (!res->bs || !res->bs->is_open() || res->bs->is_error()) { + if (!connect_bacula()) { + goto bail_out; + } + } + + tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120); + + if (res->type == R_CLIENT && !res->proxy_sent) { + res->proxy_sent = true; + res->bs->fsend("proxy\n"); + while (get_next_line(res)) { + if (strncmp(curline, "2000", 4) != 0) { + pm_strcpy(errmsg, curline); + goto bail_out; + } + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + } + + if (res->type == R_DIRECTOR && res->use_setip) { + res->bs->fsend("setip\n"); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + } + + res->bs->fsend(".api 2\n"); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + + q = QString("restore client=%1").arg(arg); + + if (arg2) { + QString where(arg2); + where.replace("\"", ""); + q += " where=\"" + where + "\""; + } + + if (arg3) { + QString comment(arg3); + comment.replace("\"", " "); + q += " comment=\"" + comment+ "\""; + } + + q += " file=\"?" + tableName + "\""; + q += " done yes\n"; + + res->bs->fsend(q.toUtf8()); + while (get_next_line(res)) { + ret = true; + // FIXME : report a signal to have a progress feedback + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + + // Close the socket, it's over or we don't want to reuse it + disconnect_bacula(); + + bail_out: + + if (tid) { + stop_thread_timer(tid); + } + if (ret) { + mark_as_done(); + } else { + mark_as_failed(); + } + return ret; +} + +bool task::clean_restore(const QString& tableName) +{ + bool ret = false; + btimer_t *tid = NULL; + QString q; + + if (!res->bs || !res->bs->is_open() || res->bs->is_error()) { + if (!connect_bacula()) { + goto bail_out; + } + } + + tid = start_thread_timer(NULL, pthread_self(), (uint32_t)120); + + if (res->type == R_CLIENT && !res->proxy_sent) { + res->proxy_sent = true; + res->bs->fsend("proxy\n"); + while (get_next_line(res)) { + if (strncmp(curline, "2000", 4) != 0) { + pm_strcpy(errmsg, curline); + goto bail_out; + } + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + } + + if (res->type == R_DIRECTOR && res->use_setip) { + res->bs->fsend("setip\n"); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + } + + res->bs->fsend(".api 2\n"); + while (get_next_line(res)) { + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + + q = QString(".bvfs_cleanup path=%1\n").arg(tableName); + + res->bs->fsend(q.toUtf8()); + while (get_next_line(res)) { + ret = true; + // FIXME : report a signal to have a progress feedback + Dmsg2(dbglvl, "<- %d %s\n", res->bs->msglen, curline); + } + + // Close the socket, it's over or we don't want to reuse it + disconnect_bacula(); + + bail_out: + + if (tid) { + stop_thread_timer(tid); + } + if (ret) { + mark_as_done(); + } else { + mark_as_failed(); + } + return ret; +} + +bool task::restore(const QString& tableName) +{ + bool ret=prepare_restore(tableName); + if (ret) { + ret = run_restore(tableName); + if (ret) { + return clean_restore(tableName); + } + } + return false; +} + /* Get resources to run a job */ bool task::get_resources() { @@ -841,7 +1323,7 @@ bool task::get_resources() btimer_t *tid = NULL; if (!res->bs || !res->bs->is_open() || res->bs->is_error()) { - if (!connect_bacula()) { + if (!connect_bacula()) { goto bail_out; } } diff --git a/bacula/src/qt-console/tray-monitor/task.h b/bacula/src/qt-console/tray-monitor/task.h index 0f529a3c3f..2b9bf6e79b 100644 --- a/bacula/src/qt-console/tray-monitor/task.h +++ b/bacula/src/qt-console/tray-monitor/task.h @@ -22,6 +22,7 @@ #include "common.h" #include +#include #include "tray_conf.h" enum { @@ -30,6 +31,8 @@ enum { TASK_RESOURCES, TASK_QUERY, TASK_RUN, + TASK_LIST_CLIENT_JOBS, + TASK_LIST_JOB_FILES, TASK_RESTORE, TASK_DEFAULTS, TASK_CLOSE, @@ -44,14 +47,17 @@ class task: public QObject Q_OBJECT public: - RESMON *res; - POOLMEM *errmsg; - int type; - bool status; - char *curline; - char *curend; - char *arg; /* Argument that can be used by some tasks */ - char *arg2; + RESMON *res; + POOLMEM *errmsg; + int type; + bool status; + char *curline; + char *curend; + const char *arg; /* Argument that can be used by some tasks */ + const char *arg2; + const char *arg3; + QStandardItemModel *model; /* model to fill, depending on context */ + uint64_t pathId; union { bool b; @@ -94,16 +100,23 @@ public: void mark_as_done() { status = true; emit done(this); - }; + } void mark_as_failed() { status = false; emit done(this); - }; + } bool get_resources(); bool get_next_line(RESMON *res); bool get_job_defaults(); /* Look r->defaults.job */ bool run_job(); bool get_job_info(const char *level); /* look r->info */ + bool get_client_jobs(const char* client); + bool get_job_files(const char* job, uint64_t pathId); + + bool prepare_restore(const QString& tableName); + bool run_restore(const QString& tableName); + bool clean_restore(const QString& tableName); + bool restore(const QString& tableName); signals: void done(task *t); diff --git a/bacula/src/qt-console/tray-monitor/tray-monitor.pro.in b/bacula/src/qt-console/tray-monitor/tray-monitor.pro.in index 8c414a05ad..265d7e2182 100644 --- a/bacula/src/qt-console/tray-monitor/tray-monitor.pro.in +++ b/bacula/src/qt-console/tray-monitor/tray-monitor.pro.in @@ -11,7 +11,7 @@ # # CONFIG options for Windows are pulled from win32/qmake.conf CONFIG += qt -#CONFIG += qt debug +#CONFIG += qt debug cross-win32 { LIBS += -mwindows -L../../win32/release32 -lbacula @@ -48,9 +48,8 @@ OBJECTS_DIR = obj UI_DIR = ui # Main directory -HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h -SOURCES += tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp common.h - -FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui +HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h restorewizard.h filesmodel.h clientselectwizardpage.h jobselectwizardpage.h fileselectwizardpage.h restoreoptionswizardpage.h +SOURCES += tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp restorewizard.cpp clientselectwizardpage.cpp jobselectwizardpage.cpp fileselectwizardpage.cpp restoreoptionswizardpage.cpp +FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui restorewizard.ui clientselectwizardpage.ui jobselectwizardpage.ui fileselectwizardpage.ui restoreoptionswizardpage.ui TRANSLATIONS += ts/tm_fr.ts ts/tm_de.ts ts/tm_ja.ts diff --git a/bacula/src/qt-console/tray-monitor/tray-monitor.pro.mingw32.in b/bacula/src/qt-console/tray-monitor/tray-monitor.pro.mingw32.in index 362fff1b30..4ac57c2a9d 100644 --- a/bacula/src/qt-console/tray-monitor/tray-monitor.pro.mingw32.in +++ b/bacula/src/qt-console/tray-monitor/tray-monitor.pro.mingw32.in @@ -58,9 +58,8 @@ QMAKE_LIB = i686-w64-mingw32-ar -ru QMAKE_RC = i686-w64-mingw32-windres # Main directory -HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h -SOURCES += tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp common.h - -FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui +HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h restorewizard.h filesmodel.h clientselectwizardpage.h jobselectwizardpage.h fileselectwizardpage.h restoreoptionswizardpage.h +SOURCES += tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp restorewizard.cpp clientselectwizardpage.cpp jobselectwizardpage.cpp fileselectwizardpage.cpp restoreoptionswizardpage.cpp +FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui restorewizard.ui clientselectwizardpage.ui jobselectwizardpage.ui fileselectwizardpage.ui restoreoptionswizardpage.ui TRANSLATIONS += ts/tm_fr.ts ts/tm_de.ts ts/tm_ja.ts diff --git a/bacula/src/qt-console/tray-monitor/tray-monitor.pro.mingw64.in b/bacula/src/qt-console/tray-monitor/tray-monitor.pro.mingw64.in index 20719ba890..792c697265 100644 --- a/bacula/src/qt-console/tray-monitor/tray-monitor.pro.mingw64.in +++ b/bacula/src/qt-console/tray-monitor/tray-monitor.pro.mingw64.in @@ -58,9 +58,8 @@ QMAKE_LIB = x86_64-w64-mingw32-ar -ru QMAKE_RC = x86_64-w64-mingw32-windres # Main directory -HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h -SOURCES += tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp common.h - -FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui +HEADERS += tray-monitor.h tray_conf.h tray-ui.h fdstatus.h task.h ../util/fmtwidgetitem.h dirstatus.h conf.h sdstatus.h runjob.h status.h restorewizard.h filesmodel.h clientselectwizardpage.h jobselectwizardpage.h fileselectwizardpage.h restoreoptionswizardpage.h +SOURCES += tray-monitor.cpp tray_conf.cpp fdstatus.cpp task.cpp authenticate.cpp ../util/fmtwidgetitem.cpp dirstatus.cpp sdstatus.cpp conf.cpp runjob.cpp status.cpp restorewizard.cpp clientselectwizardpage.cpp jobselectwizardpage.cpp fileselectwizardpage.cpp restoreoptionswizardpage.cpp +FORMS += fd-monitor.ui dir-monitor.ui sd-monitor.ui main-conf.ui res-conf.ui run.ui restorewizard.ui clientselectwizardpage.ui jobselectwizardpage.ui fileselectwizardpage.ui restoreoptionswizardpage.ui TRANSLATIONS += ts/tm_fr.ts ts/tm_de.ts ts/tm_ja.ts diff --git a/bacula/src/qt-console/tray-monitor/tray-ui.h b/bacula/src/qt-console/tray-monitor/tray-ui.h index af65c6d8e5..1efe2ec30f 100644 --- a/bacula/src/qt-console/tray-monitor/tray-ui.h +++ b/bacula/src/qt-console/tray-monitor/tray-ui.h @@ -49,6 +49,7 @@ #include "dirstatus.h" #include "conf.h" #include "runjob.h" +#include "restorewizard.h" void display_error(const char *fmt, ...); @@ -67,7 +68,8 @@ public: QSpinBox *spinRefresh; QTimer *timer; bool have_systray; - + RestoreWizard *restorewiz; + TrayUI(): QMainWindow(), tabWidget(NULL), @@ -75,12 +77,13 @@ public: tray(NULL), spinRefresh(NULL), timer(NULL), - have_systray(QSystemTrayIcon::isSystemTrayAvailable()) + have_systray(QSystemTrayIcon::isSystemTrayAvailable()), + restorewiz(NULL) { - }; + } ~TrayUI() { - }; + } void addTab(RESMON *r) { QWidget *tab; @@ -117,7 +120,7 @@ public: } tabWidget->setUpdatesEnabled(false); tabWidget->addTab(tab, t); - tabWidget->setUpdatesEnabled(true); + tabWidget->setUpdatesEnabled(true); } void clearTabs() { @@ -191,6 +194,7 @@ public: verticalLayout->addLayout(hLayout); //QSystemTrayIcon::isSystemTrayAvailable + tray = new QSystemTrayIcon(TrayMonitor); QMenu* stmenu = new QMenu(TrayMonitor); @@ -206,17 +210,16 @@ public: QAction* actRun = new QAction(QApplication::translate("TrayMonitor", "Run...", 0, QApplication::UnicodeUTF8),TrayMonitor); -/* Not yet ready - * QAction* actRes = new QAction(QApplication::translate("TrayMonitor", - * "Restore...", - * 0, QApplication::UnicodeUTF8),TrayMonitor); - */ + QAction* actRes = new QAction(QApplication::translate("TrayMonitor", + "Restore...", + 0, QApplication::UnicodeUTF8),TrayMonitor); + QAction* actConf = new QAction(QApplication::translate("TrayMonitor", "Configure...", 0, QApplication::UnicodeUTF8),TrayMonitor); stmenu->addAction(actShow); stmenu->addAction(actRun); - //stmenu->addAction(actRes); + stmenu->addAction(actRes); stmenu->addSeparator(); stmenu->addAction(actConf); stmenu->addSeparator(); @@ -227,7 +230,7 @@ public: connect(actRun, SIGNAL(triggered()), this, SLOT(cb_run())); connect(actShow, SIGNAL(triggered()), this, SLOT(cb_show())); connect(actConf, SIGNAL(triggered()), this, SLOT(cb_conf())); - //connect(actRes, SIGNAL(triggered()), this, SLOT(cb_restore())); + connect(actRes, SIGNAL(triggered()), this, SLOT(cb_restore())); connect(actQuit, SIGNAL(triggered()), this, SLOT(cb_quit())); connect(actAbout, SIGNAL(triggered()), this, SLOT(cb_about())); connect(spinRefresh, SIGNAL(valueChanged(int)), this, SLOT(cb_refresh(int))); @@ -367,7 +370,12 @@ private slots: if (!dir) { return; } + task *t = new task(); + connect(t, SIGNAL(done(task *)), this, SLOT(start_restore_wizard(task *)), Qt::QueuedConnection); + t->init(dir, TASK_RESOURCES); + dir->wrk->queue(t); } + void cb_trayIconActivated(QSystemTrayIcon::ActivationReason r) { if (r == QSystemTrayIcon::Trigger) { cb_show(); @@ -390,13 +398,21 @@ public slots: void task_done(task *t) { Dmsg0(0, "Task done!\n"); t->deleteLater(); - }; + } void run_job(task *t) { Dmsg0(0, "Task done!\n"); RESMON *dir = t->res; t->deleteLater(); new RunJob(dir); - }; + } + + void start_restore_wizard(task *t) { + RESMON *dir = t->res; + restorewiz = new RestoreWizard(dir); + restorewiz->show(); + t->deleteLater(); + } + }; diff --git a/bacula/src/qt-console/tray-monitor/tray_conf.h b/bacula/src/qt-console/tray-monitor/tray_conf.h index f2bc4d8d0f..cfd52f056c 100644 --- a/bacula/src/qt-console/tray-monitor/tray_conf.h +++ b/bacula/src/qt-console/tray-monitor/tray_conf.h @@ -30,8 +30,6 @@ #include "common.h" -/* NOTE: #includes at the end of this file */ - /* * Resource codes -- they must be sequential for indexing */ @@ -141,6 +139,7 @@ struct RESMON { char *fileset; char *catalog; int priority; + char *where; } defaults; /* Information about the job */ @@ -154,6 +153,8 @@ struct RESMON { } infos; }; +Q_DECLARE_METATYPE(RESMON*) + /* * Tray Monitor Resource *