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

9.2-releasereleng/10.0releng/10.0.1
Last change on this file since ea627be was ea627be, checked in by Ken <ken@…>, 8 months ago

Fix an issue with "~" in the filepath for file reversions of life-preserver

  • Property mode set to 100644
File size: 12.1 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// ==================
164void LPBackend::newSnapshot(QString dataset, QString snapshotname){
165  //This needs to run externally - since the snapshot is simply added to the queue, and the replication
166  //   afterwards may take a long time.
167  QString cmd = "lpreserver mksnap --replicate "+dataset+" "+snapshotname;
168  QProcess::startDetached(cmd);
169   
170  return;
171}
172
173bool LPBackend::removeSnapshot(QString dataset, QString snapshot){
174  QString cmd = "lpreserver rmsnap "+dataset +" "+snapshot;
175  int ret = LPBackend::runCmd(cmd);
176   
177  return (ret == 0);
178}
179
180bool LPBackend::revertSnapshot(QString dataset, QString snapshot){
181  QString cmd = "lpreserver revertsnap "+dataset +" "+snapshot;
182  int ret  = LPBackend::runCmd(cmd);
183   
184  return (ret == 0);
185}
186
187QString LPBackend::revertSnapshotFile(QString dsmountpoint, QString snapshot, QString filepath){
188  //Copy the given file from the snapshot back into the main dataset
189  // -- filepath: full path to the file in the snapshot directory
190  //qDebug() << " - Revert file:" << filepath;
191  filepath.replace("~",QDir::homePath());
192  //Check that the file path is complete and the file exists
193  if(!QFile::exists(filepath)){
194    //invalid file given
195    return "";
196  }
197  //Generate the new file path
198  QString newfilepath = filepath.replace(dsmountpoint+"/.zfs/snapshot/"+snapshot, dsmountpoint);       
199  if( QFile::exists(newfilepath) ){
200    //get the file extension
201    QString filename = newfilepath.section("/",-1);
202    QString ext = filename.section(".",-1);
203    if( !ext.isEmpty() && !filename.startsWith("."+ext) && ext!=filename){
204      newfilepath.chop(ext.length()+1);
205      newfilepath.append("-reversion."+ext);
206      int i=1;
207      //append a number to the end if a reversion file already exists
208      while(QFile::exists(newfilepath)){
209        newfilepath.chop(ext.length()+1);
210        newfilepath.append(QString::number(i)+"."+ext);
211      i  ++;
212      }
213    }else{
214      //File without an extension - just append a number
215      newfilepath.append("-reversion");
216      int i=1; 
217      QString npath = newfilepath;
218      while(QFile::exists(npath)){
219        npath = newfilepath.append(QString::number(i));
220        i++;
221      }
222      newfilepath = npath;
223    }
224  }
225  //perform the copy
226  qDebug() <<  " - File Reversion:" << filepath << newfilepath;
227  bool ok = QFile::copy(filepath,newfilepath);
228  //return the path to the new file if the copy was successful
229  if(ok){ 
230    //reset the permissions on the reverted file to match the original
231    QFile::setPermissions(newfilepath, QFile::permissions(filepath));
232    return newfilepath;
233  }else{ return ""; }
234}
235
236// ==================
237//    Replication Management
238// ==================
239bool LPBackend::setupReplication(QString dataset, QString remotehost, QString user, int port, QString remotedataset, int time){
240  QString stime = "sync"; //synchronize on snapshot creation (default)
241  if(time >= 0 && time < 24){ stime = QString::number(time); } //daily at a particular hour (24 hour notation)
242 
243 
244  QString cmd = "lpreserver replicate add "+remotehost+" "+user+" "+ QString::number(port)+" "+dataset+" "+remotedataset+" "+stime;
245  int ret = LPBackend::runCmd(cmd);
246 
247  return (ret == 0);
248}
249
250bool LPBackend::removeReplication(QString dataset){
251  QString cmd = "lpreserver replicate remove "+dataset;
252  int ret = LPBackend::runCmd(cmd);     
253   
254  return (ret == 0);
255}
256
257bool LPBackend::replicationInfo(QString dataset, QString& remotehost, QString& user, int& port, QString& remotedataset, int& time){
258  QString cmd = "lpreserver replicate list";
259  QStringList out = LPBackend::getCmdOutput(cmd);
260  //Now process the output
261  bool ok = false;
262  for(int i=0; i<out.length(); i++){
263    if(out[i].contains("->") && out[i].startsWith(dataset)){
264      QString data = out[i].section("->",1,1);
265      user = data.section("@",0,0);
266      remotehost = data.section("@",1,1).section("[",0,0);
267      port = data.section("[",1,1).section("]",0,0).toInt();
268      remotedataset = data.section(":",1,1).section(" Time",0,0);
269      QString synchro = data.section("Time:",1,1).simplified();
270        if(synchro == "sync"){ time = -1; }
271        else{ time = synchro.toInt(); }
272      ok = true;
273      break;
274    }
275  }       
276   
277  return ok;
278}
279
280// ======================
281//          SSH Key Management
282// ======================
283bool LPBackend::setupSSHKey(QString remoteHost, QString remoteUser, int remotePort){
284  QString LPPATH = "/usr/local/share/lifePreserver";
285  QString cmd = "xterm -e \""+LPPATH+"/scripts/setup-ssh-keys.sh "+remoteUser+" "+remoteHost+" "+QString::number(remotePort)+"\"";
286  int ret = LPBackend::runCmd(cmd);
287  return (ret == 0);
288}
289
290QStringList LPBackend::findValidUSBDevices(){
291  //Return format: "<mountpoint> (<device node>")
292  QString cmd = "mount";
293  QStringList out = LPBackend::getCmdOutput(cmd);
294  //Now process the output
295  QStringList list;
296  for(int i=0; i<out.length(); i++){
297      if(out[i].startsWith("/dev/da") && out[i].contains("(msdosfs,")){
298      QString mountpoint = out[i].section(" on ",1,1).section("(",0,0).simplified();
299      QString devnode = out[i].section(" on ",0,0).section("/",-1).simplified();
300      list << mountpoint +" ("+devnode+")";
301    }
302  }
303  return list;
304}
305
306bool LPBackend::copySSHKey(QString mountPath, QString localHost){
307  QString publicKey = "/root/.ssh/id_rsa";
308  //copy the file onto the designated USB stick
309  if(!mountPath.endsWith("/")){ mountPath.append("/"); }
310  QDir lDir=mountPath + "lpreserver";
311  if ( ! lDir.exists() )
312     lDir.mkdir(lDir.path());
313
314  mountPath.append("lpreserver/"+localHost+"-id_rsa");
315
316  bool ok = QFile::copy(publicKey, mountPath);
317  return ok;
318}
319
320// ======================
321//          Device Management
322// ======================
323QStringList LPBackend::listDevices(){
324  //Scan the system for all valid da* and ada* devices (USB/SCSI, SATA)
325  //Return format: "<device node> (<device information>)"
326  QDir devDir("/dev");
327  QStringList devs = devDir.entryList(QStringList() << "da*"<<"ada*", QDir::System | QDir::NoSymLinks, QDir::Name);
328  QStringList camOut = LPBackend::getCmdOutput("camcontrol devlist");
329  QStringList output, flist;   
330  for(int i=0; i<devs.length(); i++){
331    flist = camOut.filter("("+devs[i]+",");
332    //still need to add an additional device filter to weed out devices currently in use.
333    if(!flist.isEmpty()){ output << devs[i] + " ("+flist[0].section(">",0,0).remove("<").simplified()+")"; }
334  }
335  return output;
336}
337
338bool LPBackend::isMounted(QString device){
339  qDebug() << "Device mount check not implemented yet";
340  return false;
341}
342
343bool LPBackend::unmountDevice(QString device){
344  qDebug() << "Device unmounting not implemented yet";
345  return false;
346}
347
348// =========================
349//             PRIVATE FUNCTIONS
350// =========================
351QStringList LPBackend::getCmdOutput(QString cmd){
352  QProcess *proc = new QProcess;
353  proc->setProcessChannelMode(QProcess::MergedChannels);
354  proc->start(cmd);
355  while(!proc->waitForFinished(300)){
356    QCoreApplication::processEvents();
357  }
358  QStringList out = QString(proc->readAllStandardOutput()).split("\n"); 
359  delete proc; 
360  return out;
361}
362
363int LPBackend::runCmd(QString cmd){
364  QProcess *proc = new QProcess;
365  proc->setProcessChannelMode(QProcess::MergedChannels);
366  proc->start(cmd);
367  while(!proc->waitForFinished(300)){
368    QCoreApplication::processEvents();
369  }
370  int ret = proc->exitCode();
371  delete proc; 
372  return ret;
373}
Note: See TracBrowser for help on using the repository browser.