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

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2
Last change on this file since 2a5e337 was 2a5e337, checked in by Kris Moore <kris@…>, 11 months ago

Replace old life-preserver utility with our new one which uses ZFS snapshots / replication

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