source: src-qt4/life-preserver/lp-tray/LPWatcher.cpp @ 6a2483b

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

Remove the old life-preserver from the src-qt4/life-preserver dir, and replace it with src-qt4/life-preserver/lp-tray and src-qt4/life-preserver/lp-gui.

  • Property mode set to 100644
File size: 23.8 KB
Line 
1#include "LPWatcher.h"
2
3/* ------ HASH NUMBERING NOTE -----
4  Each set of 10 is a different type of status
5    "message" status: 10-19
6    "replication" status: 20-29
7    "critical" status: 30-39
8    "mirror" status: 40-49
9    "resilver" status: 50-59
10    "scrub" status: 60-69
11  Within each set:
12    *0 = ID Code (for internal identification as necessary)
13    *1 = zpool (example: tank1)
14    *2 = summary (shortened version of the message - tooltips)
15    *3 = message (full message)
16    *4 = timestamp (full date/time timestamp in readable format)
17    *5 = short timestamp (just time in readable format)
18    *6 = description and path of a log file (FORMAT: "example file </path/to/file.log>")
19
20  Valid Internal ID's:
21    SNAPCREATED -> new snapshot created
22    STARTED     -> Task started
23    RUNNING     -> Task running (I.E. status update)
24    FINISHED    -> Task finished
25    ERROR               -> Task failed
26   
27*/
28
29LPWatcher::LPWatcher() : QObject(){
30  //Initialize the path variables
31  FILE_LOG = "/var/log/lpreserver/lpreserver.log";
32  FILE_ERROR="/var/log/lpreserver/error.log";
33  FILE_REPLICATION=""; //this is set automatically based on the log file outputs
34  sysCheckTime = 300000; // 5 minutes
35  //initialize the watcher and timer
36  watcher = new QFileSystemWatcher(this);
37    connect(watcher, SIGNAL(fileChanged(QString)),this,SLOT(fileChanged(QString)) );
38  timer = new QTimer();
39    connect(timer, SIGNAL(timeout()), this, SLOT(checkPoolStatus()) );
40  //initialize the log file reader
41  logfile = new QFile(FILE_LOG, this);
42  LFSTREAM = new QTextStream(logfile);
43  //initialize the replication file reader
44  repfile = new QFile(this);
45}
46
47LPWatcher::~LPWatcher(){
48  //clean up the internal objects
49  delete watcher;
50  delete timer;
51  delete logfile;
52  delete LFSTREAM;
53}
54
55// -----------------------------------
56//    PUBLIC FUNCTIONS
57// -----------------------------------
58void LPWatcher::start(){
59  if(!logfile->exists()){
60    QString dir = FILE_LOG;
61          dir.chop( dir.section("/",-1).count()+1 );
62    if(!QFile::exists(dir)){ system( QString("mkdir -p "+dir).toUtf8() ); }
63    system( QString("touch "+FILE_LOG).toUtf8() );
64  }
65  //Read the current state of the log file
66  logfile->open(QIODevice::ReadOnly | QIODevice::Text);
67  readLogFile(true); //do this quietly the first time through
68  //Now start up the log file watcher
69  watcher->addPath(FILE_LOG);
70  //Now check for any current errors in the LPbackend
71  checkPoolStatus();
72  //And start up the error file watcher
73  if(!timer->isActive()){ timer->start(sysCheckTime); }
74}
75
76void LPWatcher::stop(){
77  watcher->removePaths(watcher->files());
78  logfile->close();
79  timer->stop();
80}
81
82void LPWatcher::refresh(){
83  QTimer::singleShot(1, this, SLOT(checkPoolStatus()) );
84}
85
86QStringList LPWatcher::getMessages(QString type, QStringList msgList){
87  QStringList output;
88  type = type.toLower();
89  //Valid types - "critical"/"running"/"message"
90  //Valid messages - "dataset","message","summary","id", "timestamp", "time"
91  unsigned int base;
92  if(type=="message"){base=10;}
93  else if(type=="replication"){base=20;}
94  else if(type=="critical"){base=30;}
95  else if(type=="mirror"){base=40;}
96  else if(type=="resilver"){base=50;}
97  else if(type=="scrub"){base=60;}
98  else{ return output; } //invalid input type
99  //Now fill the output array based upon requested outputs
100  for(int i=0; i<msgList.length(); i++){
101    msgList[i] = msgList[i].toLower();
102    if(msgList[i]=="id" && LOGS.contains(base)){ output << LOGS[base]; }
103    else if(msgList[i]=="device" && LOGS.contains(base+1)){ output << LOGS[base+1]; }
104    else if(msgList[i]=="summary" && LOGS.contains(base+2)){ output << LOGS[base+2]; }
105    else if(msgList[i]=="message" && LOGS.contains(base+3)){ output << LOGS[base+3]; }
106    else if(msgList[i]=="timestamp" && LOGS.contains(base+4)){ output << LOGS[base+4]; }
107    else if(msgList[i]=="time" && LOGS.contains(base+5)){ output << LOGS[base+5]; }
108    else if(msgList[i]=="files" && LOGS.contains(base+6)){ output << LOGS[base+6]; }
109    else{ output << ""; }
110  }
111  //Return the output list
112  return output;
113}
114
115QStringList LPWatcher::getAllCurrentMessages(){
116  //Useful for quickly displaying all latest messages in a tooltip or summary
117  QStringList output;
118  if(LOGS.contains(12) && LOGS.contains(14)){ output << LOGS[14]+" -- "+LOGS[12]; }
119  if(LOGS.contains(22) && LOGS.contains(24)){ output << LOGS[24]+" -- "+LOGS[22]; }
120  if(LOGS.contains(32) && LOGS.contains(34)){ 
121    //There can be multiple system errors at a time
122    QStringList errors = LOGS[32].split(":::");
123    for(int i=0; i<errors.length(); i++){
124      output << LOGS[34]+" -- "+errors[i]; 
125    }
126  }
127  if(LOGS.contains(42) && LOGS.contains(44)){ output << LOGS[44]+" -- "+LOGS[42]; }
128  if(LOGS.contains(52) && LOGS.contains(54)){ output << LOGS[54]+" -- "+LOGS[52]; }
129  if(LOGS.contains(62) && LOGS.contains(64)){ output << LOGS[64]+" -- "+LOGS[62]; }
130  return output;
131}
132
133bool LPWatcher::isRunning(){
134  if(LOGS.value(20) == "STARTED" || LOGS.value(20) == "RUNNING"){ return true; }
135  else if(LOGS.value(40) == "STARTED" || LOGS.value(40) == "RUNNING"){ return true; }
136  else if(LOGS.value(50) == "STARTED" || LOGS.value(50) == "RUNNING"){ return true; }
137  else if(LOGS.value(60) == "STARTED" || LOGS.value(60) == "RUNNING"){ return true; }
138  else{ return false; }
139}
140
141bool LPWatcher::hasError(){
142  return (LOGS.value(20)=="ERROR" || LOGS.contains(30) || LOGS.value(40)=="ERROR" || LOGS.value(50)=="ERROR" || LOGS.value(60)=="ERROR");
143}
144
145// -------------------------------------
146//    PRIVATE FUNCTIONS
147// -------------------------------------
148void LPWatcher::readLogFile(bool quiet){
149  QStringList reppools = listReplicatedPools();
150  QTextStream in(logfile);
151  while(!LFSTREAM->atEnd()){
152    QString log = LFSTREAM->readLine();
153    //Divide up the log into it's sections
154    QString timestamp = log.section(":",0,2).simplified();
155    QString time = timestamp.section(" ",3,3).simplified();
156    QString message = log.section(":",3,3).toLower().simplified();
157    QString dev = log.section(":",4,4).simplified(); //dataset/snapshot/nothing
158    //Now decide what to do/show because of the log message
159    //qDebug() << "New Log Message:" << log;
160    if(message.contains("creating snapshot")){
161      dev = message.section(" ",-1).simplified();
162      //Setup the status of the message
163      LOGS.insert(10,"SNAPCREATED");
164      LOGS.insert(11,dev); //dataset
165      LOGS.insert(12, QString(tr("New snapshot of %1")).arg(dev) ); //summary
166      LOGS.insert(13, QString(tr("Creating snapshot for %1")).arg(dev) );
167      LOGS.insert(14, timestamp); //full timestamp
168      LOGS.insert(15, time); // time only
169      if(!quiet){ emit MessageAvailable("message"); }
170    }else if(message.contains("starting replication")){
171      //Setup the file watcher for this new log file
172      FILE_REPLICATION = dev;
173      dev = message.section(" ",5,5,QString::SectionSkipEmpty);
174      //Make sure the device is currently setup for replication
175      if( !reppools.contains(dev) ){ FILE_REPLICATION.clear(); continue; }
176      //Try to start the replication watcher
177      if( startRepFileWatcher() ){
178        //Set the appropriate status variables
179        LOGS.insert(20,"STARTED");
180        LOGS.insert(21, dev); //zpool
181        LOGS.insert(22, tr("Replication Starting") ); //summary
182        LOGS.insert(23, QString(tr("Starting replication for %1")).arg(dev) ); //Full message
183        LOGS.insert(24, timestamp); //full timestamp
184        LOGS.insert(25, time); // time only
185        LOGS.insert(26,tr("Replication Log")+" <"+FILE_REPLICATION+">"); //log file
186        if(!quiet){ emit MessageAvailable("replication"); }
187      }
188    }else if(message.contains("finished replication")){
189      stopRepFileWatcher();
190      dev = message.section(" ",-1).simplified();
191      //Make sure the device is currently setup for replication
192      if( reppools.contains(dev) ){
193        //Now set the status of the process
194        LOGS.insert(20,"FINISHED");
195        LOGS.insert(21,dev); //dataset
196        LOGS.insert(22, tr("Finished Replication") ); //summary
197        LOGS.insert(23, QString(tr("Finished replication for %1")).arg(dev) );
198        LOGS.insert(24, timestamp); //full timestamp
199        LOGS.insert(25, time); // time only
200        LOGS.insert(26, ""); //clear the log file entry
201        if(!quiet){ emit MessageAvailable("replication"); }
202      }else if( !reppools.contains( LOGS.value(21) ) ){
203        //Clear the current replication log, device was removed from replication
204        LOGS.remove(20);
205        LOGS.remove(21);
206        LOGS.remove(22);
207        LOGS.remove(23);
208        LOGS.remove(24);
209        LOGS.remove(25);
210        LOGS.remove(26);
211        if(!quiet){ emit MessageAvailable(""); }
212      }
213    }else if( message.contains("failed replication") ){
214      stopRepFileWatcher();
215      //Now set the status of the process
216      dev = message.section(" ",-1).simplified();
217      //Make sure the device is currently setup for replication
218      if( reppools.contains(dev) ){
219        //Update the logs
220        QString file = log.section("LOGFILE:",1,1).simplified();
221        QString tt = QString(tr("Replication Failed for %1")).arg(dev) +"\n"+ QString(tr("Logfile available at: %1")).arg(file);
222        LOGS.insert(20,"ERROR");
223        LOGS.insert(21,dev); //dataset
224        LOGS.insert(22, tr("Replication Failed") ); //summary
225        LOGS.insert(23, tt );
226        LOGS.insert(24, timestamp); //full timestamp
227        LOGS.insert(25, time); // time only     
228        LOGS.insert(26, tr("Replication Error Log")+" <"+file+">" );
229        if(!quiet){ emit MessageAvailable("replication"); }
230      }else if( !reppools.contains( LOGS.value(21) ) ){
231        //Clear the current replication log, device was removed from replication
232        LOGS.remove(20);
233        LOGS.remove(21);
234        LOGS.remove(22);
235        LOGS.remove(23);
236        LOGS.remove(24);
237        LOGS.remove(25);
238        LOGS.remove(26);
239        if(!quiet){ emit MessageAvailable(""); }
240      }
241    }
242         
243  }
244}
245
246void LPWatcher::readReplicationFile(){
247  QString stat;
248  while( !RFSTREAM->atEnd() ){ 
249    QString line = RFSTREAM->readLine(); 
250    if(line.contains("total estimated size")){ repTotK = line.section(" ",-1).simplified(); } //save the total size to replicate
251    else if(line.startsWith("send from ")){}
252    else if(line.startsWith("TIME ")){}
253    else if(line.startsWith("warning: ")){} //start of an error
254    else{ stat = line; } //only save the relevant/latest status line
255  }
256  if(!stat.isEmpty()){
257    //qDebug() << "New Status Message:" << stat;           
258    //Divide up the status message into sections
259    stat.replace("\t"," ");
260    QString dataset = stat.section(" ",2,2,QString::SectionSkipEmpty).section("/",0,0).simplified();
261    QString cSize = stat.section(" ",1,1,QString::SectionSkipEmpty);
262    //Now Setup the tooltip
263    if(cSize != lastSize){ //don't update the info if the same size info
264      QString percent;
265      if(!repTotK.isEmpty()){
266        //calculate the percentage
267        double tot = displayToDoubleK(repTotK);
268        double c = displayToDoubleK(cSize);
269        if( tot!=-1 & c!=-1){
270          double p = (c*100)/tot;
271          p = int(p*10)/10.0; //round to 1 decimel places
272          percent = QString::number(p) + "%";
273        }
274      }
275      if(repTotK.isEmpty()){ repTotK = "??"; }
276      //Format the info string
277      QString status = cSize+"/"+repTotK;
278      if(!percent.isEmpty()){ status.append(" ("+percent+")"); }
279      QString txt = QString(tr("Replicating %1: %2")).arg(dataset, status);
280      lastSize = cSize; //save the current size for later
281      //Now set the current process status
282      LOGS.insert(20,"RUNNING");
283      LOGS.insert(21,dataset);
284      LOGS.insert(22,txt);
285      LOGS.insert(23,txt);
286      emit MessageAvailable("");
287    }
288  }
289}
290
291bool LPWatcher::startRepFileWatcher(){
292  //qDebug() << "Start Rep File Watcher:" << FILE_REPLICATION;
293  if(FILE_REPLICATION.isEmpty()){ return false; }
294  if(watcher->files().contains(FILE_REPLICATION)){ return true; } //duplicate - file already opened
295  /*else if(!watcher->files().isEmpty()){
296    //Check that the file watcher is not already operating on a file
297    // only one can be running at a time, so always cancel the previous instance (it is stale)
298    // *** WARNING - This causes seg fault for some reason****
299    QString tmp = FILE_REPLICATION;
300    stopRepFileWatcher();
301    FILE_REPLICATION = tmp;
302  }*/
303  //Check to make sure that lpreserver actually has a process running before starting this
304  if( !isReplicationRunning() ){ qDebug() << "PID not found"; FILE_REPLICATION.clear(); return false; }
305  //Check for the existance of the file to watch and create it as necessary 
306  if(!QFile::exists(FILE_REPLICATION)){ system( QString("touch "+FILE_REPLICATION).toUtf8() ); }
307  //Now open the file and start watching it for changes
308  repfile->setFileName(FILE_REPLICATION);
309  repfile->open(QIODevice::ReadOnly | QIODevice::Text);
310  RFSTREAM = new QTextStream(repfile);
311  watcher->addPath(FILE_REPLICATION);
312  //qDebug() << "Finished starting rep file watcher";
313  return true;
314}
315
316void LPWatcher::stopRepFileWatcher(){
317  //qDebug() << "Stop Rep File Watcher:" << FILE_REPLICATION;
318  if(FILE_REPLICATION.isEmpty()){ return; }
319  watcher->removePath(FILE_REPLICATION);
320  //Close down the stream
321  RFSTREAM->setStatus(QTextStream::ReadPastEnd); //let any running process know to stop now
322  delete RFSTREAM; 
323  //Close the file
324  repfile->close();
325  //clear internal variables
326  FILE_REPLICATION.clear();
327  repTotK.clear();
328  lastSize.clear();
329  //qDebug() << "Finished stopping rep file watcher";
330}
331
332double LPWatcher::displayToDoubleK(QString displayNumber){
333  QStringList labels; 
334    labels << "K" << "M" << "G" << "T" << "P" << "E";
335  QString clab = displayNumber.right(1); //last character is the size label
336        displayNumber.chop(1); //remove the label from the number
337  double num = displayNumber.toDouble();
338  //Now format the number properly
339  bool ok = false;
340  for(int i=0; i<labels.length(); i++){
341    if(labels[i] == clab){ ok = true; break; }
342    else{ num = num*1024; } //get ready for the next size
343  }
344  if(!ok){ num = -1; } //could not determine the size
345  return num;
346}
347
348bool LPWatcher::isReplicationRunning(){
349  //Check for the replication PID
350  QDir dir("/var/db/lpreserver");
351  QStringList files = dir.entryList( QStringList() << ".reptask-*", QDir::Files | QDir::Hidden);
352  return ( !files.isEmpty() );
353}
354
355QStringList LPWatcher::listReplicatedPools(){
356  QStringList out;
357  QStringList cmdout = getCmdOutput("lpreserver replicate list");
358  for(int i=0; i<cmdout.length(); i++){
359    if(cmdout[i].contains("->")){
360      QString ds = cmdout[i].section("->",0,0).simplified();
361      if(!ds.isEmpty()){ out << ds; }
362    }
363  }
364  return out;
365}
366
367QStringList LPWatcher::getCmdOutput(QString  cmd){
368  QProcess *proc = new QProcess;
369  proc->setProcessChannelMode(QProcess::MergedChannels);
370  proc->start(cmd);
371  while(!proc->waitForFinished(300)){
372    QCoreApplication::processEvents();
373  }
374  QStringList out = QString(proc->readAllStandardOutput()).split("\n"); 
375  delete proc; 
376  return out;   
377}
378
379// ------------------------------
380//    PRIVATE SLOTS
381// ------------------------------
382void LPWatcher::fileChanged(QString file){
383  if(file == FILE_LOG){ readLogFile(); }
384  else  if(file == FILE_REPLICATION){ readReplicationFile(); }
385}
386
387void LPWatcher::checkPoolStatus(){
388  //Now check zpool status for bad/running statuses
389  QStringList zstat = getCmdOutput("zpool status");
390    //parse the output
391    QString pool, state, timestamp;
392    QStringList cDev, cStat, cMsg, cSummary;
393    //qDebug() << "-----zpool status------\n" << zstat.join("\n");
394    bool newresilver = false; bool newscrub = false; bool newerror = false;
395    for(int i=0; i<zstat.length(); i++){
396      zstat[i] = zstat[i].simplified();
397      if(zstat[i].isEmpty()){ continue; }
398      //qDebug() << zstat[i];
399      if(zstat[i].startsWith("pool:")){ pool = zstat[i].section(":",1,10).simplified(); }
400      else if(zstat[i].startsWith("state:")){ state = zstat[i].section(":",1,10).simplified(); }
401      else if(zstat[i].startsWith("scan:")){
402        //check for scrubs/resilvering progress
403        // ------ SCRUB ------
404        if(zstat[i].contains("scrub")){
405          if(zstat[i].contains(" scrub repaired ")){
406            //Scrub Finished
407            zstat[i]  = zstat[i].replace("\t"," ").simplified();
408            timestamp = zstat[i].section(" ",10,14,QString::SectionSkipEmpty);
409            QString numFixed = zstat[i].section(" ",3,3,QString::SectionSkipEmpty);
410            QString numErr = zstat[i].section(" ",7,7,QString::SectionSkipEmpty);
411            QString timeRun = zstat[i].section(" ",5,5,QString::SectionSkipEmpty);
412            //Scrub finished previously
413            if(numFixed.toInt() > 0){ 
414              if(LOGS.value(60)!="ERROR"){ newscrub=true; }
415              LOGS.insert(60, "ERROR"); 
416              LOGS.insert(62, QString(tr("Scrub repaired %1 bad blocks")).arg(numFixed) );
417              LOGS.insert(63, QString(tr("Scrub repaired %1 blocks in %2 with %3 errors")).arg(numFixed, timeRun, numErr) );
418            }else{ 
419              if(LOGS.contains(60) && LOGS.value(60)!="FINISHED"){ newscrub=true; }
420              LOGS.insert(60,"FINISHED"); 
421              LOGS.insert(62, tr("Scrub completed") );
422              LOGS.insert(63, tr("Scrub completed without needing repairs") );
423            }
424            LOGS.insert(61,pool);
425            LOGS.insert(64, timestamp);
426            LOGS.insert(65, timestamp.section(" ",3,3) );
427            if(timer->interval() != sysCheckTime){ timer->start(sysCheckTime); }
428          }else if(zstat[i].contains(" scrub cancel")){
429            //Scrub was cancelled before finishing
430            zstat[i]  = zstat[i].replace("\t"," ").simplified();
431            timestamp = zstat[i].section(" ",4,8,QString::SectionSkipEmpty);
432            LOGS.insert(60, "FINISHED");
433            LOGS.insert(61, pool);
434            LOGS.insert(62, QString(tr("Scrub cancelled for %1")).arg(pool) );
435            LOGS.insert(63, QString(tr("Scrub cancelled for %1")).arg(pool) );
436            LOGS.insert(64, timestamp);
437            LOGS.insert(65,timestamp.section(" ",3,3) );
438          }else{
439            //Scrub is running - parse the line
440            timestamp = zstat[i].section(" ",5,9,QString::SectionSkipEmpty);
441            i++; QString remain = zstat[i].section(" ",7,7,QString::SectionSkipEmpty);
442            i++; QString percent = zstat[i].section(" ",2,2,QString::SectionSkipEmpty);
443            if(LOGS.value(60) != "RUNNING"){newscrub=true;}
444            LOGS.insert(60,"RUNNING");
445            LOGS.insert(61,pool);
446            LOGS.insert(62, QString(tr("Scrubbing %1: %2 (%3 remaining)")).arg(pool, percent, remain) );
447            LOGS.insert(63, QString(tr("Scrubbing %1: %2 (%3 remaining)")).arg(pool, percent, remain) );
448            LOGS.insert(64, timestamp);
449            LOGS.insert(65, timestamp.section(" ",3,3) );
450            if(timer->interval() != 60000){ timer->start(60000); } //put the timer on a 1 minute refresh since it is running
451          }
452          if(LOGS.contains(50) ){
453            //Only resilvering OR scrub is shown at a time - so remove the resilver info
454            LOGS.remove(50);
455            LOGS.remove(51);
456            LOGS.remove(52);
457            LOGS.remove(53);
458            LOGS.remove(54);
459            LOGS.remove(55);
460          }
461        // --------- RESILVERING -------
462        }else if(zstat[i].contains("resilver in progress")){
463          //Resilvering is currently running
464          timestamp = zstat[i].section(" ",5,9,QString::SectionSkipEmpty);
465          //need info from the next two lines
466          i++; QString timeleft = zstat[i].section(" ",7,7,QString::SectionSkipEmpty);
467          i++; QString percent = zstat[i].section(" ", 2,2,QString::SectionSkipEmpty);
468          //Setup the running re-silvering progress
469          if(LOGS.value(50)!="RUNNING"){newresilver=true; }
470          LOGS.insert(50, "RUNNING");
471          // 51 - need to put the actual device in here (not available on this line)
472          LOGS.insert(52, QString(tr("Resilvering: %1 (%2 remaining)")).arg(percent, timeleft) );
473          if(newresilver){ LOGS.insert(53, QString(tr("Resilvering Started: %1 remaining ")).arg( timeleft) ); }
474          else{ LOGS.insert(53,QString(tr("Resilvering: %1 (%2 remaining)")).arg(percent, timeleft) ); }
475          LOGS.insert(54, timestamp);
476          LOGS.insert(55, timestamp.section(" ",3,3) );
477          if(LOGS.contains(60) ){
478            //Only resilvering OR scrub is shown at a time - so remove the scrub info
479            LOGS.remove(60);
480            LOGS.remove(61);
481            LOGS.remove(62);
482            LOGS.remove(63);
483            LOGS.remove(64);
484            LOGS.remove(65);
485          }
486          if(timer->interval() != 60000){ timer->start(60000); }//put the timer on a 1 minute refresh since it is running
487        }else if(zstat[i].contains("resilvered")){
488          //Resilvering is finished
489          timestamp = zstat[i].section(" ",9,13,QString::SectionSkipEmpty);
490          QString timecomplete = zstat[i].section(" ",4,4,QString::SectionSkipEmpty);
491          QString errors = zstat[i].section(" ", 6,6,QString::SectionSkipEmpty);
492          //Setup the running re-silvering progress
493          if(LOGS.value(50) != "FINISHED" && LOGS.value(50) != "ERROR" ){newresilver=true; } //don't display message for first run
494          if(errors.toInt() > 0){ 
495            LOGS.insert(50, "ERROR");
496            LOGS.insert(52, QString(tr("Resilver completed in %1 with %2 errors")).arg(timecomplete, errors) );
497            LOGS.insert(53, QString(tr("Resilver completed in %1 with %2 errors")).arg(timecomplete, errors) );
498          }else{
499            LOGS.insert(50, "FINISHED");
500            LOGS.insert(52, QString(tr("Resilver completed successfully in %1")).arg(timecomplete) );
501            LOGS.insert(53, QString(tr("Resilver completed successfully in %1")).arg(timecomplete) ); 
502          }
503          // 51 - need to put the actual device in here (not available on this line)
504          LOGS.insert(54, timestamp);
505          LOGS.insert(55, timestamp.section(" ",3,3) );
506          if(LOGS.contains(60) ){
507            //Only resilvering OR scrub is shown at a time - so remove the scrub info
508            LOGS.remove(60);
509            LOGS.remove(61);
510            LOGS.remove(62);
511            LOGS.remove(63);
512            LOGS.remove(64);
513            LOGS.remove(65);
514          }
515          if(timer->interval() != sysCheckTime){ timer->start(sysCheckTime); }
516        }
517      }else if(zstat[i].startsWith("errors:")){
518        if(zstat[i] != "errors: No known data errors"){
519          qDebug() << "New zpool status error line that needs parsing:" << zstat[i];
520        }
521      }else if( state != "ONLINE" || !LOGS.value(50).isEmpty() ){
522        //Check for state/resilvering of all real devices
523        QString msg, summary, status;
524        QString device = zstat[i].section(" ",0,0,QString::SectionSkipEmpty);
525        if(zstat[i].contains("NAME STATE READ")){continue;} //nothing on this header line
526        else if(zstat[i].contains("(resilvering)")){ LOGS.insert(51, device ); continue;}
527        else if(zstat[i].contains("ONLINE")){continue;} //do nothing for this device - it is good
528        else if(zstat[i].contains("OFFLINE")){ continue; } //do nothing - this status must be set manually - it is not a "random" status
529        else if(zstat[i].contains("DEGRADED")){
530          // This should only happen on pools, not actual devices
531          cStat << "DEGRADED";
532          cMsg << tr("The pool is in a degraded state. See additional device error(s).");
533          cSummary << QString(tr("%1 is degraded.")).arg(device);
534          cDev << device;
535        }else if(zstat[i].contains("FAULTED")){ 
536          cStat << "FAULTED";
537          cMsg << tr("The device is faulty, and should be replaced.");
538          cSummary << QString(tr("%1 is faulty.")).arg(device);
539          cDev << device;
540        }else if(zstat[i].contains("REMOVED")){ 
541          cStat << "REMOVED";
542          cMsg << tr("The device was removed, and should be either be re-attached or replaced.");
543          cSummary << QString(tr("%1 was removed.")).arg(device);
544          cDev << device;
545        }else if(zstat[i].contains("UNAVAIL")){ 
546          cStat << "UNAVAILABLE";
547          cMsg << tr("The device is unavailable and should be re-added to the pool.");
548          cSummary << QString(tr("%1 is unavailable.")).arg(device);
549          cDev << device;
550        }
551      }
552    } //end of loop over zpool status lines
553   
554  //Add the critical messages to the hash
555  if(cStat.isEmpty() || (cStat.join(" ").simplified() == "DEGRADED") ){
556    //No errors, or the pool is degraded without any additional errors (usually because of a resilver going on)
557    if(LOGS.contains(30)){
558      LOGS.remove(30);
559      LOGS.remove(31);
560      LOGS.remove(32);
561      LOGS.remove(33);
562      LOGS.remove(34);
563      LOGS.remove(35);
564    }
565  }else{
566    if(LOGS.value(30) != cStat.join(":::") ){ newerror = true; }
567    LOGS.insert(30, cStat.join(":::") );
568    LOGS.insert(31, cDev.join(":::") );
569    LOGS.insert(32, cSummary.join(":::") );
570    LOGS.insert(33, cMsg.join(":::") );
571    LOGS.insert(34, timestamp);
572    LOGS.insert(35, timestamp.section(" ",3,3) );
573  }
574   
575  //Now emit the appropriate signal
576  if(newerror){ emit MessageAvailable("critical"); }
577  else if(newresilver){ emit MessageAvailable("resilver"); }
578  else if(newscrub){ emit MessageAvailable("scrub"); }
579  else{ emit StatusUpdated(); }
580}
Note: See TracBrowser for help on using the repository browser.