source: src-qt4/life-preserver/LPBackend.cpp @ 2ac57ce

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since 2ac57ce was 2ac57ce, checked in by Ken Moore <ken@…>, 16 months ago

Setup the life preserver backend to use QProcesses for all commands, and process window events while commands are running.

  • Property mode set to 100644
File size: 10.8 KB
Line 
1#include "LPBackend.h"
2
3// ==============
4//     Informational
5// ==============
6QStringList LPBackend::listPossibleDatasets(){
7  QString cmd = "zpool list -H -o name";
8  QStringList out = LPBackend::getCmdOutput(cmd);
9  //Now process the output (one dataset per line - no headers)
10  QStringList list;
11  for(int i=0; i<out.length(); i++){
12    QString ds = out[i].section("/",0,0).simplified();
13    if(!ds.isEmpty()){ list << ds; }
14  }
15  list.removeDuplicates();
16   
17  return list; 
18}
19
20QStringList LPBackend::listDatasets(){
21  QString cmd = "lpreserver listcron";
22  QStringList out = LPBackend::getCmdOutput(cmd);
23  //Now process the output
24  QStringList list;
25  for(int i=2; i<out.length(); i++){ //skip the first two lines (headers)
26    QString ds = out[i].section(" - ",0,0).simplified();
27    if(!ds.isEmpty()){ list << ds; }
28  }
29   
30  return list;
31}
32
33QStringList LPBackend::listDatasetSubsets(QString dataset){
34  QString cmd = "zfs list -H -t filesystem -o name,mountpoint,mounted";
35  QStringList out = LPBackend::getCmdOutput(cmd);
36  //Now process the output (one dataset per line - no headers)
37  QStringList list;
38  for(int i=0; i<out.length(); i++){
39    if(out[i].startsWith(dataset+"/")){
40      if(out[i].section("\t",2,2,QString::SectionSkipEmpty).simplified() == "yes"){
41        QString ds = out[i].section("\t",1,1).simplified(); //save the mountpoint
42        if(!ds.isEmpty()){ list << ds; }
43      }
44    }
45  }
46  list.removeDuplicates();     
47   
48  return list;
49}
50
51QStringList LPBackend::listSnapshots(QString dsmountpoint){
52  //List all the snapshots available for the given dataset mountpoint
53  QDir dir(dsmountpoint+"/.zfs/snapshot");
54  QStringList list;
55  if(dir.exists()){
56    list = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time | QDir::Reversed);
57  }
58  return list;
59}
60
61QStringList LPBackend::listLPSnapshots(QString dataset){
62  QString cmd = "lpreserver listsnap "+dataset;
63  QStringList out = LPBackend::getCmdOutput(cmd);
64  //Now process the output
65  QStringList list;
66  for(int i=out.length()-1; i>=0; i--){ //go in reverse order for proper time format (newest first)
67    if(out[i].startsWith(dataset+"@")){
68      QString snap = out[i].section("@",1,3).section(" ",0,0).simplified();;
69      if(!snap.isEmpty()){ list << snap; }
70    }
71  }
72   
73  return list; 
74}
75
76QStringList LPBackend::listReplicationTargets(){
77  QString cmd = "lpreserver replicate list";
78  QStringList out = LPBackend::getCmdOutput(cmd);
79  //Now process the output
80  QStringList list;
81  for(int i=0; i<out.length(); i++){
82    if(out[i].contains("->")){
83      QString ds = out[i].section("->",0,0).simplified();
84      if(!ds.isEmpty()){ list << ds; }
85    }
86  }
87   
88  return list;         
89}
90
91QStringList LPBackend::listCurrentStatus(){
92  QString cmd = "lpreserver status";
93  QStringList out = LPBackend::getCmdOutput(cmd);
94  //Now process the output     
95  QStringList list;
96  for(int i=2; i<out.length(); i++){ //first 2 lines are headers
97    //Format: <dataset>:::<lastsnapshot | NONE>:::<lastreplication | NONE>
98    if(out[i].isEmpty()){ continue; }
99    QString ds  = out[i].section(" - ",0,0).simplified();
100    QString snap = out[i].section(" - ",1,1).simplified();
101    QString rep = out[i].section(" - ",2,2).simplified();
102    if(snap == "NONE"){ snap = "-"; }
103    if(rep == "NONE"){ rep = "-"; }
104    list << ds +":::"+ snap+":::"+rep;
105  }
106   
107  return list;
108}
109
110// ==================
111//    Dataset Management
112// ==================
113bool LPBackend::setupDataset(QString dataset, int time, int numToKeep){
114  //Configure inputs
115  QString freq;
116  if(time == -30){ freq = "30min"; }
117  else if(time == -10){ freq = "10min"; }
118  else if(time == -5){ freq = "5min"; }
119  else if(time >= 0 && time < 24){ freq = "daily@"+QString::number(time); }
120  else{ freq = "hourly"; }
121 
122  //Create the command
123  QString cmd = "lpreserver cronsnap "+dataset+" start "+freq+" "+QString::number(numToKeep);
124  int ret = LPBackend::runCmd(cmd);
125   
126  return (ret == 0);
127}
128
129bool LPBackend::removeDataset(QString dataset){
130  QString cmd = "lpreserver cronsnap "+dataset+" stop";
131  int ret = LPBackend::runCmd(cmd);
132   
133  return (ret == 0);
134}
135
136bool LPBackend::datasetInfo(QString dataset, int& time, int& numToKeep){
137  QString cmd = "lpreserver listcron";
138  QStringList out = LPBackend::getCmdOutput(cmd);
139  //Now process the output
140  bool ok = false;
141  for(int i=0; i<out.length(); i++){
142    if(out[i].section(" - ",0,0).simplified() == dataset){
143      //Get time schedule (in integer format)
144      QString sch = out[i].section(" - ",1,1).simplified();
145      if(sch.startsWith("daily@")){ time = sch.section("@",1,1).simplified().toInt(); }
146      else if(sch=="5min"){time = -5;}
147      else if(sch=="10min"){time = -10;}
148      else if(sch=="30min"){time = -30;}
149      else{ time = -60; } //hourly
150      //Get total snapshots
151      numToKeep = out[i].section("- total:",1,1).simplified().toInt();
152      ok=true;
153      break;
154    }
155  }
156  //qDebug() << "lpreserver cronsnap:\n" << out << QString::number(time) << QString::number(numToKeep);
157   
158  return ok;
159}
160
161// ==================
162//    Snapshop Management
163// ==================
164bool LPBackend::newSnapshot(QString dataset, QString snapshotname){
165  QString cmd = "lpreserver mksnap --replicate "+dataset+" "+snapshotname;
166  int ret = LPBackend::runCmd(cmd);
167   
168  return (ret == 0);
169}
170
171bool LPBackend::removeSnapshot(QString dataset, QString snapshot){
172  QString cmd = "lpreserver rmsnap "+dataset +" "+snapshot;
173  int ret = LPBackend::runCmd(cmd);
174   
175  return (ret == 0);
176}
177
178bool LPBackend::revertSnapshot(QString dataset, QString snapshot){
179  QString cmd = "lpreserver revertsnap "+dataset +" "+snapshot;
180  int ret  = LPBackend::runCmd(cmd);
181   
182  return (ret == 0);
183}
184
185QString LPBackend::revertSnapshotFile(QString dsmountpoint, QString snapshot, QString filepath){
186  //Copy the given file from the snapshot back into the main dataset
187  // -- filepath: full path to the file in the snapshot directory
188 
189  //Check that the file path is complete and the file exists
190  if(!QFile::exists(filepath)){
191    //invalid file given
192    return "";
193  }
194  //Generate the new file path
195  QString newfilepath = filepath.replace(dsmountpoint+"/.zfs/snapshot/"+snapshot, dsmountpoint);       
196  if( QFile::exists(newfilepath) ){
197    //get the file extension
198    QString filename = newfilepath.section("/",-1);
199    QString ext = filename.section(".",-1);
200    if( !ext.isEmpty() && !filename.startsWith("."+ext) && ext!=filename){
201      newfilepath.chop(ext.length()+1);
202      newfilepath.append("-reversion."+ext);
203      int i=1;
204      //append a number to the end if a reversion file already exists
205      while(QFile::exists(newfilepath)){
206        newfilepath.chop(ext.length()+1);
207        newfilepath.append(QString::number(i)+"."+ext);
208      i  ++;
209      }
210    }else{
211      //File without an extension - just append a number
212      newfilepath.append("-reversion");
213      int i=1; 
214      QString npath = newfilepath;
215      while(QFile::exists(npath)){
216        npath = newfilepath.append(QString::number(i));
217        i++;
218      }
219      newfilepath = npath;
220    }
221  }
222  //perform the copy
223  bool ok = QFile::copy(filepath,newfilepath);
224  //return the path to the new file if the copy was successful
225  if(ok){ 
226    //reset the permissions on the reverted file to match the original
227    QFile::setPermissions(newfilepath, QFile::permissions(filepath));
228    return newfilepath;
229  }else{ return ""; }
230}
231
232// ==================
233//    Replication Management
234// ==================
235bool LPBackend::setupReplication(QString dataset, QString remotehost, QString user, int port, QString remotedataset, int time){
236  QString stime = "sync"; //synchronize on snapshot creation (default)
237  if(time >= 0 && time < 24){ stime = QString::number(time); } //daily at a particular hour (24 hour notation)
238 
239 
240  QString cmd = "lpreserver replicate add "+remotehost+" "+user+" "+ QString::number(port)+" "+dataset+" "+remotedataset+" "+stime;
241  int ret = LPBackend::runCmd(cmd);
242 
243  return (ret == 0);
244}
245
246bool LPBackend::removeReplication(QString dataset){
247  QString cmd = "lpreserver replicate remove "+dataset;
248  int ret = LPBackend::runCmd(cmd);     
249   
250  return (ret == 0);
251}
252
253bool LPBackend::replicationInfo(QString dataset, QString& remotehost, QString& user, int& port, QString& remotedataset, int& time){
254  QString cmd = "lpreserver replicate list";
255  QStringList out = LPBackend::getCmdOutput(cmd);
256  //Now process the output
257  bool ok = false;
258  for(int i=0; i<out.length(); i++){
259    if(out[i].contains("->") && out[i].startsWith(dataset)){
260      QString data = out[i].section("->",1,1);
261      user = data.section("@",0,0);
262      remotehost = data.section("@",1,1).section("[",0,0);
263      port = data.section("[",1,1).section("]",0,0).toInt();
264      remotedataset = data.section(":",1,1).section(" Time",0,0);
265      QString synchro = data.section("Time:",1,1).simplified();
266        if(synchro == "sync"){ time = -1; }
267        else{ time = synchro.toInt(); }
268      ok = true;
269      break;
270    }
271  }       
272   
273  return ok;
274}
275
276// ======================
277//          SSH Key Management
278// ======================
279bool LPBackend::setupSSHKey(QString remoteHost, QString remoteUser, int remotePort){
280  QString LPPATH = "/usr/local/share/lifePreserver";
281  QString cmd = "xterm -e \""+LPPATH+"/scripts/setup-ssh-keys.sh "+remoteUser+" "+remoteHost+" "+QString::number(remotePort)+"\"";
282  int ret = LPBackend::runCmd(cmd);
283  return (ret == 0);
284}
285
286QStringList LPBackend::findValidUSBDevices(){
287  //Return format: "<mountpoint> (<device node>")
288  QString cmd = "mount";
289  QStringList out = LPBackend::getCmdOutput(cmd);
290  //Now process the output
291  QStringList list;
292  for(int i=0; i<out.length(); i++){
293      if(out[i].startsWith("/dev/da") && out[i].contains("(msdosfs,")){
294      QString mountpoint = out[i].section(" on ",1,1).section("(",0,0).simplified();
295      QString devnode = out[i].section(" on ",0,0).section("/",-1).simplified();
296      list << mountpoint +" ("+devnode+")";
297    }
298  }
299  return list;
300}
301
302bool LPBackend::copySSHKey(QString mountPath, QString localHost){
303  QString publicKey = "/root/.ssh/id_rsa";
304  //copy the file onto the designated USB stick
305  if(!mountPath.endsWith("/")){ mountPath.append("/"); }
306  QDir lDir=mountPath + "lpreserver";
307  if ( ! lDir.exists() )
308     lDir.mkdir(lDir.path());
309
310  mountPath.append("lpreserver/"+localHost+"-id_rsa");
311
312  bool ok = QFile::copy(publicKey, mountPath);
313  return ok;
314}
315
316
317// =========================
318//             PRIVATE FUNCTIONS
319// =========================
320QStringList LPBackend::getCmdOutput(QString cmd){
321  QProcess *proc = new QProcess;
322  proc->setProcessChannelMode(QProcess::MergedChannels);
323  proc->start(cmd);
324  while(!proc->waitForFinished(300)){
325    QCoreApplication::processEvents();
326  }
327  QStringList out = QString(proc->readAllStandardOutput()).split("\n"); 
328  delete proc; 
329  return out;
330}
331
332int LPBackend::runCmd(QString cmd){
333  QProcess *proc = new QProcess;
334  proc->setProcessChannelMode(QProcess::MergedChannels);
335  proc->start(cmd);
336  while(!proc->waitForFinished(300)){
337    QCoreApplication::processEvents();
338  }
339  int ret = proc->exitCode();
340  delete proc; 
341  return ret;
342}
Note: See TracBrowser for help on using the repository browser.