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

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2
Last change on this file since 845aaf6 was 845aaf6, checked in by Ken Moore <ken@…>, 11 months ago

Setup the new snapshot process as an external process and update the message appropriately. Also make sure that we parse every single line in the log, just to make sure that we don't miss an important message (keep it quiet during the initial startup phase though).

  • Property mode set to 100644
File size: 11.0 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 
191  //Check that the file path is complete and the file exists
192  if(!QFile::exists(filepath)){
193    //invalid file given
194    return "";
195  }
196  //Generate the new file path
197  QString newfilepath = filepath.replace(dsmountpoint+"/.zfs/snapshot/"+snapshot, dsmountpoint);       
198  if( QFile::exists(newfilepath) ){
199    //get the file extension
200    QString filename = newfilepath.section("/",-1);
201    QString ext = filename.section(".",-1);
202    if( !ext.isEmpty() && !filename.startsWith("."+ext) && ext!=filename){
203      newfilepath.chop(ext.length()+1);
204      newfilepath.append("-reversion."+ext);
205      int i=1;
206      //append a number to the end if a reversion file already exists
207      while(QFile::exists(newfilepath)){
208        newfilepath.chop(ext.length()+1);
209        newfilepath.append(QString::number(i)+"."+ext);
210      i  ++;
211      }
212    }else{
213      //File without an extension - just append a number
214      newfilepath.append("-reversion");
215      int i=1; 
216      QString npath = newfilepath;
217      while(QFile::exists(npath)){
218        npath = newfilepath.append(QString::number(i));
219        i++;
220      }
221      newfilepath = npath;
222    }
223  }
224  //perform the copy
225  bool ok = QFile::copy(filepath,newfilepath);
226  //return the path to the new file if the copy was successful
227  if(ok){ 
228    //reset the permissions on the reverted file to match the original
229    QFile::setPermissions(newfilepath, QFile::permissions(filepath));
230    return newfilepath;
231  }else{ return ""; }
232}
233
234// ==================
235//    Replication Management
236// ==================
237bool LPBackend::setupReplication(QString dataset, QString remotehost, QString user, int port, QString remotedataset, int time){
238  QString stime = "sync"; //synchronize on snapshot creation (default)
239  if(time >= 0 && time < 24){ stime = QString::number(time); } //daily at a particular hour (24 hour notation)
240 
241 
242  QString cmd = "lpreserver replicate add "+remotehost+" "+user+" "+ QString::number(port)+" "+dataset+" "+remotedataset+" "+stime;
243  int ret = LPBackend::runCmd(cmd);
244 
245  return (ret == 0);
246}
247
248bool LPBackend::removeReplication(QString dataset){
249  QString cmd = "lpreserver replicate remove "+dataset;
250  int ret = LPBackend::runCmd(cmd);     
251   
252  return (ret == 0);
253}
254
255bool LPBackend::replicationInfo(QString dataset, QString& remotehost, QString& user, int& port, QString& remotedataset, int& time){
256  QString cmd = "lpreserver replicate list";
257  QStringList out = LPBackend::getCmdOutput(cmd);
258  //Now process the output
259  bool ok = false;
260  for(int i=0; i<out.length(); i++){
261    if(out[i].contains("->") && out[i].startsWith(dataset)){
262      QString data = out[i].section("->",1,1);
263      user = data.section("@",0,0);
264      remotehost = data.section("@",1,1).section("[",0,0);
265      port = data.section("[",1,1).section("]",0,0).toInt();
266      remotedataset = data.section(":",1,1).section(" Time",0,0);
267      QString synchro = data.section("Time:",1,1).simplified();
268        if(synchro == "sync"){ time = -1; }
269        else{ time = synchro.toInt(); }
270      ok = true;
271      break;
272    }
273  }       
274   
275  return ok;
276}
277
278// ======================
279//          SSH Key Management
280// ======================
281bool LPBackend::setupSSHKey(QString remoteHost, QString remoteUser, int remotePort){
282  QString LPPATH = "/usr/local/share/lifePreserver";
283  QString cmd = "xterm -e \""+LPPATH+"/scripts/setup-ssh-keys.sh "+remoteUser+" "+remoteHost+" "+QString::number(remotePort)+"\"";
284  int ret = LPBackend::runCmd(cmd);
285  return (ret == 0);
286}
287
288QStringList LPBackend::findValidUSBDevices(){
289  //Return format: "<mountpoint> (<device node>")
290  QString cmd = "mount";
291  QStringList out = LPBackend::getCmdOutput(cmd);
292  //Now process the output
293  QStringList list;
294  for(int i=0; i<out.length(); i++){
295      if(out[i].startsWith("/dev/da") && out[i].contains("(msdosfs,")){
296      QString mountpoint = out[i].section(" on ",1,1).section("(",0,0).simplified();
297      QString devnode = out[i].section(" on ",0,0).section("/",-1).simplified();
298      list << mountpoint +" ("+devnode+")";
299    }
300  }
301  return list;
302}
303
304bool LPBackend::copySSHKey(QString mountPath, QString localHost){
305  QString publicKey = "/root/.ssh/id_rsa";
306  //copy the file onto the designated USB stick
307  if(!mountPath.endsWith("/")){ mountPath.append("/"); }
308  QDir lDir=mountPath + "lpreserver";
309  if ( ! lDir.exists() )
310     lDir.mkdir(lDir.path());
311
312  mountPath.append("lpreserver/"+localHost+"-id_rsa");
313
314  bool ok = QFile::copy(publicKey, mountPath);
315  return ok;
316}
317
318
319// =========================
320//             PRIVATE FUNCTIONS
321// =========================
322QStringList LPBackend::getCmdOutput(QString cmd){
323  QProcess *proc = new QProcess;
324  proc->setProcessChannelMode(QProcess::MergedChannels);
325  proc->start(cmd);
326  while(!proc->waitForFinished(300)){
327    QCoreApplication::processEvents();
328  }
329  QStringList out = QString(proc->readAllStandardOutput()).split("\n"); 
330  delete proc; 
331  return out;
332}
333
334int LPBackend::runCmd(QString cmd){
335  QProcess *proc = new QProcess;
336  proc->setProcessChannelMode(QProcess::MergedChannels);
337  proc->start(cmd);
338  while(!proc->waitForFinished(300)){
339    QCoreApplication::processEvents();
340  }
341  int ret = proc->exitCode();
342  delete proc; 
343  return ret;
344}
Note: See TracBrowser for help on using the repository browser.