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

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

Fix a typo in the lpreserver replication settings modification

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