source: src-qt4/life-preserver/lp-gui/LPGUtils.cpp @ 65e10ad

releng/10.0.1releng/10.0.2
Last change on this file since 65e10ad was 65e10ad, checked in by Ken Moore <ken@…>, 5 months ago

Fix a bug in the Life-preserver GUI where if the parent/containing directory for a reversion is missing it would fail. Now it will automatically re-create the parent directory heirarchy as necessary when doing a restore.

  • Property mode set to 100644
File size: 11.9 KB
Line 
1#include "LPGUtils.h"
2
3LPDataset LPGUtils::loadPoolData(QString zpool){
4  //Load the current information for the given zpool
5  qDebug() << "[DEBUG] New Dataset: " << zpool;
6  LPDataset DSC;
7  //List all the mountpoints in this dataset
8  qDebug() << "[DEBUG] list snapshots";
9  QStringList subsets = LPBackend::listDatasetSubsets(zpool);
10  QStringList lpsnaps = LPBackend::listLPSnapshots(zpool);
11  //populate the list of snapshots available for each mountpoint
12  for(int i=0; i<subsets.length(); i++){
13    //qDebug() << "Subset:" << subsets[i];
14    QStringList snaps = LPBackend::listSnapshots(subsets[i]);
15    //qDebug() << " - Snapshots:" << snaps;
16    if(snaps.isEmpty()){
17      //invalid subset - remove it from the list
18      subsets.removeAt(i);
19      i--;
20    }else{
21      QStringList subsnaps;
22      //only list the valid snapshots that life preserver created
23      for(int s=0; s<lpsnaps.length(); s++){
24        int index = snaps.indexOf(lpsnaps[s]);
25        if(index > -1){ subsnaps << lpsnaps[s]; snaps.removeAt(index); }
26      }
27      /*//Now list all the other available snapshots (no certain ordering)
28      if(!snaps.isEmpty()){try
29        subsnaps << "--"; //so we know that this is a divider between the sections
30        subsnaps << snaps;
31      }*/
32      DSC.subsetHash.insert(subsets[i],subsnaps); //add it to the internal container hash
33    }
34  }
35  //Parse "zpool status <pool>"
36  qDebug() << "[DEBUG] get zpool status";
37  QStringList zstat = LPBackend::getCmdOutput("zpool status "+zpool);
38  qDebug() << zstat.join("\n");
39  //qDebug() << "zpool status "+zpool+":\n" << zstat.join("\n");
40  bool atheader=false;
41  QStringList disks, diskstates, running, errors, finished;
42  for(int i=0; i<zstat.length(); i++){
43    if(zstat[i].isEmpty()){ continue; }
44    else if(zstat[i].startsWith("  scan:")){
45      QString line = zstat[i].section(":",1,50).replace("\t"," ").simplified();
46      if(line.contains("scrub repaired ")){
47        QString timestamp = line.section(" ",9,13,QString::SectionSkipEmpty);
48        QString numerrors = line.section(" ",6,6,QString::SectionSkipEmpty);
49        finished << QString(QObject::tr("Scrub Finished: %1 (%2 errors)")).arg(timestamp, numerrors);
50      }else if(line.contains("scrub cancel")){
51         QString timestamp = line.section(" ",3,7,QString::SectionSkipEmpty);     
52          finished << QString(QObject::tr("Scrub Cancelled: %1")).arg(timestamp);
53      }else if(line.contains("scrub")){
54        QString timestamp = line.section(" ",4,8,QString::SectionSkipEmpty);
55        running << QString(QObject::tr("Scrub Started: %1")).arg(timestamp);     
56      }else if(line.contains("resilvered")){
57        QString timestamp = line.section(" ",8,12,QString::SectionSkipEmpty);
58        QString numerrors = line.section(" ",5,5,QString::SectionSkipEmpty);
59        finished << QString(QObject::tr("Resilver Finished: %1 (%2 errors)")).arg(timestamp, numerrors);
60      }
61    }else if(zstat[i].contains("NAME") && zstat[i].contains("STATE") && zstat[i].contains("READ") ){
62      //qDebug() <<"Found header";
63      atheader=true;
64    }else if(zstat[i].startsWith("errors:")){ 
65      atheader=false; 
66    }else if(atheader){
67      QString line = zstat[i].replace("\t"," ").simplified();
68      QString dev = line.section(" ",0,0,QString::SectionSkipEmpty);
69      QString state = line.section(" ",1,1,QString::SectionSkipEmpty);
70      //qDebug() << "Found device:" << dev << state;
71      if(dev == zpool){
72        DSC.poolStatus = state;
73      }else if(line.contains("(resilvering)")){
74        disks << dev; diskstates << state; //record this disk and state
75        running << QString(QObject::tr("%1: Currently Resilvering")).arg(dev);
76      }else{
77        disks << dev; diskstates << state; //record this disk and state
78        if(state != "ONLINE"){
79          errors << QString(QObject::tr("%1: %2")).arg(dev, state);
80        }
81      }
82    }
83  }
84  qDebug() << "[DEBUG] Get latest snapshot/replication info";
85  //Now get the latest Snapshot/Replication information
86  QStringList lpstat = LPBackend::listCurrentStatus();
87  for(int i=0; i<lpstat.length(); i++){
88    if(lpstat[i].section(":::",0,0) == zpool){
89      QString lastSnap = lpstat[i].section(":::",1,1);
90      QString lastRep = lpstat[i].section(":::",2,2);
91      if(lastSnap=="-"){ DSC.latestSnapshot = QObject::tr("No Snapshots Available"); }
92      else{ DSC.latestSnapshot = lastSnap; }
93      if(lastRep!="-"){
94        finished << QString(QObject::tr("Latest Replication: %1")).arg(lastRep);
95      }else if(LPBackend::listReplicationTargets().contains(zpool) ){
96        errors << QObject::tr("No Successful Replication");
97      }
98    }
99  }
100  qDebug() << "[DEBUG] save info to the structure and finish";
101  //Now save the info to the dataset
102  DSC.harddisks = disks;
103  DSC.harddiskStatus = diskstates;
104  DSC.errorStatus = errors.join("\n");
105  DSC.runningStatus = running.join("\n");
106  DSC.finishedStatus = finished.join("\n");
107  //Return the dataset
108  return DSC;
109}
110
111QString LPGUtils::generateReversionFileName(QString fileName, QString destDir){
112  fileName = fileName.section("/",-1); //Make sure we only have the filename (no paths)
113  if( !destDir.endsWith("/") ){ destDir.append("/"); }
114  //Quick check if that file already exists in the destination directory
115  if( !QFile::exists(destDir+fileName) ){ return fileName; }
116  //Change the filename to prevent overwriting an existing file
117  QString ext = fileName.section(".",-1); //Get the extension for the filename
118  if( fileName != ext && fileName != ("."+ext) ){
119    //Extension found - need careful adjustment of filename
120    QString newFileName = fileName;
121    newFileName.replace("."+ext, "-reversion."+ext);
122    int i=2;
123    while( QFile::exists(destDir+newFileName) ){
124      //Also need to append a reversion number
125      newFileName = fileName; 
126      newFileName.replace("."+ext, "-reversion"+QString::number(i)+"."+ext);
127      i++; //for the next time around
128    }
129    fileName = newFileName;
130  }else{
131    //no extension - just append the reversion
132    fileName.append("-reversion");
133    if(QFile::exists(destDir+fileName) ){
134      //Also need to append a reversion number
135      int i=2;
136      while( QFile::exists(destDir+fileName+QString::number(i)) ){ i++; }
137      fileName.append( QString::number(i) );
138    }
139  }
140  return fileName;
141}
142
143bool LPGUtils::revertFile(QString oldPath, QString newPath){
144  qDebug() << "Reverting file:" << oldPath << " -> " << newPath;
145  bool ok = QFile::copy(oldPath,newPath);
146  //return the path to the new file if the copy was successful
147  if(ok){ 
148    //reset the permissions on the reverted file to match the original
149    QFile::setPermissions(newPath, QFile::permissions(oldPath));
150    QFileInfo FI(oldPath);
151      system( QString("chown "+FI.owner()+":"+FI.group()+" "+newPath).toUtf8() );
152  }else{
153    qDebug() << " - Error: Could not copy file";
154  }
155  return ok;
156}
157
158QStringList LPGUtils::revertDir(QString oldPath, QString newPath){
159  //Note: this is a recursive function and can take quite a while to perform lots of file copies
160
161  //Load the directories and create it if necessary
162  QDir oDir(oldPath);
163  QDir nDir(newPath);
164  bool ok=true;
165  if( !nDir.exists() ){
166    //Create the new Directory
167    qDebug() << "Re-Create parent directory structure:" << newPath;
168    nDir.cdUp();
169    ok = nDir.mkpath(newPath.section("/",-1)); //also create all parent directories if necessary
170    if(ok){ 
171      qDebug() << " - Reset permissions on the main parent dir to match snapshot";
172      nDir.cd(newPath.section("/",-1)); 
173      QFile::setPermissions(newPath, QFile::permissions(oldPath)); //make sure the new dir has the old permissions
174      QFileInfo FI(oldPath);
175      system( QString("chown "+FI.owner()+":"+FI.group()+" "+newPath).toUtf8() );
176    }
177  }
178  //Get a list of any files that error
179  QStringList errors;
180  if(!ok){
181    errors << newPath;
182    return errors;
183  }
184  //Get a list of all the files in the old dir and copy them over
185  QStringList fList = oDir.entryList(QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot, QDir::Name);
186  for(int i=0; i<fList.length(); i++){
187    if( !revertFile(oldPath+"/"+fList[i], newPath+"/"+fList[i]) ){
188       errors << newPath+"/"+fList[i];
189    }
190  }
191  //Now list all the directories in the old dir and recursively copy them over
192  fList = oDir.entryList(QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot, QDir::Name);
193  for(int i=0; i<fList.length(); i++){
194    QStringList errs = revertDir(oldPath+"/"+fList[i], newPath+"/"+fList[i]);
195    if( !errs.isEmpty() ){ errors << errs; }
196  }
197  return errors;
198}
199
200QString LPGUtils::packageHomeDir(QString username, QString packageName){
201  //Check that the user directory exists
202  //qDebug() << "Start package dir:" << username << packageName;
203  if(!QFile::exists("/usr/home/"+username)){ return ""; }
204  //Check that the package has the right extension
205  if(!packageName.endsWith(".home.tar.gz")){ packageName.append(".home.tar.gz"); }
206  //Generate any additional files to be contained in the package
207 
208 
209  //Generate the command
210  QString cmd = "tar";
211  QStringList args;
212  args << "-czf" << "/usr/home/"+packageName << "-C" << "/usr/home" << username;
213  //Create the exclude list and skip these files
214  QStringList excludes;
215    excludes << "*flashplayer*" << "*/PBI-*.desktop"; //Don't include the flash plugin/PBI entries
216  for(int i=0; i<excludes.length(); i++){
217    args.prepend(excludes[i]); args.prepend("--exclude");
218  }
219  //Run the command
220  qDebug() << "Package command:" << cmd << args;
221  LPBackend::runCmd(cmd, args); //need to use this version due to the complex command
222
223  //Check that the package was created
224  QString packagePath;
225  if(QFile::exists("/usr/home/"+packageName)){ packagePath = "/usr/home/"+packageName; }
226  //Now return the path to the package file
227  return packagePath;
228}
229
230bool LPGUtils::checkPackageUserPath(QString packagePath, QString *user){
231  user->clear();
232  //Determine if the file exists
233  if( !QFile::exists(packagePath) ){ return false; }
234  //Check the username of the home dir in the package
235  QStringList ret = LPBackend::getCmdOutput("tar -tvf "+packagePath+" -q \"*/Desktop\"");
236  if(ret.isEmpty()){ return false; }
237  QString username = ret[0].section(" ",2,2,QString::SectionSkipEmpty).simplified();
238  QString dirname = ret[0].section(" ",8,8,QString::SectionSkipEmpty).section("/",0,0).simplified();
239  user->append(username); //additional output
240  //Now check for the user on the local system
241  //This is just a simple check that the user directory exists, and the user/directory are the same within the package
242  return (username == dirname && QFile::exists("/usr/home/"+dirname) ); 
243}
244
245bool LPGUtils::extractHomeDirPackage(QString packagePath){
246 //Determine if the file exists
247  if( !QFile::exists(packagePath) ){ return false; }
248  //Now extract the archive in the home directory
249  QString cmd = "tar -xpf "+packagePath+" -C /usr/home";
250  qDebug() << "Extract command:" << cmd;
251  int ret = LPBackend::runCmd(cmd);
252  return (ret == 0);
253}
254
255QStringList LPGUtils::listAvailableHardDisks(){
256  QDir dev("/dev");
257  QStringList filters;
258        filters << "ada*" << "da*";
259  QStringList devs = dev.entryList(filters, QDir::Files | QDir::System | QDir::NoDotAndDotDot, QDir::Name);
260  //Filter out all the partitions (only keep full devices)
261  for(int i=0; i<devs.length(); i++){
262    devs[i] = devs[i].section("s",0,0,QString::SectionSkipEmpty);
263  }
264  devs.removeDuplicates();
265  return devs;
266}
267
268QStringList LPGUtils::scanNetworkSSH(){
269  //Output format: <name>:::<address>::::<port>
270  QStringList out;
271  QStringList netout = LPBackend::getCmdOutput("avahi-browse -art");
272  for(int i=0; i<netout.length(); i++){
273    if(netout[i].startsWith("=") && netout[i].contains("local")){
274      QString name, address, port;
275      for(int j=0; j<3; j++){ //need the next 3 lines
276        i++; //Move to the next line
277        QString var = netout[i].section("=",0,0).replace("\t"," ").simplified();
278        QString val = netout[i].section("[",1,1).section("]",0,0).simplified();
279        if(var == "hostname"){ name = val.section(".local",0,0).simplified(); }
280        else if(var == "address"){ address = val; }
281        else if(var == "port"){ port = val; }
282      }
283      //Check that it is an SSH connection that is open (port 22)
284      if(port == "22"){ 
285         out << name+":::"+address+":::"+port;
286      }
287    }
288  }
289  return out;
290}
Note: See TracBrowser for help on using the repository browser.