source: src-qt4/life-preserver/lp-gui/LPGUtils.cpp @ 109c737

enter/10releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1releng/10.1.1releng/10.1.2releng/10.2stable/10
Last change on this file since 109c737 was 109c737, checked in by Ken Moore <ken@…>, 18 months ago

Add a new "Classic Backup" dialog for customizing the home-dir archive and providing status updates while it is running.

  • Property mode set to 100644
File size: 11.7 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    nDir.cdUp();
168    ok = nDir.mkdir(newPath.section("/",-1));
169    if(ok){ 
170      nDir.cd(newPath.section("/",-1)); 
171      QFile::setPermissions(newPath, QFile::permissions(oldPath)); //make sure the new dir has the old permissions
172      QFileInfo FI(oldPath);
173      system( QString("chown "+FI.owner()+":"+FI.group()+" "+newPath).toUtf8() );
174    }
175  }
176  //Get a list of any files that error
177  QStringList errors;
178  if(!ok){
179    errors << newPath;
180    return errors;
181  }
182  //Get a list of all the files in the old dir and copy them over
183  QStringList fList = oDir.entryList(QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot, QDir::Name);
184  for(int i=0; i<fList.length(); i++){
185    if( !revertFile(oldPath+"/"+fList[i], newPath+"/"+fList[i]) ){
186       errors << newPath+"/"+fList[i];
187    }
188  }
189  //Now list all the directories in the old dir and recursively copy them over
190  fList = oDir.entryList(QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot, QDir::Name);
191  for(int i=0; i<fList.length(); i++){
192    QStringList errs = revertDir(oldPath+"/"+fList[i], newPath+"/"+fList[i]);
193    if( !errs.isEmpty() ){ errors << errs; }
194  }
195  return errors;
196}
197
198QString LPGUtils::packageHomeDir(QString username, QString packageName){
199  //Check that the user directory exists
200  //qDebug() << "Start package dir:" << username << packageName;
201  if(!QFile::exists("/usr/home/"+username)){ return ""; }
202  //Check that the package has the right extension
203  if(!packageName.endsWith(".home.tar.gz")){ packageName.append(".home.tar.gz"); }
204  //Generate any additional files to be contained in the package
205 
206 
207  //Generate the command
208  QString cmd = "tar";
209  QStringList args;
210  args << "-czf" << "/usr/home/"+packageName << "-C" << "/usr/home" << username;
211  //Create the exclude list and skip these files
212  QStringList excludes;
213    excludes << "*flashplayer*" << "*/PBI-*.desktop"; //Don't include the flash plugin/PBI entries
214  for(int i=0; i<excludes.length(); i++){
215    args.prepend(excludes[i]); args.prepend("--exclude");
216  }
217  //Run the command
218  qDebug() << "Package command:" << cmd << args;
219  LPBackend::runCmd(cmd, args); //need to use this version due to the complex command
220
221  //Check that the package was created
222  QString packagePath;
223  if(QFile::exists("/usr/home/"+packageName)){ packagePath = "/usr/home/"+packageName; }
224  //Now return the path to the package file
225  return packagePath;
226}
227
228bool LPGUtils::checkPackageUserPath(QString packagePath, QString *user){
229  user->clear();
230  //Determine if the file exists
231  if( !QFile::exists(packagePath) ){ return false; }
232  //Check the username of the home dir in the package
233  QStringList ret = LPBackend::getCmdOutput("tar -tvf "+packagePath+" -q \"*/Desktop\"");
234  if(ret.isEmpty()){ return false; }
235  QString username = ret[0].section(" ",2,2,QString::SectionSkipEmpty).simplified();
236  QString dirname = ret[0].section(" ",8,8,QString::SectionSkipEmpty).section("/",0,0).simplified();
237  user->append(username); //additional output
238  //Now check for the user on the local system
239  //This is just a simple check that the user directory exists, and the user/directory are the same within the package
240  return (username == dirname && QFile::exists("/usr/home/"+dirname) ); 
241}
242
243bool LPGUtils::extractHomeDirPackage(QString packagePath){
244 //Determine if the file exists
245  if( !QFile::exists(packagePath) ){ return false; }
246  //Now extract the archive in the home directory
247  QString cmd = "tar -xpf "+packagePath+" -C /usr/home";
248  qDebug() << "Extract command:" << cmd;
249  int ret = LPBackend::runCmd(cmd);
250  return (ret == 0);
251}
252
253QStringList LPGUtils::listAvailableHardDisks(){
254  QDir dev("/dev");
255  QStringList filters;
256        filters << "ada*" << "da*";
257  QStringList devs = dev.entryList(filters, QDir::Files | QDir::System | QDir::NoDotAndDotDot, QDir::Name);
258  //Filter out all the partitions (only keep full devices)
259  for(int i=0; i<devs.length(); i++){
260    devs[i] = devs[i].section("s",0,0,QString::SectionSkipEmpty);
261  }
262  devs.removeDuplicates();
263  return devs;
264}
265
266QStringList LPGUtils::scanNetworkSSH(){
267  //Output format: <name>:::<address>::::<port>
268  QStringList out;
269  QStringList netout = LPBackend::getCmdOutput("avahi-browse -art");
270  for(int i=0; i<netout.length(); i++){
271    if(netout[i].startsWith("=") && netout[i].contains("local")){
272      QString name, address, port;
273      for(int j=0; j<3; j++){ //need the next 3 lines
274        i++; //Move to the next line
275        QString var = netout[i].section("=",0,0).replace("\t"," ").simplified();
276        QString val = netout[i].section("[",1,1).section("]",0,0).simplified();
277        if(var == "hostname"){ name = val.section(".local",0,0).simplified(); }
278        else if(var == "address"){ address = val; }
279        else if(var == "port"){ port = val; }
280      }
281      //Check that it is an SSH connection that is open (port 22)
282      if(port == "22"){ 
283         out << name+":::"+address+":::"+port;
284      }
285    }
286  }
287  return out;
288}
Note: See TracBrowser for help on using the repository browser.