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

releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since 1f1481c9 was 1f1481c9, checked in by Ken Moore <ken@…>, 10 months ago

Fix a bug in the LP tray "messages" dialog use that was wiping out the non-error messages.

  • Property mode set to 100644
File size: 24.5 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"/"replication"/"mirror"/"resilver"/"scrub"
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.