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

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

Adjust the replication detection routine for the Life-Preserver configuration UI and enable a bit more debugging as to when changes are getting started in the background.

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