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

releng/10.0
Last change on this file since d45a9eb was d45a9eb, checked in by Kris Moore <kris@…>, 11 months ago

Couple quick fixes to the life-preserver tray:
1) Make sure that the new single-instance functionality will not startup a prompt to open the main GUI for 30 seconds after the tray gets launched. This prevents "double-taps" on user login when the DE also starts up an instance automatically.
2) Whenever the system check timer goes off, also check that the lpreserver log file is being watched, and re-initialize the log file watcher if necessary.

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