void LPWatcher::checkPoolStatus(){ //Now check zpool status for bad/running statuses QStringList zstat = getCmdOutput("zpool status"); //parse the output QString pool, state, timestamp; QStringList cDev, cStat, cMsg, cSummary; //qDebug() << "-----zpool status------\n" << zstat.join("\n"); bool newresilver = false; bool newscrub = false; bool newerror = false; for(int i=0; i<zstat.length(); i++){ zstat[i] = zstat[i].simplified(); if(zstat[i].isEmpty()){ continue; } //qDebug() << zstat[i]; if(zstat[i].startsWith("pool:")){ pool = zstat[i].section(":",1,10).simplified(); } else if(zstat[i].startsWith("state:")){ state = zstat[i].section(":",1,10).simplified(); } else if(zstat[i].startsWith("scan:")){ //check for scrubs/resilvering progress // ------ SCRUB ------ if(zstat[i].contains("scrub")){ if(zstat[i].contains(" scrub repaired ")){ //Scrub Finished zstat[i] = zstat[i].replace("\t"," ").simplified(); timestamp = zstat[i].section(" ",10,14,QString::SectionSkipEmpty); QString numFixed = zstat[i].section(" ",3,3,QString::SectionSkipEmpty); QString numErr = zstat[i].section(" ",7,7,QString::SectionSkipEmpty); QString timeRun = zstat[i].section(" ",5,5,QString::SectionSkipEmpty); //Scrub finished previously if(numFixed.toInt() > 0){ if(LOGS.value(60)!="ERROR"){ newscrub=true; } LOGS.insert(60, "ERROR"); LOGS.insert(62, QString(tr("Scrub repaired %1 bad blocks")).arg(numFixed) ); LOGS.insert(63, QString(tr("Scrub repaired %1 blocks in %2 with %3 errors")).arg(numFixed, timeRun, numErr) ); }else{ if(LOGS.contains(60) && LOGS.value(60)!="FINISHED"){ newscrub=true; } LOGS.insert(60,"FINISHED"); LOGS.insert(62, tr("Scrub completed") ); LOGS.insert(63, tr("Scrub completed without needing repairs") ); } LOGS.insert(61,pool); LOGS.insert(64, timestamp); LOGS.insert(65, timestamp.section(" ",3,3) ); if(timer->interval() != sysCheckTime){ timer->start(sysCheckTime); } }else if(zstat[i].contains(" scrub cancel")){ //Scrub was cancelled before finishing zstat[i] = zstat[i].replace("\t"," ").simplified(); timestamp = zstat[i].section(" ",4,8,QString::SectionSkipEmpty); LOGS.insert(60, "FINISHED"); LOGS.insert(61, pool); LOGS.insert(62, QString(tr("Scrub cancelled for %1")).arg(pool) ); LOGS.insert(63, QString(tr("Scrub cancelled for %1")).arg(pool) ); LOGS.insert(64, timestamp); LOGS.insert(65,timestamp.section(" ",3,3) ); }else{ //Scrub is running - parse the line timestamp = zstat[i].section(" ",5,9,QString::SectionSkipEmpty); i++; QString remain = zstat[i].section(" ",7,7,QString::SectionSkipEmpty); i++; QString percent = zstat[i].section(" ",2,2,QString::SectionSkipEmpty); if(LOGS.value(60) != "RUNNING"){newscrub=true;} LOGS.insert(60,"RUNNING"); LOGS.insert(61,pool); LOGS.insert(62, QString(tr("Scrubbing %1: %2 (%3 remaining)")).arg(pool, percent, remain) ); LOGS.insert(63, QString(tr("Scrubbing %1: %2 (%3 remaining)")).arg(pool, percent, remain) ); LOGS.insert(64, timestamp); LOGS.insert(65, timestamp.section(" ",3,3) ); if(timer->interval() != 60000){ timer->start(60000); } //put the timer on a 1 minute refresh since it is running } if(LOGS.contains(50) ){ //Only resilvering OR scrub is shown at a time - so remove the resilver info LOGS.remove(50); LOGS.remove(51); LOGS.remove(52); LOGS.remove(53); LOGS.remove(54); LOGS.remove(55); } // --------- RESILVERING ------- }else if(zstat[i].contains("resilver in progress")){ //Resilvering is currently running timestamp = zstat[i].section(" ",5,9,QString::SectionSkipEmpty); //need info from the next two lines i++; QString timeleft = zstat[i].section(" ",7,7,QString::SectionSkipEmpty); i++; QString percent = zstat[i].section(" ", 2,2,QString::SectionSkipEmpty); //Setup the running re-silvering progress if(LOGS.value(50)!="RUNNING"){newresilver=true; } LOGS.insert(50, "RUNNING"); // 51 - need to put the actual device in here (not available on this line) LOGS.insert(52, QString(tr("Resilvering: %1 (%2 remaining)")).arg(percent, timeleft) ); if(newresilver){ LOGS.insert(53, QString(tr("Resilvering Started: %1 remaining ")).arg( timeleft) ); } else{ LOGS.insert(53,QString(tr("Resilvering: %1 (%2 remaining)")).arg(percent, timeleft) ); } LOGS.insert(54, timestamp); LOGS.insert(55, timestamp.section(" ",3,3) ); if(LOGS.contains(60) ){ //Only resilvering OR scrub is shown at a time - so remove the scrub info LOGS.remove(60); LOGS.remove(61); LOGS.remove(62); LOGS.remove(63); LOGS.remove(64); LOGS.remove(65); } if(timer->interval() != 60000){ timer->start(60000); }//put the timer on a 1 minute refresh since it is running }else if(zstat[i].contains("resilvered")){ //Resilvering is finished timestamp = zstat[i].section(" ",9,13,QString::SectionSkipEmpty); QString timecomplete = zstat[i].section(" ",4,4,QString::SectionSkipEmpty); QString errors = zstat[i].section(" ", 6,6,QString::SectionSkipEmpty); //Setup the running re-silvering progress if(LOGS.value(50) != "FINISHED" && LOGS.value(50) != "ERROR" ){newresilver=true; } //don't display message for first run if(errors.toInt() > 0){ LOGS.insert(50, "ERROR"); LOGS.insert(52, QString(tr("Resilver completed in %1 with %2 errors")).arg(timecomplete, errors) ); LOGS.insert(53, QString(tr("Resilver completed in %1 with %2 errors")).arg(timecomplete, errors) ); }else{ LOGS.insert(50, "FINISHED"); LOGS.insert(52, QString(tr("Resilver completed successfully in %1")).arg(timecomplete) ); LOGS.insert(53, QString(tr("Resilver completed successfully in %1")).arg(timecomplete) ); } // 51 - need to put the actual device in here (not available on this line) LOGS.insert(54, timestamp); LOGS.insert(55, timestamp.section(" ",3,3) ); if(LOGS.contains(60) ){ //Only resilvering OR scrub is shown at a time - so remove the scrub info LOGS.remove(60); LOGS.remove(61); LOGS.remove(62); LOGS.remove(63); LOGS.remove(64); LOGS.remove(65); } if(timer->interval() != sysCheckTime){ timer->start(sysCheckTime); } } }else if(zstat[i].startsWith("errors:")){ if(zstat[i] != "errors: No known data errors"){ qDebug() << "New zpool status error line that needs parsing:" << zstat[i]; } }else if( state != "ONLINE" || !LOGS.value(50).isEmpty() ){ //Check for state/resilvering of all real devices QString msg, summary, status; QString device = zstat[i].section(" ",0,0,QString::SectionSkipEmpty); if(zstat[i].contains("NAME STATE READ")){continue;} //nothing on this header line else if(zstat[i].contains("(resilvering)")){ LOGS.insert(51, device ); continue;} else if(zstat[i].contains("ONLINE")){continue;} //do nothing for this device - it is good else if(zstat[i].contains("OFFLINE")){ continue; } //do nothing - this status must be set manually - it is not a "random" status else if(zstat[i].contains("DEGRADED")){ // This should only happen on pools, not actual devices cStat << "DEGRADED"; cMsg << tr("The pool is in a degraded state. See additional device error(s)."); cSummary << QString(tr("%1 is degraded.")).arg(device); cDev << device; }else if(zstat[i].contains("FAULTED")){ cStat << "FAULTED"; cMsg << tr("The device is faulty, and should be replaced."); cSummary << QString(tr("%1 is faulty.")).arg(device); cDev << device; }else if(zstat[i].contains("REMOVED")){ cStat << "REMOVED"; cMsg << tr("The device was removed, and should be either be re-attached or replaced."); cSummary << QString(tr("%1 was removed.")).arg(device); cDev << device; }else if(zstat[i].contains("UNAVAIL")){ cStat << "UNAVAILABLE"; cMsg << tr("The device is unavailable and should be re-added to the pool."); cSummary << QString(tr("%1 is unavailable.")).arg(device); cDev << device; } } } //end of loop over zpool status lines //Add the critical messages to the hash if(cStat.isEmpty() || (cStat.join(" ").simplified() == "DEGRADED") ){ //No errors, or the pool is degraded without any additional errors (usually because of a resilver going on) if(LOGS.contains(30)){ LOGS.remove(30); LOGS.remove(31); LOGS.remove(32); LOGS.remove(33); LOGS.remove(34); LOGS.remove(35); } }else{ if(LOGS.value(30) != cStat.join(":::") ){ newerror = true; } LOGS.insert(30, cStat.join(":::") ); LOGS.insert(31, cDev.join(":::") ); LOGS.insert(32, cSummary.join(":::") ); LOGS.insert(33, cMsg.join(":::") ); LOGS.insert(34, timestamp); LOGS.insert(35, timestamp.section(" ",3,3) ); } //Now emit the appropriate signal if(newerror){ emit MessageAvailable("critical"); } else if(newresilver){ emit MessageAvailable("resilver"); } else if(newscrub){ emit MessageAvailable("scrub"); } else{ emit StatusUpdated(); } }
//PUBLIC LPTray::LPTray() : QSystemTrayIcon() { qDebug() << "Starting up Life-preserver Tray..."; //Start up the log file watcher and connect the signals/slots watcher = new LPWatcher(); connect(watcher,SIGNAL(MessageAvailable(QString)),this,SLOT(watcherMessage(QString)) ); connect(watcher,SIGNAL(StatusUpdated()),this,SLOT(watcherMessage()) ); //Load the tray settings file settings = new QSettings(QSettings::UserScope,"PCBSD", "life-preserver-tray"); //Create the notification option widgets nShowAll = new QRadioButton(tr("Show all")); naAll = new QWidgetAction(this); naAll->setDefaultWidget(nShowAll); nShowError = new QRadioButton(tr("Warnings Only")); naErr = new QWidgetAction(this); naErr->setDefaultWidget(nShowError); nShowNone = new QRadioButton(tr("None")); naNone = new QWidgetAction(this); naNone->setDefaultWidget(nShowNone); //Create notification menu notificationMenu = new QMenu(tr("Popup Settings")); notificationMenu->setIcon( QIcon(":/images/configure.png") ); notificationMenu->addAction(naAll); notificationMenu->addAction(naErr); notificationMenu->addAction(naNone); //Activate the proper notification setting widget QString popset = settings->value("popup-policy", "").toString(); qDebug() << "Current Popup Policy:" << popset; if(popset=="all") { nShowAll->setChecked(true); popupPolicy = 2; } else if(popset=="errors") { nShowError->setChecked(true); popupPolicy = 1; } else if(popset=="none") { nShowNone->setChecked(true); popupPolicy = 0; } else { nShowError->setChecked(true); settings->setValue("popup-policy","errors"); //save the proper setting popupPolicy = 1; } //Now connect the popup settings signals/slots connect(nShowAll, SIGNAL(clicked()), this, SLOT(changePopupPolicy()) ); connect(nShowError, SIGNAL(clicked()), this, SLOT(changePopupPolicy()) ); connect(nShowNone, SIGNAL(clicked()), this, SLOT(changePopupPolicy()) ); //Setup the context menu menu = new QMenu; menu->addAction(QIcon(":/images/tray-icon-idle.png"),tr("Open Life Preserver"),this,SLOT(startGUI()) ); menu->addSeparator(); menu->addAction(QIcon(":/images/backup-failed.png"),tr("View Messages"),this,SLOT(startMessageDialog()) ); menu->addMenu(notificationMenu); menu->addAction(QIcon(":/images/refresh.png"),tr("Refresh Tray"),this,SLOT(refreshStatus()) ); menu->addSeparator(); menu->addAction(QIcon(":/images/application-exit.png"),tr("Close Tray"),this,SLOT(slotClose()) ); this->setContextMenu(menu); //Setup initial icon for the tray this->setIcon( QIcon(":/images/tray-icon-idle.png") ); //Create the messages GUI msgdlg = new LPMessages(); //connect other signals/slots connect(this, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(slotTrayClicked(QSystemTrayIcon::ActivationReason)) ); //Start up the watcher watcher->start(); updateTrayIcon(); updateToolTip(); }