Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add the beginnings of the new Life preserver GUI application. It curr…

…ently runs but only displays the available datasets/snapshots with a working timer-slider (no snapshot viewer yet).
  • Loading branch information...
commit 5baebda58c0d9fbacc8e50b4c50724ca02f499e7 1 parent dbb91c3
@beanpole135 beanpole135 authored
Showing with 16,945 additions and 0 deletions.
  1. +373 −0 lp-gui/LPBackend.cpp
  2. +51 −0 lp-gui/LPBackend.h
  3. BIN  lp-gui/LPBackend.o
  4. +171 −0 lp-gui/LPConfig.cpp
  5. +39 −0 lp-gui/LPConfig.h
  6. BIN  lp-gui/LPConfig.o
  7. +397 −0 lp-gui/LPConfig.ui
  8. +32 −0 lp-gui/LPContainers.h
  9. +68 −0 lp-gui/LPGUtils.cpp
  10. +18 −0 lp-gui/LPGUtils.h
  11. BIN  lp-gui/LPGUtils.o
  12. +179 −0 lp-gui/LPMain.cpp
  13. +47 −0 lp-gui/LPMain.h
  14. BIN  lp-gui/LPMain.o
  15. +593 −0 lp-gui/LPMain.ui
  16. +58 −0 lp-gui/LPWizard.cpp
  17. +35 −0 lp-gui/LPWizard.h
  18. BIN  lp-gui/LPWizard.o
  19. +627 −0 lp-gui/LPWizard.ui
  20. +362 −0 lp-gui/Makefile
  21. +4 −0 lp-gui/images/.directory
  22. BIN  lp-gui/images/application-exit.png
  23. BIN  lp-gui/images/backup-failed.png
  24. BIN  lp-gui/images/backup-ok.png
  25. BIN  lp-gui/images/camera_add.png
  26. BIN  lp-gui/images/configure.png
  27. BIN  lp-gui/images/copy.png
  28. BIN  lp-gui/images/drive-removable-media.png
  29. BIN  lp-gui/images/folder.png
  30. BIN  lp-gui/images/key.png
  31. BIN  lp-gui/images/lifepreserver.png
  32. BIN  lp-gui/images/list-add.png
  33. BIN  lp-gui/images/list-remove.png
  34. BIN  lp-gui/images/network-wired.png
  35. BIN  lp-gui/images/next.png
  36. BIN  lp-gui/images/previous.png
  37. BIN  lp-gui/images/system-run.png
  38. BIN  lp-gui/images/tray-icon-active1.png
  39. BIN  lp-gui/images/tray-icon-active10.png
  40. BIN  lp-gui/images/tray-icon-active11.png
  41. BIN  lp-gui/images/tray-icon-active12.png
  42. BIN  lp-gui/images/tray-icon-active13.png
  43. BIN  lp-gui/images/tray-icon-active14.png
  44. BIN  lp-gui/images/tray-icon-active15.png
  45. BIN  lp-gui/images/tray-icon-active16.png
  46. BIN  lp-gui/images/tray-icon-active2.png
  47. BIN  lp-gui/images/tray-icon-active3.png
  48. BIN  lp-gui/images/tray-icon-active4.png
  49. BIN  lp-gui/images/tray-icon-active5.png
  50. BIN  lp-gui/images/tray-icon-active6.png
  51. BIN  lp-gui/images/tray-icon-active7.png
  52. BIN  lp-gui/images/tray-icon-active8.png
  53. BIN  lp-gui/images/tray-icon-active9.png
  54. BIN  lp-gui/images/tray-icon-error.png
  55. BIN  lp-gui/images/tray-icon-failed.png
  56. BIN  lp-gui/images/tray-icon-idle.png
  57. BIN  lp-gui/images/tray-icon-working.png
  58. BIN  lp-gui/images/upload.png
  59. BIN  lp-gui/images/view.png
  60. +42 −0 lp-gui/lPreserve.qrc
  61. BIN  lp-gui/life-preserver
  62. +105 −0 lp-gui/life-preserver.pro
  63. +42 −0 lp-gui/main.cpp
  64. BIN  lp-gui/main.o
  65. +99 −0 lp-gui/moc_LPConfig.cpp
  66. BIN  lp-gui/moc_LPConfig.o
  67. +107 −0 lp-gui/moc_LPMain.cpp
  68. BIN  lp-gui/moc_LPMain.o
  69. +96 −0 lp-gui/moc_LPWizard.cpp
  70. BIN  lp-gui/moc_LPWizard.o
  71. +11,943 −0 lp-gui/qrc_lPreserve.cpp
  72. BIN  lp-gui/qrc_lPreserve.o
  73. +46 −0 lp-gui/scripts/setup-ssh-keys.sh
  74. +373 −0 lp-gui/ui_LPConfig.h
  75. +518 −0 lp-gui/ui_LPMain.h
  76. +520 −0 lp-gui/ui_LPWizard.h
View
373 lp-gui/LPBackend.cpp
@@ -0,0 +1,373 @@
+#include "LPBackend.h"
+
+// ==============
+// Informational
+// ==============
+QStringList LPBackend::listPossibleDatasets(){
+ QString cmd = "zpool list -H -o name";
+ QStringList out = LPBackend::getCmdOutput(cmd);
+ //Now process the output (one dataset per line - no headers)
+ QStringList list;
+ for(int i=0; i<out.length(); i++){
+ QString ds = out[i].section("/",0,0).simplified();
+ if(!ds.isEmpty()){ list << ds; }
+ }
+ list.removeDuplicates();
+
+ return list;
+}
+
+QStringList LPBackend::listDatasets(){
+ QString cmd = "lpreserver listcron";
+ QStringList out = LPBackend::getCmdOutput(cmd);
+ //Now process the output
+ QStringList list;
+ for(int i=2; i<out.length(); i++){ //skip the first two lines (headers)
+ QString ds = out[i].section(" - ",0,0).simplified();
+ if(!ds.isEmpty()){ list << ds; }
+ }
+
+ return list;
+}
+
+QStringList LPBackend::listDatasetSubsets(QString dataset){
+ QString cmd = "zfs list -H -t filesystem -o name,mountpoint,mounted";
+ QStringList out = LPBackend::getCmdOutput(cmd);
+ //Now process the output (one dataset per line - no headers)
+ QStringList list;
+ for(int i=0; i<out.length(); i++){
+ if(out[i].startsWith(dataset+"/")){
+ if(out[i].section("\t",2,2,QString::SectionSkipEmpty).simplified() == "yes"){
+ QString ds = out[i].section("\t",1,1).simplified(); //save the mountpoint
+ if(!ds.isEmpty()){ list << ds; }
+ }
+ }
+ }
+ list.removeDuplicates();
+
+ return list;
+}
+
+QStringList LPBackend::listSnapshots(QString dsmountpoint){
+ //List all the snapshots available for the given dataset mountpoint
+ QDir dir(dsmountpoint+"/.zfs/snapshot");
+ QStringList list;
+ if(dir.exists()){
+ list = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time | QDir::Reversed);
+ }
+ return list;
+}
+
+QStringList LPBackend::listLPSnapshots(QString dataset){
+ QString cmd = "lpreserver listsnap "+dataset;
+ QStringList out = LPBackend::getCmdOutput(cmd);
+ //Now process the output
+ QStringList list;
+ for(int i=out.length()-1; i>=0; i--){ //go in reverse order for proper time format (newest first)
+ if(out[i].startsWith(dataset+"@")){
+ QString snap = out[i].section("@",1,3).section(" ",0,0).simplified();;
+ if(!snap.isEmpty()){ list << snap; }
+ }
+ }
+
+ return list;
+}
+
+QStringList LPBackend::listReplicationTargets(){
+ QString cmd = "lpreserver replicate list";
+ QStringList out = LPBackend::getCmdOutput(cmd);
+ //Now process the output
+ QStringList list;
+ for(int i=0; i<out.length(); i++){
+ if(out[i].contains("->")){
+ QString ds = out[i].section("->",0,0).simplified();
+ if(!ds.isEmpty()){ list << ds; }
+ }
+ }
+
+ return list;
+}
+
+QStringList LPBackend::listCurrentStatus(){
+ QString cmd = "lpreserver status";
+ QStringList out = LPBackend::getCmdOutput(cmd);
+ //Now process the output
+ QStringList list;
+ for(int i=2; i<out.length(); i++){ //first 2 lines are headers
+ //Format: <dataset>:::<lastsnapshot | NONE>:::<lastreplication | NONE>
+ if(out[i].isEmpty()){ continue; }
+ QString ds = out[i].section(" - ",0,0).simplified();
+ QString snap = out[i].section(" - ",1,1).simplified();
+ QString rep = out[i].section(" - ",2,2).simplified();
+ if(snap == "NONE"){ snap = "-"; }
+ if(rep == "NONE"){ rep = "-"; }
+ list << ds +":::"+ snap+":::"+rep;
+ }
+
+ return list;
+}
+
+// ==================
+// Dataset Management
+// ==================
+bool LPBackend::setupDataset(QString dataset, int time, int numToKeep){
+ //Configure inputs
+ QString freq;
+ if(time == -30){ freq = "30min"; }
+ else if(time == -10){ freq = "10min"; }
+ else if(time == -5){ freq = "5min"; }
+ else if(time >= 0 && time < 24){ freq = "daily@"+QString::number(time); }
+ else{ freq = "hourly"; }
+
+ //Create the command
+ QString cmd = "lpreserver cronsnap "+dataset+" start "+freq+" "+QString::number(numToKeep);
+ int ret = LPBackend::runCmd(cmd);
+
+ return (ret == 0);
+}
+
+bool LPBackend::removeDataset(QString dataset){
+ QString cmd = "lpreserver cronsnap "+dataset+" stop";
+ int ret = LPBackend::runCmd(cmd);
+
+ return (ret == 0);
+}
+
+bool LPBackend::datasetInfo(QString dataset, int& time, int& numToKeep){
+ QString cmd = "lpreserver listcron";
+ QStringList out = LPBackend::getCmdOutput(cmd);
+ //Now process the output
+ bool ok = false;
+ for(int i=0; i<out.length(); i++){
+ if(out[i].section(" - ",0,0).simplified() == dataset){
+ //Get time schedule (in integer format)
+ QString sch = out[i].section(" - ",1,1).simplified();
+ if(sch.startsWith("daily@")){ time = sch.section("@",1,1).simplified().toInt(); }
+ else if(sch=="5min"){time = -5;}
+ else if(sch=="10min"){time = -10;}
+ else if(sch=="30min"){time = -30;}
+ else{ time = -60; } //hourly
+ //Get total snapshots
+ numToKeep = out[i].section("- total:",1,1).simplified().toInt();
+ ok=true;
+ break;
+ }
+ }
+ //qDebug() << "lpreserver cronsnap:\n" << out << QString::number(time) << QString::number(numToKeep);
+
+ return ok;
+}
+
+// ==================
+// Snapshop Management
+// ==================
+void LPBackend::newSnapshot(QString dataset, QString snapshotname){
+ //This needs to run externally - since the snapshot is simply added to the queue, and the replication
+ // afterwards may take a long time.
+ QString cmd = "lpreserver mksnap --replicate "+dataset+" "+snapshotname;
+ QProcess::startDetached(cmd);
+
+ return;
+}
+
+bool LPBackend::removeSnapshot(QString dataset, QString snapshot){
+ QString cmd = "lpreserver rmsnap "+dataset +" "+snapshot;
+ int ret = LPBackend::runCmd(cmd);
+
+ return (ret == 0);
+}
+
+bool LPBackend::revertSnapshot(QString dataset, QString snapshot){
+ QString cmd = "lpreserver revertsnap "+dataset +" "+snapshot;
+ int ret = LPBackend::runCmd(cmd);
+
+ return (ret == 0);
+}
+
+QString LPBackend::revertSnapshotFile(QString dsmountpoint, QString snapshot, QString filepath){
+ //Copy the given file from the snapshot back into the main dataset
+ // -- filepath: full path to the file in the snapshot directory
+ //qDebug() << " - Revert file:" << filepath;
+ filepath.replace("~",QDir::homePath());
+ //Check that the file path is complete and the file exists
+ if(!QFile::exists(filepath)){
+ //invalid file given
+ return "";
+ }
+ //Generate the new file path
+ QString newfilepath = filepath.replace(dsmountpoint+"/.zfs/snapshot/"+snapshot, dsmountpoint);
+ if( QFile::exists(newfilepath) ){
+ //get the file extension
+ QString filename = newfilepath.section("/",-1);
+ QString ext = filename.section(".",-1);
+ if( !ext.isEmpty() && !filename.startsWith("."+ext) && ext!=filename){
+ newfilepath.chop(ext.length()+1);
+ newfilepath.append("-reversion."+ext);
+ int i=1;
+ //append a number to the end if a reversion file already exists
+ while(QFile::exists(newfilepath)){
+ newfilepath.chop(ext.length()+1);
+ newfilepath.append(QString::number(i)+"."+ext);
+ i ++;
+ }
+ }else{
+ //File without an extension - just append a number
+ newfilepath.append("-reversion");
+ int i=1;
+ QString npath = newfilepath;
+ while(QFile::exists(npath)){
+ npath = newfilepath.append(QString::number(i));
+ i++;
+ }
+ newfilepath = npath;
+ }
+ }
+ //perform the copy
+ qDebug() << " - File Reversion:" << filepath << newfilepath;
+ bool ok = QFile::copy(filepath,newfilepath);
+ //return the path to the new file if the copy was successful
+ if(ok){
+ //reset the permissions on the reverted file to match the original
+ QFile::setPermissions(newfilepath, QFile::permissions(filepath));
+ return newfilepath;
+ }else{ return ""; }
+}
+
+// ==================
+// Replication Management
+// ==================
+bool LPBackend::setupReplication(QString dataset, QString remotehost, QString user, int port, QString remotedataset, int time){
+ QString stime = "sync"; //synchronize on snapshot creation (default)
+ if(time >= 0 && time < 24){ stime = QString::number(time); } //daily at a particular hour (24 hour notation)
+
+
+ QString cmd = "lpreserver replicate add "+remotehost+" "+user+" "+ QString::number(port)+" "+dataset+" "+remotedataset+" "+stime;
+ int ret = LPBackend::runCmd(cmd);
+
+ return (ret == 0);
+}
+
+bool LPBackend::removeReplication(QString dataset){
+ QString cmd = "lpreserver replicate remove "+dataset;
+ int ret = LPBackend::runCmd(cmd);
+
+ return (ret == 0);
+}
+
+bool LPBackend::replicationInfo(QString dataset, QString& remotehost, QString& user, int& port, QString& remotedataset, int& time){
+ QString cmd = "lpreserver replicate list";
+ QStringList out = LPBackend::getCmdOutput(cmd);
+ //Now process the output
+ bool ok = false;
+ for(int i=0; i<out.length(); i++){
+ if(out[i].contains("->") && out[i].startsWith(dataset)){
+ QString data = out[i].section("->",1,1);
+ user = data.section("@",0,0);
+ remotehost = data.section("@",1,1).section("[",0,0);
+ port = data.section("[",1,1).section("]",0,0).toInt();
+ remotedataset = data.section(":",1,1).section(" Time",0,0);
+ QString synchro = data.section("Time:",1,1).simplified();
+ if(synchro == "sync"){ time = -1; }
+ else{ time = synchro.toInt(); }
+ ok = true;
+ break;
+ }
+ }
+
+ return ok;
+}
+
+// ======================
+// SSH Key Management
+// ======================
+bool LPBackend::setupSSHKey(QString remoteHost, QString remoteUser, int remotePort){
+ QString LPPATH = "/usr/local/share/lifePreserver";
+ QString cmd = "xterm -e \""+LPPATH+"/scripts/setup-ssh-keys.sh "+remoteUser+" "+remoteHost+" "+QString::number(remotePort)+"\"";
+ int ret = LPBackend::runCmd(cmd);
+ return (ret == 0);
+}
+
+QStringList LPBackend::findValidUSBDevices(){
+ //Return format: "<mountpoint> (<device node>")
+ QString cmd = "mount";
+ QStringList out = LPBackend::getCmdOutput(cmd);
+ //Now process the output
+ QStringList list;
+ for(int i=0; i<out.length(); i++){
+ if(out[i].startsWith("/dev/da") && out[i].contains("(msdosfs,")){
+ QString mountpoint = out[i].section(" on ",1,1).section("(",0,0).simplified();
+ QString devnode = out[i].section(" on ",0,0).section("/",-1).simplified();
+ list << mountpoint +" ("+devnode+")";
+ }
+ }
+ return list;
+}
+
+bool LPBackend::copySSHKey(QString mountPath, QString localHost){
+ QString publicKey = "/root/.ssh/id_rsa";
+ //copy the file onto the designated USB stick
+ if(!mountPath.endsWith("/")){ mountPath.append("/"); }
+ QDir lDir=mountPath + "lpreserver";
+ if ( ! lDir.exists() )
+ lDir.mkdir(lDir.path());
+
+ mountPath.append("lpreserver/"+localHost+"-id_rsa");
+
+ bool ok = QFile::copy(publicKey, mountPath);
+ return ok;
+}
+
+// ======================
+// Device Management
+// ======================
+QStringList LPBackend::listDevices(){
+ //Scan the system for all valid da* and ada* devices (USB/SCSI, SATA)
+ //Return format: "<device node> (<device information>)"
+ QDir devDir("/dev");
+ QStringList devs = devDir.entryList(QStringList() << "da*"<<"ada*", QDir::System | QDir::NoSymLinks, QDir::Name);
+ QStringList camOut = LPBackend::getCmdOutput("camcontrol devlist");
+ QStringList output, flist;
+ for(int i=0; i<devs.length(); i++){
+ flist = camOut.filter("("+devs[i]+",");
+ //still need to add an additional device filter to weed out devices currently in use.
+ if(!flist.isEmpty()){ output << devs[i] + " ("+flist[0].section(">",0,0).remove("<").simplified()+")"; }
+ }
+ return output;
+}
+
+bool LPBackend::isMounted(QString device){
+ qDebug() << "Device mount check not implemented yet";
+ return false;
+}
+
+bool LPBackend::unmountDevice(QString device){
+ qDebug() << "Device unmounting not implemented yet";
+ return false;
+}
+
+// =========================
+// PRIVATE FUNCTIONS
+// =========================
+QStringList LPBackend::getCmdOutput(QString cmd){
+ QProcess *proc = new QProcess;
+ proc->setProcessChannelMode(QProcess::MergedChannels);
+ proc->start(cmd);
+ while(!proc->waitForFinished(300)){
+ QCoreApplication::processEvents();
+ }
+ QStringList out = QString(proc->readAllStandardOutput()).split("\n");
+ delete proc;
+ return out;
+}
+
+int LPBackend::runCmd(QString cmd){
+ QProcess *proc = new QProcess;
+ proc->setProcessChannelMode(QProcess::MergedChannels);
+ proc->start(cmd);
+ while(!proc->waitForFinished(300)){
+ QCoreApplication::processEvents();
+ }
+ int ret = proc->exitCode();
+ delete proc;
+ return ret;
+}
View
51 lp-gui/LPBackend.h
@@ -0,0 +1,51 @@
+#ifndef _LP_BACKEND_H
+#define _LP_BACKEND_H
+
+#include <QProcess>
+#include <QString>
+#include <QStringList>
+#include <QDebug>
+#include <QDir>
+#include <QCoreApplication>
+
+//Class of static functions for using the "lpreserver" backend
+class LPBackend{
+
+public:
+ //Informational
+ static QStringList listPossibleDatasets(); //list all possible datasets on the system
+ static QStringList listDatasets(); //list all current lifepreserver datasets
+ static QStringList listDatasetSubsets(QString dataset); //list all subsets of the main dataset
+ static QStringList listSnapshots(QString dsmountpoint); //list all snapshots for a particular dataset mountpoint
+ static QStringList listLPSnapshots(QString dataset); //list all snapshots created by life preserver
+ static QStringList listReplicationTargets(); //list all datasets with replication enabled
+ static QStringList listCurrentStatus(); //list the current snapshot/replication status
+ //Dataset Management
+ static bool setupDataset(QString dataset, int time, int numToKeep); //add or configure dataset
+ static bool removeDataset(QString dataset);
+ static bool datasetInfo(QString dataset, int& time, int& numToKeep); //get current settings for a dataset
+ //Snapshop Management
+ static void newSnapshot(QString dataset, QString snapshotname);
+ static bool removeSnapshot(QString dataset, QString snapshot);
+ static bool revertSnapshot(QString dataset, QString snapshot); //revert to given snapshot
+ static QString revertSnapshotFile(QString dataset, QString snapshot, QString filepath);
+ //Replication Management
+ static bool setupReplication(QString dataset, QString remotehost, QString user, int port, QString remotedataset, int time);
+ static bool removeReplication(QString dataset);
+ static bool replicationInfo(QString dataset, QString& remotehost, QString& user, int& port, QString& remotedataset, int& time);
+ //SSH Key Management
+ static bool setupSSHKey(QString remoteHost, QString remoteUser, int remotePort);
+ static QStringList findValidUSBDevices();
+ static bool copySSHKey(QString mountPath, QString localHost);
+ //Device Management
+ static QStringList listDevices();
+ static bool isMounted(QString device);
+ static bool unmountDevice(QString device);
+ //Mirroring Management
+
+ //General utility functions
+ static QStringList getCmdOutput(QString);
+ static int runCmd(QString);
+
+};
+#endif
View
BIN  lp-gui/LPBackend.o
Binary file not shown
View
171 lp-gui/LPConfig.cpp
@@ -0,0 +1,171 @@
+#include "LPConfig.h"
+#include "ui_LPConfig.h"
+
+LPConfig::LPConfig(QWidget *parent) : QDialog(parent), ui(new Ui::LPConfig){
+ ui->setupUi(this); //initialize the designer UI
+ qDebug() << "Initializing Configuration Dialog";
+ //initialize the output variables as necessary
+ localChanged = false;
+ remoteChanged = false;
+ //Variables that will be changed when loading the dataset properties
+
+ //now connect the buttons
+ connect(ui->tool_apply,SIGNAL(clicked()), this,SLOT(slotApplyChanges()) );
+ connect(ui->tool_cancel,SIGNAL(clicked()), this, SLOT(slotCancelConfig()) );
+}
+
+LPConfig::~LPConfig(){
+
+}
+
+void LPConfig::loadDataset(QString ds, bool replicated){
+ ui->label_dataset->setText(ds);
+ loadDatasetConfiguration(ds, replicated);
+}
+
+//==========
+// PRIVATE
+// ==========
+void LPConfig::loadDatasetConfiguration(QString dataset, bool replicated){
+ qDebug() <<" - Loading dataset configuration:" << dataset;
+ //Load the dataset values
+ isReplicated = replicated;
+ // - Local settings
+ if( !LPBackend::datasetInfo(dataset, localSchedule, localSnapshots) ){
+ localSchedule = 1; //daily at 1 AM
+ localSnapshots = 7;
+ }
+ // - Replication settings
+ bool ok=false;
+ if(isReplicated){
+ ok = LPBackend::replicationInfo(dataset, remoteHost, remoteUser, remotePort, remoteDataset, remoteFreq);
+ }
+ if(!ok){
+ isReplicated = false;
+ remotePort = 22;
+ remoteFreq = -1; //sync
+ remoteHost = "";
+ remoteUser = "";
+ remoteDataset = "";
+ }
+ //Now put the values into the UI
+ // - local settings
+ if(localSchedule == -5){ //5 minutes
+ ui->combo_local_schedule->setCurrentIndex(4);
+ }else if(localSchedule == -10){ //10 minutes
+ ui->combo_local_schedule->setCurrentIndex(3);
+ }else if(localSchedule == -30){ //30 minutes
+ ui->combo_local_schedule->setCurrentIndex(2);
+ }else if(localSchedule > 0 && localSchedule < 24){ //daily @ hour
+ ui->combo_local_schedule->setCurrentIndex(0);
+ ui->time_local_daily->setTime( QTime(localSchedule, 0) );
+ }else{ //assume hourly
+ localSchedule = -60;
+ ui->combo_local_schedule->setCurrentIndex(1);
+ }
+ setLocalKeepNumber();
+
+ // - Replication settings
+ ui->groupReplicate->setChecked(isReplicated);
+ ui->lineHostName->setText(remoteHost);
+ ui->lineUserName->setText(remoteUser);
+ ui->lineRemoteDataset->setText(remoteDataset);
+ ui->spinPort->setValue(remotePort);
+ if(remoteFreq >=0 && remoteFreq < 24){
+ ui->radioRepTime->setChecked(true);
+ ui->time_replicate->setTime( QTime(remoteFreq,0) );
+ }else{
+ ui->radioSYNC->setChecked(true);
+ }
+
+}
+
+void LPConfig::checkForChanges(){
+ //Checks for changes to the settings while also updating the output variables to match the GUI
+
+ localChanged = false;
+ remoteChanged = false;
+ //Local Settings
+ int nSchedule;
+ int schint = ui->combo_local_schedule->currentIndex();
+ if(schint == 0){ nSchedule = ui->time_local_daily->time().hour(); } //daily @ hour
+ else if(schint == 1){ nSchedule = -60; } //hourly
+ else if(schint == 2){ nSchedule = -30; } //30 min
+ else if(schint == 3){ nSchedule = -10; } //10 min
+ else{ nSchedule = -5; } //5 min
+ int nTotSnaps;
+ if( ui->combo_local_keepunits->currentIndex() == 0 && (schint != 0) ){ //days
+ int numperday = 1440/(-nSchedule);
+ nTotSnaps = numperday * ui->spin_local_numkeep->value();
+ }else{ //total number (or daily snapshots)
+ nTotSnaps = ui->spin_local_numkeep->value();
+ }
+ if(nSchedule != localSchedule){localChanged = true; localSchedule = nSchedule; }
+ if(nTotSnaps != localSnapshots){ localChanged = true; localSnapshots = nTotSnaps; }
+
+ //Replication Settings
+ bool updateSSHKey = false;
+ if(isReplicated != ui->groupReplicate->isChecked()){
+ remoteChanged = true;
+ isReplicated = ui->groupReplicate->isChecked();
+ if(isReplicated){ updateSSHKey = true; }
+ }
+ QString tmp = ui->lineHostName->text().simplified();
+ if( tmp != remoteHost ){ remoteChanged = true; remoteHost = tmp; updateSSHKey=true;}
+ tmp = ui->lineUserName->text().simplified();
+ if( tmp != remoteUser ){ remoteChanged = true; remoteUser = tmp; updateSSHKey=true;}
+ tmp = ui->lineRemoteDataset->text().simplified();
+ if( tmp != remoteDataset ){ remoteChanged = true; remoteDataset = tmp; }
+ if( ui->spinPort->value() != remotePort){ remoteChanged = true; remotePort = ui->spinPort->value(); updateSSHKey=true;}
+ int nFreq = -1;
+ if(ui->radioRepTime->isChecked()){
+ nFreq = ui->time_replicate->time().hour();
+ }
+ if(nFreq < 0){
+ if( remoteFreq >= 0 && remoteFreq < 24){remoteChanged = true; remoteFreq = nFreq;}
+ }else{
+ if( nFreq != remoteFreq ){ remoteChanged = true; remoteFreq = nFreq; }
+ }
+ if(updateSSHKey){
+ //Prompt for the SSH key generation
+ LPBackend::setupSSHKey(remoteHost, remoteUser, remotePort);
+ }
+}
+
+void LPConfig::setLocalKeepNumber(){
+ if(localSchedule >=0){
+ ui->combo_local_keepunits->setCurrentIndex(0); //num days
+ ui->spin_local_numkeep->setValue(localSnapshots);
+ }else{
+ int numperday = 1440/(-localSchedule);
+ if( localSnapshots % numperday == 0 ){
+ //daily
+ ui->combo_local_keepunits->setCurrentIndex(0); //num days
+ ui->spin_local_numkeep->setValue(localSnapshots/numperday);
+ }else{
+ //odd number of snapshots - must be total
+ ui->combo_local_keepunits->setCurrentIndex(1); //num total
+ ui->spin_local_numkeep->setValue(localSnapshots);
+ }
+ }
+}
+
+// =============
+// PRIVATE SLOTS
+// =============
+void LPConfig::slotApplyChanges(){
+ checkForChanges();
+ this->close();
+}
+
+void LPConfig::slotCancelConfig(){
+ //Make sure it is flagged as unchanged before closing
+ localChanged = false;
+ remoteChanged = false;
+ this->close();
+}
+
+void LPConfig::on_combo_local_schedule_currentIndexChanged(int index){
+ //Adjust whether the daily time box is visible
+ ui->time_local_daily->setVisible( (index == 0) );
+}
View
39 lp-gui/LPConfig.h
@@ -0,0 +1,39 @@
+#ifndef _LP_CONFIG_H
+#define _LP_CONFIG_H
+
+#include <QDialog>
+#include <QString>
+#include <QDebug>
+
+#include "LPBackend.h"
+
+namespace Ui{
+ class LPConfig;
+}
+
+class LPConfig : public QDialog{
+ Q_OBJECT
+public:
+ LPConfig(QWidget* parent = 0);
+ ~LPConfig();
+
+ void loadDataset(QString, bool);
+
+ //Output variables
+ bool localChanged, remoteChanged, isReplicated;
+ int localSchedule, localSnapshots, remotePort, remoteFreq;
+ QString remoteHost, remoteUser, remoteDataset;
+
+private:
+ Ui::LPConfig *ui;
+ void loadDatasetConfiguration(QString, bool);
+ void checkForChanges();
+ void setLocalKeepNumber();
+
+private slots:
+ void slotApplyChanges();
+ void slotCancelConfig();
+ void on_combo_local_schedule_currentIndexChanged(int);
+};
+
+#endif
View
BIN  lp-gui/LPConfig.o
Binary file not shown
View
397 lp-gui/LPConfig.ui
@@ -0,0 +1,397 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LPConfig</class>
+ <widget class="QDialog" name="LPConfig">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>374</width>
+ <height>327</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Dataset</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Data Set:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_dataset">
+ <property name="text">
+ <string notr="true">sample</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab_local">
+ <attribute name="title">
+ <string>Local Snapshots</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Schedule:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QComboBox" name="combo_local_schedule">
+ <item>
+ <property name="text">
+ <string>Daily</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Hourly</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>30 minutes</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>10 minutes</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>5 minutes</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTimeEdit" name="time_local_daily">
+ <property name="maximumTime">
+ <time>
+ <hour>23</hour>
+ <minute>0</minute>
+ <second>0</second>
+ </time>
+ </property>
+ <property name="minimumTime">
+ <time>
+ <hour>1</hour>
+ <minute>0</minute>
+ <second>0</second>
+ </time>
+ </property>
+ <property name="displayFormat">
+ <string>@ h AP</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QSpinBox" name="spin_local_numkeep">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>800</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="combo_local_keepunits">
+ <item>
+ <property name="text">
+ <string>Days</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Total</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Keep:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_remote">
+ <attribute name="title">
+ <string>Replication</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="groupReplicate">
+ <property name="title">
+ <string>Replicate on a Remote System</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <item>
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Host Name</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="lineHostName"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <item>
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>User Name</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="lineUserName"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>SSH Port</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="spinPort">
+ <property name="maximum">
+ <number>999999</number>
+ </property>
+ <property name="value">
+ <number>22</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Remote Dataset</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="lineRemoteDataset"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Frequency</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QRadioButton" name="radioSYNC">
+ <property name="text">
+ <string>With snapshot creation (Best for daily snapshots)</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <widget class="QRadioButton" name="radioRepTime">
+ <property name="text">
+ <string>Daily at:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTimeEdit" name="time_replicate">
+ <property name="displayFormat">
+ <string>h AP</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_9">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="tool_apply">
+ <property name="text">
+ <string>Apply</string>
+ </property>
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/backup-ok.png</normaloff>:/images/backup-ok.png</iconset>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextBesideIcon</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="tool_cancel">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/application-exit.png</normaloff>:/images/application-exit.png</iconset>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextBesideIcon</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="lPreserve.qrc"/>
+ </resources>
+ <connections/>
+</ui>
View
32 lp-gui/LPContainers.h
@@ -0,0 +1,32 @@
+#ifndef _LP_CONTAINERS_H
+#define _LP_CONTAINERS_H
+
+#include <QHash>
+#include <QStringList>
+#include <QString>
+
+
+class LPDataset{
+public:
+ LPDataset(){}
+ ~LPDataset(){}
+
+ //Information needed on each dataset
+ QString replicationStatus, mirrorStatus, errorStatus;
+ QString latestSnapshot;
+ QString numberOfDisks;
+ QString poolStatus;
+ QHash<QString,QStringList> subsetHash; //<subset, snapshot list> (complete dataset name should be <ds><subset>)
+
+ //Simplification functions for getting info from the hash
+ QStringList subsets(){ return QStringList(subsetHash.keys()); }
+ QStringList snapshots(QString subset){
+ if(subsetHash.contains(subset)){
+ return subsetHash[subset];
+ }else{
+ return QStringList();
+ }
+ }
+};
+
+#endif
View
68 lp-gui/LPGUtils.cpp
@@ -0,0 +1,68 @@
+#include "LPGUtils.h"
+
+LPDataset LPGUtils::loadPoolData(QString zpool){
+ //Load the current information for the given zpool
+ qDebug() << "New Dataset: " << zpool;
+ LPDataset DSC;
+ //List all the mountpoints in this dataset
+ QStringList subsets = LPBackend::listDatasetSubsets(zpool);
+ QStringList lpsnaps = LPBackend::listLPSnapshots(zpool);
+ //populate the list of snapshots available for each mountpoint
+ for(int i=0; i<subsets.length(); i++){
+ //qDebug() << "Subset:" << subsets[i];
+ QStringList snaps = LPBackend::listSnapshots(subsets[i]);
+ //qDebug() << " - Snapshots:" << snaps;
+ if(snaps.isEmpty()){
+ //invalid subset - remove it from the list
+ subsets.removeAt(i);
+ i--;
+ }else{
+ QStringList subsnaps;
+ //only list the valid snapshots that life preserver created
+ for(int s=0; s<lpsnaps.length(); s++){
+ int index = snaps.indexOf(lpsnaps[s]);
+ if(index > -1){ subsnaps << lpsnaps[s]; snaps.removeAt(index); }
+ }
+ //Now list all the other available snapshots (no certain ordering)
+ if(!snaps.isEmpty()){
+ subsnaps << "--"; //so we know that this is a divider between the sections
+ subsnaps << snaps;
+ }
+ DSC.subsetHash.insert(subsets[i],subsnaps); //add it to the internal container hash
+ }
+ }
+ //Get the time for the latest life-preserver snapshot (and total number)
+ /*
+ //Find the index for the current list
+ int ci = 0;
+ while(ci < CLIST.length()){
+ if(CLIST[ci].startsWith(zpool+":::")){ break; }
+ else{ ci++; }
+ }
+ if(CLIST.isEmpty()){ ci = -1; } //catch for empty list
+ if(DSC.subsetHash.size() < 1){
+ DSC.numberOfSnapshots = "0";
+ DSC.latestSnapshot= "";
+ }else{
+ DSC.numberOfSnapshots = QString::number(lpsnaps.length());
+ if(lpsnaps.isEmpty()){ DSC.latestSnapshot=""; }
+ else if(ci > -1 && ci < CLIST.length()){
+ QString sna = CLIST[ci].section(":::",1,1);
+ if(sna != "-"){ DSC.latestSnapshot= sna; }
+ else{ DSC.latestSnapshot = ""; }
+ }else{ DSC.latestSnapshot=lpsnaps[0]; }
+ }
+ //List the replication status
+ if(RLIST.contains(zpool) && (ci > -1)){
+ QString rep = CLIST[ci].section(":::",2,2);
+ if(rep != "-"){ DSC.latestReplication = rep; }
+ else{ DSC.latestReplication= QObject::tr("Enabled"); }
+ }else{
+ DSC.latestReplication= QObject::tr("Disabled");
+ }
+ */
+ //Now parse the "zpool status <zpool>" output
+
+ //Return the dataset
+ return DSC;
+}
View
18 lp-gui/LPGUtils.h
@@ -0,0 +1,18 @@
+#ifndef _LP_GUTILS_H
+#define _LP_GUTILS_H
+
+#include <QMessageBox>
+#include <QDialog>
+#include <QStringList>
+#include <QString>
+
+#include "LPBackend.h"
+#include "LPContainers.h"
+
+class LPGUtils{
+public:
+ static LPDataset loadPoolData(QString zpool); //Load backend data into container
+
+};
+
+#endif
View
BIN  lp-gui/LPGUtils.o
Binary file not shown
View
179 lp-gui/LPMain.cpp
@@ -0,0 +1,179 @@
+#include "LPMain.h"
+#include "ui_LPMain.h"
+
+LPMain::LPMain(QWidget *parent) : QMainWindow(parent), ui(new Ui::LPMain){
+ ui->setupUi(this); //load the Qt-designer UI file
+ //Create the basic/advanced view options
+ viewBasic = new QRadioButton(tr("Basic"), ui->menuView);
+ QWidgetAction *WABasic = new QWidgetAction(this); WABasic->setDefaultWidget(viewBasic);
+ viewAdvanced = new QRadioButton(tr("Advanced"), ui->menuView);
+ QWidgetAction *WAAdv = new QWidgetAction(this); WAAdv->setDefaultWidget(viewAdvanced);
+
+ ui->menuView->addAction(WABasic);
+ ui->menuView->addAction(WAAdv);
+ connect(viewBasic, SIGNAL(toggled(bool)), this, SLOT(viewChanged()) );
+ //Now set the default view type
+ viewBasic->setChecked(true); //will automatically call the "viewChanged" function
+
+ //Connect the UI to all the functions
+ connect(ui->combo_pools, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTabs()) );
+ connect(ui->combo_datasets, SIGNAL(currentIndexChanged(int)), this, SLOT(updateDataset()) );
+ connect(ui->slider_snapshots, SIGNAL(valueChanged(int)), this, SLOT(updateSnapshot()) );
+ //Update the interface
+ updatePoolList();
+ //Make sure the status tab is shown initially
+ ui->tabWidget->setCurrentWidget(ui->tab_status);
+}
+
+LPMain::~LPMain(){
+
+}
+
+// ==============
+// PUBLIC SLOTS
+// ==============
+void LPMain::slotSingleInstance(){
+ this->raise();
+ this->show();
+}
+
+// ==============
+// PRIVATE
+// ==============
+
+// ==============
+// PRIVATE SLOTS
+// ==============
+void LPMain::updatePoolList(){
+ //Get the currently selected pool (if there is one)
+ QString cPool;
+ if(ui->combo_pools->currentIndex() != -1){ cPool = ui->combo_pools->currentText(); }
+ //Get the list of managed pools
+ QStringList pools = LPBackend::listDatasets();
+ //Now put that list into the UI
+ ui->combo_pools->clear();
+ if(!pools.isEmpty()){ ui->combo_pools->addItems(pools); }
+ //Now set the currently selected pools
+ if(pools.length() > 0){
+ int index = pools.indexOf(cPool);
+ if(index < 0){ ui->combo_pools->setCurrentIndex(0); }
+ else{ ui->combo_pools->setCurrentIndex(index); }
+ poolSelected=true;
+ }else{
+ //No managed pools
+ ui->combo_pools->addItem("No Managed Pools!");
+ ui->combo_pools->setCurrentIndex(0);
+ poolSelected=false;
+ }
+ //Now update the interface appropriately
+ ui->combo_pools->setEnabled(poolSelected);
+ updateTabs();
+}
+
+void LPMain::viewChanged(){
+ ui->menubar->clear();
+ if(viewBasic->isChecked()){
+ ui->menubar->addMenu(ui->menuFile);
+ ui->menubar->addMenu(ui->menuView);
+ }else{
+ ui->menubar->addMenu(ui->menuFile);
+ ui->menubar->addMenu(ui->menuView);
+ ui->menubar->addMenu(ui->menuDisks);
+ ui->menubar->addMenu(ui->menuSnapshots);
+ }
+}
+
+void LPMain::updateTabs(){
+ viewChanged();
+ ui->tabWidget->setEnabled(poolSelected);
+ ui->menuView->setEnabled(poolSelected);
+ ui->tool_configure->setVisible(poolSelected);
+ ui->tool_configBackups->setVisible(poolSelected);
+ ui->actionUnmanage_Pool->setEnabled(poolSelected);
+ ui->action_SaveKeyToUSB->setEnabled(poolSelected);
+ if(poolSelected){
+ POOLDATA = LPGUtils::loadPoolData(ui->combo_pools->currentText());
+ //Now list the status information
+ ui->label_status->setText(POOLDATA.poolStatus);
+ ui->label_numdisks->setText(POOLDATA.numberOfDisks);
+ ui->label_latestsnapshot->setText(POOLDATA.latestSnapshot);
+ if(POOLDATA.replicationStatus.isEmpty()){ ui->label_replication->setVisible(false); }
+ else{
+ ui->label_replication->setText(POOLDATA.replicationStatus);
+ ui->label_replication->setVisible(true);
+ }
+ if(POOLDATA.mirrorStatus.isEmpty()){ ui->label_mirror->setVisible(false); }
+ else{
+ ui->label_mirror->setText(POOLDATA.mirrorStatus);
+ ui->label_mirror->setVisible(true);
+ }
+ if(POOLDATA.errorStatus.isEmpty()){ ui->label_errors->setVisible(false); }
+ else{
+ ui->label_errors->setText(POOLDATA.errorStatus);
+ ui->label_errors->setVisible(true);
+ }
+ //Now list the data restore options
+ QString cds = ui->combo_datasets->currentText();
+ ui->combo_datasets->clear();
+ QStringList dslist = POOLDATA.subsets();
+ qDebug() << "Datasets:" << dslist;
+ ui->combo_datasets->addItems(dslist);
+ int dsin = dslist.indexOf(cds);
+ if(dsin >= 0){ ui->combo_datasets->setCurrentIndex(dsin); }
+ else if( !dslist.isEmpty() ){ ui->combo_datasets->setCurrentIndex(0); }
+ else{ ui->combo_datasets->addItem(tr("No datasets available")); }
+ //Automatically calls the "updateDataset()" function
+ }else{
+ //No Pool selected
+ ui->menuDisks->setEnabled(false); //make sure this is always invisible if nothing selected
+ ui->menuSnapshots->setEnabled(false); //make sure this is always invisible if nothing selected
+ ui->label_numdisks->clear();
+ ui->label_latestsnapshot->clear();
+ ui->label_status->clear();
+ ui->label_errors->setVisible(false);
+ ui->label_mirror->setVisible(false);
+ ui->label_replication->setVisible(false);
+ }
+
+}
+
+void LPMain::updateDataset(){
+ //Update the snapshots for the currently selected dataset
+ QString cds = ui->combo_datasets->currentText();
+ if(POOLDATA.subsets().indexOf(cds) >= 0){
+ QStringList snaps = POOLDATA.snapshots(cds);
+ qDebug() << "dataset:" << cds << "snapshots:" << snaps;
+ ui->slider_snapshots->setEnabled(true);
+ ui->slider_snapshots->setMinimum(0);
+ int max = snaps.length() -1;
+ if(max < 0){ max = 0; ui->slider_snapshots->setEnabled(false); }
+ ui->slider_snapshots->setMaximum(max);
+ ui->slider_snapshots->setValue(max); //most recent snapshot
+ updateSnapshot();
+ }else{
+ ui->slider_snapshots->setEnabled(false);
+ ui->label_snapshot->clear();
+ ui->tool_nextsnap->setEnabled(false);
+ ui->tool_prevsnap->setEnabled(false);
+ }
+
+}
+
+void LPMain::updateSnapshot(){
+ int sval = ui->slider_snapshots->value();
+ QStringList snaps = POOLDATA.snapshots(ui->combo_datasets->currentText());
+ qDebug() << "Snapshots:" << snaps;
+ //Update the previous/next buttons
+ if(sval == ui->slider_snapshots->minimum() ){ ui->tool_prevsnap->setEnabled(false); }
+ else{ ui->tool_prevsnap->setEnabled(true); }
+ if(sval == ui->slider_snapshots->maximum() ){ ui->tool_nextsnap->setEnabled(false); }
+ else{ ui->tool_nextsnap->setEnabled(true); }
+ //Now update the snapshot viewer
+ if(snaps.isEmpty()){ ui->label_snapshot->clear(); ui->slider_snapshots->setEnabled(false); }
+ else{
+ QString snap = snaps.at(sval);
+ ui->label_snapshot->setText(snap);
+ //Now update the snapshot view
+ qDebug() << "Snapshot viewer not implemented yet";
+ }
+}
View
47 lp-gui/LPMain.h
@@ -0,0 +1,47 @@
+#ifndef _LP_MAIN_H
+#define _LP_MAIN_H
+
+#include <QMainWindow>
+#include <QComboBox>
+#include <QToolButton>
+#include <QLabel>
+#include <QString>
+#include <QStringList>
+#include <QRadioButton>
+#include <QWidgetAction>
+
+#include "LPBackend.h"
+#include "LPContainers.h"
+#include "LPGUtils.h"
+
+namespace Ui{
+ class LPMain;
+};
+
+class LPMain : public QMainWindow{
+ Q_OBJECT
+public:
+ LPMain(QWidget *parent = 0);
+ ~LPMain();
+
+public slots:
+ void slotSingleInstance();
+
+private:
+ Ui::LPMain *ui;
+ QRadioButton *viewBasic, *viewAdvanced;
+ bool poolSelected;
+ LPDataset POOLDATA;
+
+private slots:
+ void updatePoolList(); //re-load available pools
+ void viewChanged();
+ void updateTabs(); //load current pool info and update tabs
+ void updateDataset(); //restore dataset changed
+ void updateSnapshot(); //selected snapshot changed
+
+protected:
+
+};
+
+#endif
View
BIN  lp-gui/LPMain.o
Binary file not shown
View
593 lp-gui/LPMain.ui
@@ -0,0 +1,593 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LPMain</class>
+ <widget class="QMainWindow" name="LPMain">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>377</width>
+ <height>257</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Life Preserver</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/tray-icon-idle.png</normaloff>:/images/tray-icon-idle.png</iconset>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QComboBox" name="combo_pools">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="tool_configure">
+ <property name="statusTip">
+ <string>Configure the local snapshot policies</string>
+ </property>
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/configure.png</normaloff>:/images/configure.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="tool_configBackups">
+ <property name="statusTip">
+ <string>Configure additional data safety procedures</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/drive-removable-media.png</normaloff>:/images/drive-removable-media.png</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>1</number>
+ </property>
+ <widget class="QWidget" name="tab_status">
+ <attribute name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/drive-removable-media.png</normaloff>:/images/drive-removable-media.png</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Status</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>System State:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_status">
+ <property name="text">
+ <string notr="true">GOOD</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Number of Disks:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_numdisks">
+ <property name="text">
+ <string notr="true">1</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Latest Snapshot:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_latestsnapshot">
+ <property name="text">
+ <string notr="true">snapshot</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_replication">
+ <property name="statusTip">
+ <string/>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">background-color: rgba(10,200,10,100); border-radius: 5px</string>
+ </property>
+ <property name="text">
+ <string notr="true">Replication Status</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_mirror">
+ <property name="styleSheet">
+ <string notr="true">background-color: rgba(10,100,200,100); border-radius: 5px</string>
+ </property>
+ <property name="text">
+ <string notr="true">Mirror Status</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_errors">
+ <property name="styleSheet">
+ <string notr="true">background-color: rgba(230,10,10,100); border-radius: 5px</string>
+ </property>
+ <property name="text">
+ <string notr="true">System Errors</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_restore">
+ <attribute name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/copy.png</normaloff>:/images/copy.png</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Restore Data</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QComboBox" name="combo_datasets"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QToolButton" name="tool_prevsnap">
+ <property name="statusTip">
+ <string>Select the previous (older) snapshot</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string>Prev</string>
+ </property>
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/previous.png</normaloff>:/images/previous.png</iconset>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QSlider" name="slider_snapshots">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::TicksAbove</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_snapshot">
+ <property name="styleSheet">
+ <string notr="true">border: 1px solid grey; border-radius: 5px</string>
+ </property>
+ <property name="text">
+ <string notr="true">Selected Snapshot</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QToolButton" name="tool_nextsnap">
+ <property name="statusTip">
+ <string>Select the next (more recent) snapshot</string>
+ </property>
+ <property name="text">
+ <string>Next</string>
+ </property>
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/next.png</normaloff>:/images/next.png</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="Line" name="line_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QToolButton" name="tool_viewSnapshot">
+ <property name="statusTip">
+ <string>Browse through the currently selected snapshot</string>
+ </property>
+ <property name="text">
+ <string>View Snapshot</string>
+ </property>
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/view.png</normaloff>:/images/view.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="tool_restoreDir">
+ <property name="statusTip">
+ <string>Revert the entire directory to the selected snapshot</string>
+ </property>
+ <property name="text">
+ <string>Restore Dir</string>
+ </property>
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/folder.png</normaloff>:/images/folder.png</iconset>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextBesideIcon</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="tool_restoreFile">
+ <property name="statusTip">
+ <string>Retrieve file(s) from the selected snapshot</string>
+ </property>
+ <property name="text">
+ <string>Restore File</string>
+ </property>
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/copy.png</normaloff>:/images/copy.png</iconset>
+ </property>
+ <property name="toolButtonStyle">
+ <enum>Qt::ToolButtonTextBesideIcon</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ <zorder></zorder>
+ <zorder></zorder>
+ <zorder>verticalSpacer_2</zorder>
+ <zorder>line_2</zorder>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>377</width>
+ <height>20</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuFile">
+ <property name="title">
+ <string>File</string>
+ </property>
+ <addaction name="actionManage_Pool"/>
+ <addaction name="actionUnmanage_Pool"/>
+ <addaction name="separator"/>
+ <addaction name="action_SaveKeyToUSB"/>
+ <addaction name="separator"/>
+ <addaction name="actionClose_WIndow"/>
+ </widget>
+ <widget class="QMenu" name="menuView">
+ <property name="title">
+ <string>View</string>
+ </property>
+ </widget>
+ <widget class="QMenu" name="menuDisks">
+ <property name="title">
+ <string>Disks</string>
+ </property>
+ <addaction name="actionAdd_Disk"/>
+ <addaction name="actionRemove_Disk"/>
+ <addaction name="separator"/>
+ <addaction name="actionSet_Disk_Offline"/>
+ <addaction name="separator"/>
+ <addaction name="action_startScrub"/>
+ </widget>
+ <widget class="QMenu" name="menuSnapshots">
+ <property name="title">
+ <string>Snapshots</string>
+ </property>
+ <addaction name="action_newSnapshot"/>
+ <addaction name="action_rmSnapshot"/>
+ </widget>
+ <addaction name="menuFile"/>
+ <addaction name="menuView"/>
+ <addaction name="menuDisks"/>
+ <addaction name="menuSnapshots"/>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ <action name="actionClose_WIndow">
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/application-exit.png</normaloff>:/images/application-exit.png</iconset>
+ </property>
+ <property name="text">
+ <string>Close Window</string>
+ </property>
+ </action>
+ <action name="actionAdvanced">
+ <property name="text">
+ <string>Advanced</string>
+ </property>
+ </action>
+ <action name="actionBasic">
+ <property name="text">
+ <string>Basic</string>
+ </property>
+ </action>
+ <action name="actionManage_Pool">
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/list-add.png</normaloff>:/images/list-add.png</iconset>
+ </property>
+ <property name="text">
+ <string>Manage Pool</string>
+ </property>
+ </action>
+ <action name="actionUnmanage_Pool">
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/list-remove.png</normaloff>:/images/list-remove.png</iconset>
+ </property>
+ <property name="text">
+ <string>Unmanage Pool</string>
+ </property>
+ </action>
+ <action name="actionRecover_File">
+ <property name="text">
+ <string>Recover File</string>
+ </property>
+ </action>
+ <action name="actionRestore_Dataset">
+ <property name="text">
+ <string>Restore Dataset</string>
+ </property>
+ </action>
+ <action name="actionSave_Key_to_USB">
+ <property name="text">
+ <string>Save Key to USB</string>
+ </property>
+ </action>
+ <action name="actionAdd_Disk">
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/list-add.png</normaloff>:/images/list-add.png</iconset>
+ </property>
+ <property name="text">
+ <string>Add Disk</string>
+ </property>
+ </action>
+ <action name="actionRemove_Disk">
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/list-remove.png</normaloff>:/images/list-remove.png</iconset>
+ </property>
+ <property name="text">
+ <string>Remove Disk</string>
+ </property>
+ </action>
+ <action name="actionSet_Disk_Offline">
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/network-wired.png</normaloff>:/images/network-wired.png</iconset>
+ </property>
+ <property name="text">
+ <string>Set Disk Offline</string>
+ </property>
+ </action>
+ <action name="actionNew_Snapshot">
+ <property name="text">
+ <string>New Snapshot</string>
+ </property>
+ </action>
+ <action name="action_newSnapshot">
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/camera_add.png</normaloff>:/images/camera_add.png</iconset>
+ </property>
+ <property name="text">
+ <string>New Snapshot</string>
+ </property>
+ </action>
+ <action name="action_rmSnapshot">
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/list-remove.png</normaloff>:/images/list-remove.png</iconset>
+ </property>
+ <property name="text">
+ <string>Delete Snapshot</string>
+ </property>
+ </action>
+ <action name="action_SaveKeyToUSB">
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/key.png</normaloff>:/images/key.png</iconset>
+ </property>
+ <property name="text">
+ <string>Save Key to USB</string>
+ </property>
+ </action>
+ <action name="action_startScrub">
+ <property name="icon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/system-run.png</normaloff>:/images/system-run.png</iconset>
+ </property>
+ <property name="text">
+ <string>Start Scrub</string>
+ </property>
+ <property name="statusTip">
+ <string>Check system data integrity</string>
+ </property>
+ </action>
+ </widget>
+ <resources>
+ <include location="lPreserve.qrc"/>
+ </resources>
+ <connections/>
+</ui>
View
58 lp-gui/LPWizard.cpp
@@ -0,0 +1,58 @@
+#include "LPWizard.h"
+#include "ui_LPWizard.h"
+
+LPWizard::LPWizard(QWidget *parent) : QWizard(parent), ui(new Ui::LPWizard){
+ //Initialize the graphical items
+ ui->setupUi(this); //load the mainUI.ui file
+ cancelled = true; //Make sure this is always set by default
+ connect(this,SIGNAL(accepted()), this,SLOT(slotFinished()) );
+ connect(this,SIGNAL(rejected()),this,SLOT(slotCancelled()) );
+}
+
+LPWizard::~LPWizard(){
+
+}
+
+void LPWizard::setDataset(QString ds){
+ ui->label_dataset->setText(ds);
+}
+
+void LPWizard::slotFinished(){
+ qDebug() << "Wizard Finished";
+ cancelled = false; //use the values from the UI
+ //Now load the information from the UI
+ enableReplication = ui->groupReplicate->isChecked();
+ if(enableReplication){
+ remotePort = ui->spinPort->value();
+ if(ui->radioSYNC->isChecked()){
+ remoteTime = -1;
+ }else{
+ remoteTime = ui->time_replicate->time().hour();
+ }
+ remoteHost = ui->lineHostName->text();
+ remoteUser = ui->lineUserName->text();
+ remoteDataset = ui->lineRemoteDataset->text();
+ //Prompt for the SSH key generation
+ LPBackend::setupSSHKey(remoteHost, remoteUser, remotePort);
+ }
+ if(ui->radioDaily->isChecked()){ localTime = ui->timeEdit->time().hour(); }
+ else if( ui->radio10Min->isChecked()){ localTime = -10; }
+ else if( ui->radio30Min->isChecked()){ localTime = -30; }
+ else if( ui->radio5Min->isChecked()){ localTime = -5; }
+ else{ localTime = -60; } //hourly
+ if( ui->radioKeepTotal->isChecked() ){
+ totalSnapshots = ui->spin_keepTotal->value();
+ }else{
+ if(localTime >0){ totalSnapshots = ui->spin_keepDays->value(); }
+ else{ totalSnapshots = ui->spin_keepDays->value() * (1440/(-localTime)); } //convert to number of snapshots a day
+ }
+
+ //Now close the UI
+ this->close();
+}
+
+void LPWizard::slotCancelled(){
+ qDebug() << "Wizard Cancelled";
+ cancelled = true; //just to make sure
+ this->close();
+}
View
35 lp-gui/LPWizard.h
@@ -0,0 +1,35 @@
+#ifndef _LP_WIZARD_H
+#define _LP_WIZARD_H
+
+#include <QWizard>
+#include <QString>
+#include <QDebug>
+
+#include "LPBackend.h"
+
+namespace Ui{
+ class LPWizard;
+}
+
+class LPWizard : public QWizard{
+ Q_OBJECT
+public:
+ explicit LPWizard(QWidget *parent = 0);
+ ~LPWizard();
+
+ //Input dataset
+ void setDataset(QString);
+ //Output variables
+ bool cancelled, enableReplication;
+ int localTime, totalSnapshots, remotePort, remoteTime;
+ QString remoteHost, remoteUser, remoteDataset;
+
+private:
+ Ui::LPWizard *ui;
+
+private slots:
+ void slotFinished();
+ void slotCancelled();
+};
+
+#endif
View
BIN  lp-gui/LPWizard.o
Binary file not shown
View
627 lp-gui/LPWizard.ui
@@ -0,0 +1,627 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LPWizard</class>
+ <widget class="QWizard" name="LPWizard">
+ <property name="windowModality">
+ <enum>Qt::ApplicationModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>474</width>
+ <height>381</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>New Life Preserver</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="lPreserve.qrc">
+ <normaloff>:/images/lifepreserver.png</normaloff>:/images/lifepreserver.png</iconset>
+ </property>
+ <property name="options">
+ <set>QWizard::IndependentPages</set>
+ </property>
+ <widget class="QWizardPage" name="wizardPage1">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>This wizard will walk you through the process of setting up a new snapshot schedule for the following ZFS dataset:</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Minimum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_dataset">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string notr="true">SAMPLE</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWizardPage" name="wizardPage">
+ <property name="title">
+ <string/>
+ </property>
+ <property name="subTitle">
+ <string/>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_8">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="title">
+ <string>Snapshot schedule</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Snapshots can be scheduled anywhere from daily, down to every 5 minutes. Snapshots consume very little disk space, and will only grow as the current data on disk changes. </string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="1" column="0">
+ <