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

releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since ec61e8e was ec61e8e, checked in by Ken Moore <ken@…>, 11 months ago

Make sure that the life-preserver GUI saves which view mode the UI is in for the next time it gets started.

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