source: src-qt4/life-preserver/lp-gui/LPMain.cpp

Last change on this file was 615eea1, checked in by Ken Moore <ken@…>, 2 weeks ago

Add an option to the snapshots menu to start a replication for the current dataset/pool (if replication is setup for that pool).

  • Property mode set to 100644
File size: 33.6 KB
Line 
1#include "LPMain.h"
2#include "ui_LPMain.h"
3#include <unistd.h>
4
5LPMain::LPMain(QWidget *parent) : QMainWindow(parent), ui(new Ui::LPMain){
6  ui->setupUi(this); //load the Qt-designer UI file
7  //Initialize the auto-refresh timer
8  timer = new QTimer(this);
9        timer->setSingleShot(true);
10        timer->setInterval(60000); // 1 minute timer
11        connect(timer, SIGNAL(timeout()), this, SLOT(updateTabs()) );
12  //Initialize the system watcher
13  watcher = new QFileSystemWatcher(this);
14    //Make sure the lpreserver log directory exists and watch it
15    if(!QFile::exists("/var/log/lpreserver")){
16      qDebug() << "Creating the lpreserver log directory (/var/log/lpreserver)";
17      QDir dir;
18      dir.mkpath("/var/log/lpreserver");
19    }
20    watcher->addPath("/var/log/lpreserver/");
21  //Initialize the waitbox pointer
22  waitBox = 0;
23  //Initialize the classic dialog pointer
24  classicDLG = 0;
25  //Create the basic/advanced view options
26  viewBasic = new QRadioButton(tr("Basic"), ui->menuView);
27        QWidgetAction *WABasic = new QWidgetAction(this); WABasic->setDefaultWidget(viewBasic);
28        ui->menuView->addAction(WABasic);
29  viewAdvanced = new QRadioButton(tr("Advanced"), ui->menuView);
30        QWidgetAction *WAAdv = new QWidgetAction(this); WAAdv->setDefaultWidget(viewAdvanced);
31        ui->menuView->addAction(WAAdv);
32  connect(viewBasic, SIGNAL(toggled(bool)), this, SLOT(viewChanged()) );
33  //Now set the default view type
34  settings = new QSettings(QSettings::UserScope, "PC-BSD", "Life-Preserver-GUI", this);
35  bool basicMode = settings->value("viewmode", true).toBool(); //basic by default
36  if(basicMode){ viewBasic->setChecked(true); } //will automatically call the "viewChanged" function
37  else{ viewAdvanced->setChecked(true); } //will automatically call the "viewChanged" function
38  //Create the filesystem model and tie it to the treewidget
39  fsModel = new QFileSystemModel(this);
40        fsModel->setReadOnly(true);
41        ui->treeView->setModel(fsModel);
42  //Connect the UI to all the functions
43  connect(ui->tool_refresh, SIGNAL(clicked()), this, SLOT(updatePoolList()) );
44  connect(ui->combo_pools, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTabs()) );
45  connect(ui->combo_datasets, SIGNAL(currentIndexChanged(int)), this, SLOT(updateDataset()) );
46  connect(ui->slider_snapshots, SIGNAL(valueChanged(int)), this, SLOT(updateSnapshot()) );
47  connect(ui->push_prevsnap, SIGNAL(clicked()), this, SLOT(prevSnapshot()) );
48  connect(ui->push_nextsnap, SIGNAL(clicked()), this, SLOT(nextSnapshot()) );
49  connect(ui->check_hidden, SIGNAL(stateChanged(int)), this, SLOT(setFileVisibility()) );
50  connect(ui->push_restore, SIGNAL(clicked()), this, SLOT(restoreFiles()) );
51  connect(ui->push_configure, SIGNAL(clicked()), this, SLOT(openConfigGUI()) );
52  //Connect the Menu buttons
53  connect(ui->menuManage_Pool, SIGNAL(triggered(QAction*)), this, SLOT(menuAddPool(QAction*)) );
54  connect(ui->menuUnmanage_Pool, SIGNAL(triggered(QAction*)), this, SLOT(menuRemovePool(QAction*)) );
55  connect(ui->action_SaveKeyToUSB, SIGNAL(triggered()), this, SLOT(menuSaveSSHKey()) );
56  connect(ui->actionClose_Window, SIGNAL(triggered()), this, SLOT(menuCloseWindow()) );
57  connect(ui->menuCompress_Home_Dir, SIGNAL(triggered(QAction*)), this, SLOT(menuCompressHomeDir(QAction*)) );
58  connect(ui->actionExtract_Home_Dir, SIGNAL(triggered()), this, SLOT(menuExtractHomeDir()) );
59  connect(ui->actionAdd_Disk, SIGNAL(triggered()), this, SLOT(menuAddDisk()) );
60  connect(ui->menuRemove_Disk, SIGNAL(triggered(QAction*)), this, SLOT(menuRemoveDisk(QAction*)) );
61  connect(ui->menuSet_Disk_Offline, SIGNAL(triggered(QAction*)), this, SLOT(menuOfflineDisk(QAction*)) );
62  connect(ui->menuSet_Disk_Online, SIGNAL(triggered(QAction*)), this, SLOT(menuOnlineDisk(QAction*)) );
63  connect(ui->action_startScrub, SIGNAL(triggered()), this, SLOT(menuStartScrub()) );
64  connect(ui->action_stopScrub, SIGNAL(triggered()), this, SLOT(menuStopScrub()) );
65  connect(ui->action_newSnapshot, SIGNAL(triggered()), this, SLOT(menuNewSnapshot()) );
66  connect(ui->menuDelete_Snapshot, SIGNAL(triggered(QAction*)), this, SLOT(menuRemoveSnapshot(QAction*)) );
67  connect(ui->actionStart_Replication, SIGNAL(triggered()), this, SLOT(menuStartReplication()) );
68  //Update the interface
69  QTimer::singleShot(0,this,SLOT(updatePoolList()) );
70 
71  //Make sure the status tab is shown initially
72  ui->tabWidget->setCurrentWidget(ui->tab_status);
73  //Now connect the watcher to the update slot
74  connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(autoRefresh()) );
75}
76
77LPMain::~LPMain(){
78       
79}
80
81// ==============
82//      PUBLIC SLOTS
83// ==============
84void LPMain::slotSingleInstance(){
85  this->raise();
86  this->show();
87}
88
89// ==============
90//          PRIVATE
91// ==============
92void LPMain::showErrorDialog(QString title, QString message, QString errors){
93  QMessageBox MB(QMessageBox::Warning, title, message, QMessageBox::Ok, this);
94    MB.setDetailedText(errors);
95    MB.exec();
96}
97
98void LPMain::showWaitBox(QString message){
99  if(waitBox == 0){
100    qDebug() << "New Wait Box";
101    waitBox = new QMessageBox(QMessageBox::NoIcon, tr("Please Wait"), message, QMessageBox::NoButton, this);
102    waitBox->setStandardButtons(QMessageBox::NoButton);
103    waitBox->setWindowModality(Qt::WindowModal);
104  }else{
105    qDebug() << "Update Wait Box:" << message;
106    try{
107      waitBox->setText(message);
108    }catch(...){
109      waitBox = 0; //reset flag if necessary
110      showWaitBox(message);
111      return;
112    }
113  }
114  if(!waitBox->isVisible()){ waitBox->show(); waitBox->raise(); }
115  QCoreApplication::processEvents();
116}
117
118void LPMain::hideWaitBox(){
119  if(waitBox != 0){
120    if(waitBox->isVisible()){ waitBox->hide(); }
121  }
122       
123}
124
125// ==============
126//     PRIVATE SLOTS
127// ==============
128void LPMain::updatePoolList(){
129  //Get the currently selected pool (if there is one)
130  qDebug() << "Update Pool List";
131  QString cPool;
132  if(ui->combo_pools->currentIndex() != -1){ cPool = ui->combo_pools->currentText(); }
133  //Get the list of managed pools
134  qDebug() << "[DEBUG] Fetching list of pools";
135  QStringList pools = LPBackend::listDatasets();
136  QStringList poolsAvail = LPBackend::listPossibleDatasets();
137  //Now put the lists into the UI
138  ui->combo_pools->clear();
139  if(!pools.isEmpty()){ ui->combo_pools->addItems(pools); }
140  //Now set the currently selected pools
141  qDebug() << "[DEBUG] Pool list:" << pools;
142  if(pools.length() > 0){
143    poolSelected=true;   
144    int index = pools.indexOf(cPool);
145    if(index < 0){ ui->combo_pools->setCurrentIndex(0); }
146    else{ ui->combo_pools->setCurrentIndex(index); }
147  }else{
148    //No managed pools
149    poolSelected=false;
150    ui->combo_pools->addItem("No Managed Pools!");
151    ui->combo_pools->setCurrentIndex(0);
152    //Reset to Basic View
153    viewBasic->setChecked(true);
154  }
155  qDebug() << "[DEBUG] Update pool menu options";
156  //Now update the add/remove pool menu's
157  ui->menuManage_Pool->clear();
158  for( int i=0; i<poolsAvail.length(); i++){
159    if(pools.contains(poolsAvail[i])){ continue; } //already managed
160    ui->menuManage_Pool->addAction(poolsAvail[i]);
161  }
162  ui->menuManage_Pool->setEnabled( !ui->menuManage_Pool->isEmpty() );
163  ui->menuUnmanage_Pool->clear();
164  for( int i=0; i<pools.length(); i++){
165    ui->menuUnmanage_Pool->addAction(pools[i]);
166  }
167  ui->menuUnmanage_Pool->setEnabled( !ui->menuUnmanage_Pool->isEmpty() );
168  qDebug() << "[DEBUG] Update user menus";
169  //Now update the user's that are available for home-dir packaging
170  QDir hdir("/usr/home");
171  QStringList users = hdir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
172  ui->menuCompress_Home_Dir->clear();
173  for(int i=0; i<users.length(); i++){
174    ui->menuCompress_Home_Dir->addAction(users[i]);
175  }
176  //Now update the interface appropriately
177  ui->combo_pools->setEnabled(poolSelected);
178  qDebug() << "[DEBUG] Finished updatePoolList()";
179  updateTabs();
180}
181
182void LPMain::viewChanged(){
183  ui->menuView->hide();
184  ui->menubar->clear();
185  settings->setValue("viewmode", viewBasic->isChecked()); //save value for later
186  if(viewBasic->isChecked()){
187    ui->menubar->addMenu(ui->menuFile);
188    ui->menubar->addMenu(ui->menuView);
189    ui->menubar->addMenu(ui->menuClassic_Backups);
190  }else{
191    ui->menubar->addMenu(ui->menuFile);
192    ui->menubar->addMenu(ui->menuView);
193    ui->menubar->addMenu(ui->menuClassic_Backups);
194    ui->menubar->addMenu(ui->menuSnapshots);
195    ui->menubar->addMenu(ui->menuDisks);
196  }
197}
198
199void LPMain::updateTabs(){
200  //qDebug() << "Update Tabs" << poolSelected;
201  qDebug() << "[DEBUG] start updateTabs():" << poolSelected;
202  viewChanged();
203  ui->tabWidget->setEnabled(poolSelected);
204  ui->menuView->setEnabled(poolSelected);       
205  ui->menuDisks->setEnabled(poolSelected); 
206  ui->menuSnapshots->setEnabled(poolSelected);
207  ui->push_configure->setVisible(poolSelected);
208  ui->action_SaveKeyToUSB->setEnabled(poolSelected);
209  if(poolSelected){
210    showWaitBox(tr("Loading zpool information"));
211    qDebug() << "[DEBUG] loadPoolData:" << ui->combo_pools->currentText();
212    POOLDATA = LPGUtils::loadPoolData(ui->combo_pools->currentText());
213    qDebug() << "[DEBUG] loaded data";
214    hideWaitBox();
215    //Now list the status information
216    ui->label_status->setText(POOLDATA.poolStatus);
217    ui->label_numdisks->setText( QString::number(POOLDATA.harddisks.length()) );
218    ui->label_latestsnapshot->setText(POOLDATA.latestSnapshot);
219    qDebug() << "[DEBUG] Latest Snapshot:" << POOLDATA.latestSnapshot;
220    if(POOLDATA.finishedStatus.isEmpty()){ ui->label_finishedstat->setVisible(false); }
221    else{
222      ui->label_finishedstat->setText(POOLDATA.finishedStatus);
223      ui->label_finishedstat->setVisible(true);
224    }
225    if(POOLDATA.runningStatus.isEmpty()){ 
226      ui->label_runningstat->setVisible(false);
227      ui->action_startScrub->setEnabled(true);
228      ui->action_stopScrub->setEnabled(false);
229    }else{
230      ui->label_runningstat->setText(POOLDATA.runningStatus);
231      ui->label_runningstat->setVisible(true);
232      ui->action_startScrub->setEnabled(false); //Something already running
233      ui->action_stopScrub->setEnabled(POOLDATA.runningStatus.contains("scrub", Qt::CaseInsensitive));
234    }       
235    if(POOLDATA.errorStatus.isEmpty()){ ui->label_errorstat->setVisible(false); }
236    else{
237      ui->label_errorstat->setText(POOLDATA.errorStatus);
238      ui->label_errorstat->setVisible(true);
239    }       
240    //Now list the data restore options
241    QString cds = ui->combo_datasets->currentText();
242    ui->combo_datasets->clear();
243    QStringList dslist = POOLDATA.subsets();
244    dslist.sort();
245    //Now move the home directories to the top of the list
246    int moved = 0;
247    for(int i=0; i<dslist.length(); i++){  //make sure it stays in alphabetical order
248      if(dslist[i].startsWith("/usr/home/")){
249        dslist.move(i,moved);
250        moved++; 
251        i--; //make sure to not miss any items from moving
252      }
253    }
254    ui->combo_datasets->addItems(dslist);
255    int dsin = dslist.indexOf(cds);
256    if(dsin >= 0){ ui->combo_datasets->setCurrentIndex(dsin); }
257    else if( !dslist.isEmpty() ){ ui->combo_datasets->setCurrentIndex(0); }
258    else{ ui->combo_datasets->addItem(tr("No datasets available")); }
259    //NOTE: this automatically calls the "updateDataset()" function in a new thread
260   
261    //Now update the snapshot removal menu list
262    //QStringList snapComments;
263    QStringList snaps = POOLDATA.allSnapshots(); //LPBackend::listLPSnapshots(ui->combo_pools->currentText(), snapComments);
264    ui->menuDelete_Snapshot->clear();
265    for(int i=0; i<snaps.length(); i++){
266       QString comment = POOLDATA.snapshotComment(snaps[i]).simplified();
267        if(comment.isEmpty()){ ui->menuDelete_Snapshot->addAction(snaps[i]); }
268        else{ ui->menuDelete_Snapshot->addAction(snaps[i] + " (" + comment + ")" ); }
269    }
270    ui->menuDelete_Snapshot->setEnabled( !ui->menuDelete_Snapshot->isEmpty() );
271    ui->actionStart_Replication->setEnabled( !POOLDATA.repHost.isEmpty() );
272    //Now update the disk menu items
273    ui->menuRemove_Disk->clear();
274    ui->menuSet_Disk_Offline->clear();
275    ui->menuSet_Disk_Online->clear();
276    for(int i=0; i<POOLDATA.harddisks.length(); i++){
277      ui->menuRemove_Disk->addAction(POOLDATA.harddisks[i]);
278      if(POOLDATA.harddiskStatus[i] == "OFFLINE"){
279        ui->menuSet_Disk_Online->addAction(POOLDATA.harddisks[i]);
280      }else{
281        ui->menuSet_Disk_Offline->addAction(POOLDATA.harddisks[i]);     
282      }
283    }
284    ui->menuRemove_Disk->setEnabled(!ui->menuRemove_Disk->isEmpty());
285    ui->menuSet_Disk_Offline->setEnabled(!ui->menuSet_Disk_Offline->isEmpty());
286    ui->menuSet_Disk_Online->setEnabled(!ui->menuSet_Disk_Online->isEmpty());
287  }else{
288    //No Pool selected
289    ui->label_numdisks->clear();
290    ui->label_latestsnapshot->clear();
291    ui->label_status->clear();
292          ui->label_errorstat->setVisible(false);
293          ui->label_runningstat->setVisible(false);
294          ui->label_finishedstat->setVisible(false);
295  }
296
297}
298
299void LPMain::updateDataset(){
300  //Update the snapshots for the currently selected dataset
301  QString cds = ui->combo_datasets->currentText();
302  if(POOLDATA.subsets().indexOf(cds) >= 0){
303    QStringList snaps = POOLDATA.snapshots(cds);
304      qDebug() << "Update Dataset";
305      ui->slider_snapshots->setEnabled(true);
306      ui->slider_snapshots->setMinimum(0);
307      int max = snaps.length() -1;
308      if(max < 0){ max = 0; ui->slider_snapshots->setEnabled(false); }
309      ui->slider_snapshots->setMaximum(max);
310      ui->slider_snapshots->setValue(max); //most recent snapshot
311      int interval = 1; //one tick per snapshot
312      if( max > 20 ){ interval = max / 20; } //show 20 ticks
313      ui->slider_snapshots->setTickInterval(interval);
314
315      updateSnapshot();
316  }else{
317    ui->slider_snapshots->setEnabled(false);
318    ui->label_snapshot->clear();
319    ui->push_nextsnap->setEnabled(false);
320    ui->push_prevsnap->setEnabled(false);
321  }
322       
323}
324
325void LPMain::updateSnapshot(){
326  int sval = ui->slider_snapshots->value();
327  QStringList snaps = POOLDATA.snapshots(ui->combo_datasets->currentText());
328  //qDebug() << "Update Snapshot";
329  //Update the previous/next buttons
330  if(sval == ui->slider_snapshots->minimum() ){ ui->push_prevsnap->setEnabled(false); }
331  else{ ui->push_prevsnap->setEnabled(true); }
332  if(sval == ui->slider_snapshots->maximum() ){ ui->push_nextsnap->setEnabled(false); }
333  else{ ui->push_nextsnap->setEnabled(true); }
334  //Now update the snapshot viewer
335  if(snaps.isEmpty()){ ui->label_snapshot->clear(); ui->slider_snapshots->setEnabled(false); }
336  else{
337    QString snap = snaps.at(sval);
338    QString path = ui->combo_datasets->currentText() + "/.zfs/snapshot/"+snap;
339    //qDebug() << "Snapshot path:" << path;
340    QString comment = POOLDATA.snapshotComment(snap).simplified();
341    if(comment.isEmpty()){ ui->label_snapshot->setText(snap); }
342    else{ ui->label_snapshot->setText(snap+" ("+comment+")"); }
343    //Now update the snapshot view
344    ui->treeView->setRootIndex( fsModel->setRootPath(path) );
345   
346  }
347}
348
349void LPMain::nextSnapshot(){
350  ui->slider_snapshots->setValue( ui->slider_snapshots->value()+1 );
351}
352
353void LPMain::prevSnapshot(){
354  ui->slider_snapshots->setValue( ui->slider_snapshots->value()-1 );
355}
356
357void LPMain::setFileVisibility(){
358  if(ui->check_hidden->isChecked()){
359    fsModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden );
360  }else{
361    fsModel->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot );
362  }
363}
364
365void LPMain::restoreFiles(){
366  QString filePath = fsModel->filePath( ui->treeView->currentIndex() );
367  qDebug() << " Restore file(s):" << filePath;
368  QFileInfo info(filePath);     
369  QString destDir = filePath;
370        destDir.remove("/.zfs/snapshot/"+ui->label_snapshot->text().section("(",0,0).simplified());
371        destDir.chop( filePath.section("/",-1).size()+1 ); //get rid of the filename at the end
372        while(!QFile::exists(destDir)){ destDir.chop( destDir.section("/",-1).size() +1); }
373  QString newFilePath = destDir+"/"+LPGUtils::generateReversionFileName(filePath, destDir);
374  //qDebug() << "Destination:" << newFilePath;
375  //Perform the reversion(s)
376  QStringList errors;
377  if( info.isDir() ){
378    //Is a directory
379    showWaitBox( QString(tr("Restoring Directory: %1")).arg(newFilePath) );
380    errors = LPGUtils::revertDir(filePath, newFilePath);
381    hideWaitBox();
382    if(!errors.isEmpty()){
383      qDebug() << "Failed Reversions:" << errors;
384      errors.prepend(tr("File destination(s) that could not be restored:")+"\n");
385      showErrorDialog(tr("Reversion Error"), tr("Some files could not be restored from the snapshot."), errors.join("\n") );
386    }else{
387      qDebug() << "Reversion successful";           
388      QMessageBox::information(this,tr("Restore Successful"),QString(tr("The following directory was succesfully restored: %1")).arg(newFilePath) );
389    }
390  }else{
391    //Just a single file
392    showWaitBox( QString(tr("Restoring file: %1")).arg(newFilePath) );
393    bool ok = LPGUtils::revertFile(filePath, newFilePath);
394    hideWaitBox();
395    if( !ok ){
396      qDebug() << "Failed Reversion:" << newFilePath;
397      errors << QString(tr("Snapshot file: %1")).arg(filePath);
398      errors << QString(tr("Destination: %1")).arg(newFilePath);
399      errors << tr("Please check that the destination directory exists and is writable");
400      showErrorDialog(tr("Reversion Error"), tr("The file could not be restored from the snapshot."), errors.join("\n") );
401    }else{
402      qDebug() << "Reversion successful";
403      QMessageBox::information(this,tr("Restore Successful"),QString(tr("The following file was succesfully restored: %1")).arg(newFilePath) );
404    }
405  }       
406       
407}
408
409void LPMain::openConfigGUI(){
410  qDebug() << "Open Configuration UI";
411  QString ds = ui->combo_pools->currentText();
412  if(ds.isEmpty()){ return; }
413  LPConfig CFG(this);
414  CFG.loadDataset(ds, LPBackend::listReplicationTargets().contains(ds));
415  CFG.exec();
416  //Now check for return values and update appropriately
417  bool change = false;
418  if(CFG.localChanged){
419    ui->statusbar->showMessage(QString(tr("Configuring dataset: %1")).arg(ds),0);
420    qDebug() << "Settings up local snapshots:" << ds << "Frequency:" << CFG.localSchedule;
421    LPBackend::setupDataset(ds, CFG.localSchedule, CFG.localSnapshots);
422    ui->statusbar->clearMessage();
423    change = true;
424  }
425  if(CFG.remoteChanged){
426    change = true;
427    if(CFG.isReplicated){
428      ui->statusbar->showMessage(QString(tr("Configuring replication: %1")).arg(ds),0);
429      qDebug() << "Setting up Replication:" << ds << " Frequency:" << CFG.remoteFreq;
430      LPBackend::setupReplication(ds, CFG.remoteHost, CFG.remoteUser, CFG.remotePort, CFG.remoteDataset, CFG.remoteFreq);
431      QMessageBox::information(this,tr("Reminder"),tr("Don't forget to save your SSH key to a USB stick so that you can restore your system from the remote host later!!"));
432    }else{
433      ui->statusbar->showMessage(QString(tr("Removing replication: %1")).arg(ds),0);
434      qDebug() << "Removing Replication:" << ds;
435      LPBackend::removeReplication(ds);
436    }
437    ui->statusbar->clearMessage();
438  }
439  //Now update the UI if appropriate
440  if(change){
441    updateTabs();
442  }     
443}
444
445void LPMain::autoRefresh(){
446  //This slot makes sure that the GUI is not refreshed too frequently
447  if(!timer->isActive()){
448    timer->start(); //start countdown to GUI refresh
449  }       
450}
451
452// -----------------------------------------------
453//   MENU SLOTS
454// -----------------------------------------------
455// ==== File Menu ====
456void LPMain::menuAddPool(QAction *act){
457  QString dataset = act->text();
458  qDebug() << "Start Wizard for new managing pool:" << dataset;
459  LPWizard wiz(this);
460  wiz.setDataset(dataset);
461  wiz.exec();
462  //See if the wizard was cancelled or not
463  if(!wiz.cancelled){
464    ui->statusbar->showMessage(QString(tr("Enabling dataset management: %1")).arg(dataset),0);
465    //run the proper commands to get the dataset enabled
466    qDebug() << "Setup Snapshots:" << dataset << " Frequency:" << wiz.localTime;
467    if( LPBackend::setupDataset(dataset, wiz.localTime, wiz.totalSnapshots) ){
468      if(wiz.enableReplication){
469         qDebug() << "Setting up replication:" << dataset << " Frequency:" << wiz.remoteTime;
470         LPBackend::setupReplication(dataset, wiz.remoteHost, wiz.remoteUser, wiz.remotePort, wiz.remoteDataset, wiz.remoteTime);     
471         QMessageBox::information(this,tr("Reminder"),tr("Don't forget to save your SSH key to a USB stick so that you can restore your system from the remote host later!!"));
472      }
473    }
474    ui->statusbar->clearMessage();
475    //Now update the list of pools
476    updatePoolList();
477  }     
478}
479
480void LPMain::menuRemovePool(QAction *act){
481  QString ds = act->text();
482  qDebug() << "Remove Pool:" << ds;
483  if(!ds.isEmpty()){
484    //Verify the removal of the dataset
485    if( QMessageBox::Yes == QMessageBox::question(this,tr("Verify Dataset Backup Removal"),tr("Are you sure that you wish to cancel automated snapshots and/or replication of the following dataset?")+"\n\n"+ds,QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){           
486      //verify the removal of all the snapshots for this dataset
487      QStringList snapComments;
488      QStringList snaps = LPBackend::listLPSnapshots(ds, snapComments);
489      if(!snaps.isEmpty()){
490        if( QMessageBox::Yes == QMessageBox::question(this,tr("Verify Snapshot Deletion"),tr("Do you wish to remove the local snapshots for this dataset?")+"\n"+tr("WARNING: This is a permanant change that cannot be reversed"),QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){
491          //Remove all the snapshots
492          ui->statusbar->showMessage(QString(tr("%1: Removing snapshots")).arg(ds),0);
493          showWaitBox(tr("Removing snapshots"));
494          for(int i=0; i<snaps.length(); i++){
495            LPBackend::removeSnapshot(ds,snaps[i]);
496          }
497          ui->statusbar->clearMessage();
498        }
499      }
500      //Remove the dataset from life-preserver management
501      if(LPBackend::listReplicationTargets().contains(ds)){ 
502        ui->statusbar->showMessage(QString(tr("%1: Disabling Replication")).arg(ds),0);
503        showWaitBox(tr("Disabling Replication"));
504        LPBackend::removeReplication(ds); 
505        ui->statusbar->clearMessage();     
506      }
507      ui->statusbar->showMessage(QString(tr("%1: Disabling Life-Preserver Management")).arg(ds),0);
508      showWaitBox(tr("Removing Life Preserver Schedules"));
509      LPBackend::removeDataset(ds);
510      ui->statusbar->clearMessage();
511      updatePoolList();
512      hideWaitBox();
513    }
514  } //end check for empty ds
515
516}
517
518void LPMain::menuSaveSSHKey(){
519  QString ds = ui->combo_pools->currentText(); 
520  qDebug() << "Save SSH Key:" << ds;
521  if(ds.isEmpty()){ return; }
522  //Get the local hostname
523  char host[1023] = "\0";
524  gethostname(host,1023);
525  QString localHost = QString(host).simplified();
526  qDebug() << " - hostname:" << localHost;
527  //Scan for mounted USB devices
528  QStringList devs = LPBackend::findValidUSBDevices();
529  qDebug() << " - devs:" << devs;
530  if(devs.isEmpty()){
531    QMessageBox::warning(this,tr("No Valid USB Devices"), tr("No valid USB devices could be found. Please mount a FAT32 formatted USB stick and try again."));
532    return;
533  }
534  //Ask the user which one to save the file to
535  bool ok;
536  QString dev = QInputDialog::getItem(this, tr("Select USB Device"), tr("Available USB Devices:"), devs,0,false,&ok);   
537  if(!ok or dev.isEmpty()){ return; } //cancelled
538  QString devPath = dev.section("(",0,0).simplified();
539  //Now copy the file over
540  ok = LPBackend::copySSHKey(devPath, localHost);
541  if(ok){
542    QMessageBox::information(this,tr("Success"), tr("The public SSH key file was successfully copied onto the USB device."));
543  }else{
544    QMessageBox::information(this,tr("Failure"), tr("The public SSH key file could not be copied onto the USB device."));
545  }
546}
547
548void LPMain::menuCloseWindow(){
549  this->close();
550}
551
552// ==== Classic Backups Menu ====
553void LPMain::menuCompressHomeDir(QAction* act){
554  QString user = act->text();
555  qDebug() << "Compress Home Dir:" << user;
556  //Start up the Classic Dialog
557  bool recreate = false;
558  if(classicDLG == 0){ recreate = true; }
559  if(recreate){
560    classicDLG = new LPClassic(this);
561    classicDLG->setHomeDir("/usr/home/"+user);
562    classicDLG->show();
563  }else if(!classicDLG->running){
564    classicDLG->setHomeDir("/usr/home/"+user); //move to the alternate user dir
565  }
566  classicDLG->raise();
567  classicDLG->show();
568  /*
569  //Prompt for the package name
570  QString pkgName = user+"-"+QDateTime::currentDateTime().toString("yyyyMMdd-hhmm");
571  bool ok;
572  pkgName = QInputDialog::getText(this, tr("Package Name"), tr("Name of the package to create:"), QLineEdit::Normal, pkgName, &ok);
573  if(!ok || pkgName.isEmpty() ){ return; } //cancelled
574  //Now create the package
575  showWaitBox(tr("Packaging home directory"));
576  QString pkgPath = LPGUtils::packageHomeDir(user, pkgName);
577  hideWaitBox();
578  //Now inform the user of the result
579  if(pkgPath.isEmpty()){
580    qDebug() << "No Package created";
581    QMessageBox::warning(this,tr("Package Failure"), tr("The home directory package could not be created."));
582  }else{
583    qDebug() << "Package created at:" << pkgPath;
584    QMessageBox::information(this,tr("Package Success"), tr("The home directory package was successfully created.")+"\n\n"+pkgPath);
585  }
586  */
587}
588
589void LPMain::menuExtractHomeDir(){
590  qDebug() << "Extract Home Dir";
591  //Get the file path from the user
592  QString filePath = QFileDialog::getOpenFileName(this,tr("Find Home Dir Package"), "/usr/home", tr("Home Dir Package (*.home.tar.gz)") );
593  if(filePath.isEmpty() || !QFile::exists(filePath)){ return; } //cancelled
594  //Now check if the user in the package is also on the system
595  QString username;
596  bool ok = LPGUtils::checkPackageUserPath(filePath, &username);
597  if(!ok){
598    QMessageBox::warning(this,tr("User Missing"),QString(tr("The user (%1) does not exist on this system. Please create this user first and then try again.")).arg(username) );
599    return;
600  }
601  //Now extract the package
602  showWaitBox(tr("Extracting Home Directory"));
603  ok = LPGUtils::extractHomeDirPackage(filePath);
604  hideWaitBox();
605  //Now report the results
606  if(ok){
607    QMessageBox::information(this,tr("Package Extracted"), QString(tr("The package was successfully extracted within %1")).arg("/usr/home/"+username) );
608  }else{
609    QMessageBox::warning(this, tr("Package Failure"), QString(tr("The package could not be extracted within %1")).arg("/usr/home/"+username) );
610  }
611 
612}
613
614// ==== Disks Menu ====
615void LPMain::menuAddDisk(){
616  QString pool = ui->combo_pools->currentText();
617  //Get the available disks and remove the current disks
618  QStringList adisks = LPGUtils::listAvailableHardDisks();
619  for(int i=0; i<POOLDATA.harddisks.length(); i++){
620    adisks.removeAll( POOLDATA.harddisks[i].section("s",0,0,QString::SectionSkipEmpty) );
621  }
622  if(adisks.isEmpty()){
623    QMessageBox::information(this,tr("Attach New Disk"), tr("No available disks could be found"));
624    return;
625  }
626  //Find a disk that can be added to the system
627  bool ok=false;
628  QString disk = QInputDialog::getItem(this, tr("Attach New Disk"),tr("Detected Disks:"), adisks,0,false, &ok);
629  if( !ok || disk.isEmpty() ){ return; }
630  qDebug() << "Add Disk:" << disk << pool;
631  showWaitBox(tr("Attaching disk"));
632  ok = LPBackend::attachDisk(pool, disk);
633  hideWaitBox();
634  if(ok){
635    QMessageBox::information(this,tr("Disk Attached"),QString(tr("Success: %1 was added to %2")).arg(disk,pool) );
636    QTimer::singleShot(0,this,SLOT(updateTabs()) );
637  }else{
638    QMessageBox::warning(this,tr("Disk Attach Error"),QString(tr("Failure: %1 could not be attached to %2.")).arg(disk,pool) );
639  }     
640}
641
642void LPMain::menuRemoveDisk(QAction *act){
643  QString disk = act->text();
644  QString pool = ui->combo_pools->currentText();
645  //Verify action
646  if(QMessageBox::Yes != QMessageBox::question(this,tr("Verify Disk Removal"),QString(tr("Are you sure that you want to remove %1 from %2?")).arg(disk,pool) + "\n\n" + tr("CAUTION: This disk can only be re-attached later as a brand new disk"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){
647    return; //cancelled
648  }
649  qDebug() << "Remove Disk:" << disk << pool;
650  showWaitBox(tr("Detaching disk"));
651  bool ok = LPBackend::detachDisk(pool, disk);
652  hideWaitBox();
653  if(ok){
654    QMessageBox::information(this,tr("Disk Removal Success"),QString(tr("Success: %1 was removed from %2")).arg(disk, pool) );
655    QTimer::singleShot(0,this,SLOT(updateTabs()) );
656  }else{
657    QMessageBox::warning(this,tr("Disk Removal Failure"),QString(tr("Failure: %1 could not be removed from %2 at this time.")).arg(disk, pool) );
658  }
659}
660
661void LPMain::menuOfflineDisk(QAction *act){
662  QString disk = act->text();
663  QString pool = ui->combo_pools->currentText();
664  //Verify action
665  if(QMessageBox::Yes != QMessageBox::question(this,tr("Verify Disk Offline"),QString(tr("Are you sure you wish to set %1 offline?")).arg(disk), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){
666    return; //cancelled
667  }
668  qDebug() << "Offline Disk:" << disk << pool;
669  showWaitBox(tr("Setting disk offline"));
670  bool ok = LPBackend::setDiskOffline(pool, disk);
671  hideWaitBox();
672  if(ok){
673    QMessageBox::information(this,tr("Disk Offline Success"),QString(tr("Success: %1 has been taken offline.")).arg(disk) );
674    QTimer::singleShot(0,this,SLOT(updateTabs()) );
675  }else{
676    QMessageBox::warning(this,tr("Disk Offline Failure"),QString(tr("Failure: %1 could not be taken offline at this time.")).arg(disk) );
677  }
678}
679
680void LPMain::menuOnlineDisk(QAction *act){
681  QString disk = act->text();
682  QString pool = ui->combo_pools->currentText();
683  //Verify action
684  if(QMessageBox::Yes != QMessageBox::question(this,tr("Verify Disk Online"),QString(tr("Are you sure you wish to set %1 online?")).arg(disk), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){
685    return; //cancelled
686  }
687  qDebug() << "Online Disk:" << disk << pool;
688  showWaitBox(tr("Setting disk online"));
689  bool ok = LPBackend::setDiskOnline(pool, disk);
690  hideWaitBox();
691  if(ok){
692    QMessageBox::information(this,tr("Disk Online Success"),QString(tr("Success: %1 has been set online.")).arg(disk) );
693    QTimer::singleShot(0,this,SLOT(updateTabs()) );
694  }else{
695    QMessageBox::warning(this,tr("Disk Online Failure"),QString(tr("Failure: %1 could not be set online at this time.")).arg(disk) );
696  }
697}
698
699void LPMain::menuStartScrub(){
700  QString pool = ui->combo_pools->currentText();
701  //Verify starting a scrub
702  if( QMessageBox::Yes != QMessageBox::question(this,tr("Verify Scrub"),QString(tr("Are you sure you want to start a scrub on %1?")).arg(pool) + "\n"+tr("NOTE: This may take quite a while to complete"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){
703    return; //cancelled   
704  }
705  qDebug() << "Start Scrub:" << pool;
706  QString cmd = "zpool scrub "+pool;
707  showWaitBox(tr("Trying to start a scrub"));
708  int ret = system(cmd.toUtf8());
709  hideWaitBox();
710  if(ret == 0){
711    //Now let te user know that one has been triggered
712    QMessageBox::information(this,tr("Scrub Started"),QString(tr("A scrub has just been started on %1")).arg(pool));
713    QTimer::singleShot(0,this,SLOT(updateTabs()) );
714  }else{
715    QMessageBox::warning(this,tr("Scrub Not Started"), QString(tr("A scrub on %1 could not be started at this time.")).arg(pool) + "\n"+tr("Please wait until any current resilvering or scrubs are finished before trying again.") );
716  }
717}
718
719void LPMain::menuStopScrub(){
720  QString pool = ui->combo_pools->currentText();
721  //Verify stopping a scrub
722  if( QMessageBox::Yes != QMessageBox::question(this,tr("Verify Scrub"),QString(tr("Are you sure you want to stop the scrub on %1?")).arg(pool), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){
723    return; //cancelled   
724  }
725  qDebug() << "Stop Scrub:" << pool;
726  QString cmd = "zpool scrub -s "+pool;
727  showWaitBox(tr("Trying to stop scrub"));
728  int ret = system(cmd.toUtf8());
729  hideWaitBox();
730  if(ret == 0){
731    //Now let te user know that one has been triggered
732    QMessageBox::information(this,tr("Scrub Stopped"),QString(tr("The scrub on %1 has been stopped.")).arg(pool));
733    QTimer::singleShot(0,this,SLOT(updateTabs()) );
734  }else{
735    QMessageBox::warning(this,tr("Scrub Not Running"), QString(tr("There was no scrub running on %1.")).arg(pool) );
736  }     
737}
738
739// ==== Snapshots Menu ====
740void LPMain::menuNewSnapshot(){
741  qDebug() << "New Snapshot";
742  QString ds = ui->combo_pools->currentText();
743  if(ds.isEmpty()){return; }
744  //Get the new snapshot name from the user
745  bool ok;
746  QString name = QInputDialog::getText(this,tr("New Snapshot Name"), tr("Snapshot Name:"), QLineEdit::Normal, tr("Name"), &ok, 0, Qt::ImhUppercaseOnly | Qt::ImhLowercaseOnly | Qt::ImhDigitsOnly );
747  if(!ok || name.isEmpty()){ return; } //cancelled
748  //Now get the comment
749   QString comment = QInputDialog::getText (this, QObject::tr("Snapshot comment"), QObject::tr("Snapshot comment"), QLineEdit::Normal, tr("GUI Snapshot"), &ok);
750   if (!ok){ comment = tr("GUI Snapshot"); }
751  qDebug() << "Creating a new snapshot:" << ds << name << comment;
752  //Now create the new snapshot
753  LPBackend::newSnapshot(ds,name, comment);
754  QMessageBox::information(this,tr("Snapshot Pending"), tr("The new snapshot creation has been added to the queue"));
755  updateTabs();
756}
757
758void LPMain::menuRemoveSnapshot(QAction *act){
759  QString snapshot = act->text().section(" ", 0, 0);
760  QString comment = act->text().section(" ", 1, -1);
761  QString pool = ui->combo_pools->currentText();
762  qDebug() << "Remove Snapshot:" << snapshot;
763  //verify snapshot removal
764  if( QMessageBox::Yes == QMessageBox::question(this,tr("Verify Snapshot Deletion"),QString(tr("Do you wish to delete this snapshot? %1 (%2)")).arg(pool+"/"+snapshot, comment)+"\n"+tr("WARNING: This is a permanant change that cannot be reversed"),QMessageBox::Yes | QMessageBox::No, QMessageBox::No) ){
765    bool ok = LPBackend::removeSnapshot(ui->combo_pools->currentText(), snapshot);
766    if(ok){
767      QMessageBox::information(this,tr("Snapshot Removed"),tr("The snapshot was successfully deleted"));
768    }else{
769      QMessageBox::information(this,tr("Snapshot Removal Failure"),tr("The snapshot removal experienced an error and it not be completed at this time."));
770    }
771    updateTabs();
772  }
773}
774
775void LPMain::menuStartReplication(){
776  //Get the pool/host as appropriate
777  QString pool = ui->combo_pools->currentText();
778  QString host = POOLDATA.repHost;
779  if(host.isEmpty()){ return; } //invalid
780  QString cmd = "lpreserver replicate run %1 %2";
781  cmd = cmd.arg(pool, host);
782  QProcess::startDetached(cmd);
783  QMessageBox::information(this, tr("Replication Triggered"), tr("A replication has been queued up for this dataset"));
784}
Note: See TracBrowser for help on using the repository browser.