source: src-qt4/life-preserver/LPWatcher.cpp @ 490345a

9.2-releasereleng/10.0releng/10.0.1
Last change on this file since 490345a was 490345a, checked in by Ken Moore <ken@…>, 7 months ago

Add a check for the replication PID file when starting up the replication file watcher. This should prevent possible watcher conflicts down the road.

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