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

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

Get the new watcher class up to the same (or slightly more) functionality thatn the old implementation. Now to start adding the new checking for mirroring/resilvering and or zpool errors.

  • Property mode set to 100644
File size: 11.2 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  Within each set:
10    *0 = ID Code (for internal identification as necessary)
11    *1 = dataset (example: tank1)
12    *2 = summary (shortened version of the message - good for titles)
13    *3 = message (full message)
14    *4 = timestamp (full date/time timestamp in readable format)
15    *5 = short timestamp (just time in readable format)
16
17  Valid Internal ID's:
18    SNAPCREATED -> new snapshot created
19    STARTED     -> Task started
20    RUNNING     -> Task running (I.E. status update)
21    FINISHED    -> Task finished
22    ERROR               -> Task failed
23   
24*/
25
26LPWatcher::LPWatcher() : QObject(){
27  //Initialize the path variables
28  FILE_LOG = "/var/log/lpreserver/lpreserver.log";
29  FILE_ERROR="/var/log/lpreserver/error.log";
30  FILE_REPLICATION=""; //this is set automatically based on the log file outputs
31
32  //initialize the watcher and timer
33  watcher = new QFileSystemWatcher(this);
34    connect(watcher, SIGNAL(fileChanged(QString)),this,SLOT(fileChanged(QString)) );
35  timer = new QTimer();
36    timer->setInterval( 600000 ); //10 minute check time
37    connect(timer, SIGNAL(timeout()), this, SLOT(checkErrorFile()) );
38  //initialize the log file reader
39  logfile = new QFile(FILE_LOG, this);
40  LFSTREAM = new QTextStream(logfile);
41  //initialize the replication file reader
42  repfile = new QFile(this);
43}
44
45LPWatcher::~LPWatcher(){
46  //clean up the internal objects
47  delete watcher;
48  delete timer;
49  delete logfile;
50  delete LFSTREAM;
51}
52
53// -----------------------------------
54//    PUBLIC FUNCTIONS
55// -----------------------------------
56void LPWatcher::start(){
57  if(!logfile->exists()){
58    QString dir = FILE_LOG;
59          dir.chop( dir.section("/",-1).count()+1 );
60    if(!QFile::exists(dir)){ system( QString("mkdir -p "+dir).toUtf8() ); }
61    system( QString("touch "+FILE_LOG).toUtf8() );
62  }
63  //Read the current state of the log file
64  logfile->open(QIODevice::ReadOnly | QIODevice::Text);
65  readLogFile(true); //do this quietly the first time through
66  //Now start up the log file watcher
67  watcher->addPath(FILE_LOG);
68  //Now check for any current errors in the LPbackend
69  checkErrorFile();
70  //And start up the error file watcher
71  timer->start();
72}
73
74void LPWatcher::stop(){
75  watcher->removePaths(watcher->files());
76  logfile->close();
77  timer->stop();
78}
79
80QStringList LPWatcher::getMessages(QString type, QStringList msgList){
81  QStringList output;
82  type = type.toLower();
83  //Valid types - "critical"/"running"/"message"
84  //Valid messages - "dataset","message","summary","id", "timestamp", "time"
85  unsigned int base;
86  if(type=="message"){base=10;}
87  else if(type=="replicate"){base=20;}
88  else if(type=="critical"){base=30;}
89  else if(type=="mirror"){base=40;}
90  else{ return output; } //invalid input type
91  //Now fill the output array based upon requested outputs
92  for(int i=0; i<msgList.length(); i++){
93    msgList[i] = msgList[i].toLower();
94    if(msgList[i]=="id" && LOGS.contains(base)){ output << LOGS[base]; }
95    else if(msgList[i]=="dataset" && LOGS.contains(base+1)){ output << LOGS[base+1]; }
96    else if(msgList[i]=="summary" && LOGS.contains(base+2)){ output << LOGS[base+2]; }
97    else if(msgList[i]=="message" && LOGS.contains(base+3)){ output << LOGS[base+3]; }
98    else if(msgList[i]=="timestamp" && LOGS.contains(base+4)){ output << LOGS[base+4]; }
99    else if(msgList[i]=="time" && LOGS.contains(base+5)){ output << LOGS[base+5]; }
100    else{ output << ""; }
101  }
102  //Return the output list
103  return output;
104}
105
106QStringList LPWatcher::getAllCurrentMessages(){
107  //Useful for quickly displaying all latest messages in a tooltip or summary
108  QStringList output;
109  if(LOGS.contains(12) && LOGS.contains(14)){ output << LOGS[14]+" -- "+LOGS[12]; }
110  if(LOGS.contains(22) && LOGS.contains(24)){ output << LOGS[24]+" -- "+LOGS[22]; }
111  if(LOGS.contains(32) && LOGS.contains(34)){ output << LOGS[34]+" -- "+LOGS[32]; }
112  if(LOGS.contains(42) && LOGS.contains(44)){ output << LOGS[44]+" -- "+LOGS[42]; }
113  return output;
114}
115
116bool LPWatcher::isRunning(){
117  if(LOGS.value(20) == "STARTED" || LOGS.value(20) == "RUNNING"){ return true; }
118  else if(LOGS.value(40) == "STARTED" || LOGS.value(40) == "RUNNING"){ return true; }
119  else{ return false; }
120}
121
122bool LPWatcher::hasError(){
123  return (LOGS.value(20)=="ERROR" || LOGS.contains(30) || LOGS.value(40)=="ERROR");
124}
125
126// -------------------------------------
127//    PRIVATE FUNCTIONS
128// -------------------------------------
129void LPWatcher::readLogFile(bool quiet){
130  QTextStream in(logfile);
131  while(!LFSTREAM->atEnd()){
132    QString log = LFSTREAM->readLine();
133    //Divide up the log into it's sections
134    QString timestamp = log.section(":",0,2).simplified();
135    QString time = timestamp.section(" ",3,3).simplified();
136    QString message = log.section(":",3,3).toLower().simplified();
137    QString dev = log.section(":",4,4).simplified(); //dataset/snapshot/nothing
138    //Now decide what to do/show because of the log message
139    qDebug() << "New Log Message:" << log;
140    if(message.contains("creating snapshot")){
141      dev = message.section(" ",-1).simplified();
142      //Setup the status of the message
143      LOGS.insert(10,"SNAPCREATED");
144      LOGS.insert(11,dev); //dataset
145      LOGS.insert(12, tr("New Snapshot") ); //summary
146      LOGS.insert(13, QString(tr("Creating snapshot for %1")).arg(dev) );
147      LOGS.insert(14, timestamp); //full timestamp
148      LOGS.insert(15, time); // time only
149      if(!quiet){ emit MessageAvailable("message"); }
150    }else if(message.contains("starting replication")){
151      //Setup the file watcher for this new log file
152      FILE_REPLICATION = dev;
153      startRepFileWatcher();
154      //Set the appropriate status variables
155      LOGS.insert(20,"REPSTARTED");
156      // 21 - dataset set on update ping
157      LOGS.insert(22, tr("Replication Started") ); //summary
158      // 23 - Full message set on update ping
159      LOGS.insert(24, timestamp); //full timestamp
160      LOGS.insert(25, time); // time only
161      if(!quiet){ emit MessageAvailable("replication"); }
162    }else if(message.contains("finished replication")){
163      stopRepFileWatcher();
164      dev = message.section(" ",-1).simplified();
165      //Now set the status of the process
166      LOGS.insert(20,"REPFINISHED");
167      LOGS.insert(21,dev); //dataset
168      LOGS.insert(22, tr("Finished Replication") ); //summary
169      LOGS.insert(23, QString(tr("Finished replication for %1")).arg(dev) );
170      LOGS.insert(24, timestamp); //full timestamp
171      LOGS.insert(25, time); // time only     
172      if(!quiet){ emit MessageAvailable("replication"); }
173    }else if( message.contains("FAILED replication") ){
174      stopRepFileWatcher();
175      //Now set the status of the process
176      dev = message.section(" ",-1).simplified();
177      QString file = log.section("LOGFILE:",1,1).simplified();
178      QString tt = QString(tr("Replication Failed for %1")).arg(dev) +"\n"+ QString(tr("Logfile available at: %1")).arg(file);
179      LOGS.insert(20,"REPERROR");
180      LOGS.insert(21,dev); //dataset
181      LOGS.insert(22, tr("Replication Failed") ); //summary
182      LOGS.insert(23, tt );
183      LOGS.insert(24, timestamp); //full timestamp
184      LOGS.insert(25, time); // time only     
185      if(!quiet){ emit MessageAvailable("replication"); }
186    }
187         
188  }
189}
190
191void LPWatcher::readReplicationFile(bool quiet){
192  QString stat;
193  while( !RFSTREAM->atEnd() ){ 
194    QString line = RFSTREAM->readLine(); 
195    if(line.contains("total estimated size")){ repTotK = line.section(" ",-1).simplified(); } //save the total size to replicate
196    else if(line.startsWith("send from ")){}
197    else if(line.startsWith("TIME ")){}
198    else{ stat = line; } //only save the relevant/latest status line
199  }
200  if(stat.isEmpty()){
201    qDebug() << "New Status Message:" << stat;
202    //Divide up the status message into sections
203    stat.replace("\t"," ");
204    QString dataset = stat.section(" ",2,2,QString::SectionSkipEmpty).section("/",0,0).simplified();
205    QString cSize = stat.section(" ",1,1,QString::SectionSkipEmpty);
206    //Now Setup the tooltip
207    if(cSize != lastSize){ //don't update the info if the same size info
208      QString percent;
209      if(!repTotK.isEmpty()){
210        //calculate the percentage
211        double tot = displayToDoubleK(repTotK);
212        double c = displayToDoubleK(cSize);
213        if( tot!=-1 & c!=-1){
214          double p = (c*100)/tot;
215          p = int(p*10)/10.0; //round to 1 decimel places
216          percent = QString::number(p) + "%";
217        }
218      }
219      if(repTotK.isEmpty()){ repTotK = "??"; }
220      //Format the info string
221      QString status = cSize+"/"+repTotK;
222      if(!percent.isEmpty()){ status.append(" ("+percent+")"); }
223      QString txt = QString(tr("Replicating %1: %2")).arg(dataset, status);
224      lastSize = cSize; //save the current size for later
225      //Now set the current process status
226      LOGS.insert(20,"RUNNING");
227      LOGS.insert(21,dataset);
228      LOGS.insert(23,txt);
229      if(!quiet){ emit MessageAvailable("replication"); }
230    }
231  }
232}
233
234void LPWatcher::startRepFileWatcher(){
235  if(FILE_REPLICATION.isEmpty()){ return; }
236  if(!QFile::exists(FILE_REPLICATION)){ system( QString("touch "+FILE_REPLICATION).toUtf8() ); }
237  repfile->setFileName(FILE_REPLICATION);
238  repfile->open(QIODevice::ReadOnly | QIODevice::Text);
239  RFSTREAM = new QTextStream(repfile);
240  watcher->addPath(FILE_REPLICATION);
241}
242
243void LPWatcher::stopRepFileWatcher(){
244  if(FILE_REPLICATION.isEmpty()){ return; }
245  watcher->removePath(FILE_REPLICATION);
246  repfile->close();
247  delete RFSTREAM;
248  FILE_REPLICATION.clear();
249  repTotK.clear();
250  lastSize.clear();
251}
252
253double LPWatcher::displayToDoubleK(QString displayNumber){
254  QStringList labels; 
255    labels << "K" << "M" << "G" << "T" << "P" << "E";
256  QString clab = displayNumber.right(1); //last character is the size label
257        displayNumber.chop(1); //remove the label from the number
258  double num = displayNumber.toDouble();
259  //Now format the number properly
260  bool ok = false;
261  for(int i=0; i<labels.length(); i++){
262    if(labels[i] == clab){ ok = true; break; }
263    else{ num = num*1024; } //get ready for the next size
264  }
265  if(!ok){ num = -1; } //could not determine the size
266  return num;
267}
268
269// ------------------------------
270//    PRIVATE SLOTS
271// ------------------------------
272void LPWatcher::fileChanged(QString file){
273  if(file == FILE_LOG){ readLogFile(); }
274  else  if(file == FILE_REPLICATION){ readReplicationFile(); }
275}
276
277void LPWatcher::checkErrorFile(){
278  return;
279  if(QFile::exists(FILE_ERROR)){
280    //Read the file to determine the cause of the error
281    QString msg, id, summary, timestamp, time, dataset;
282    QFile file(FILE_ERROR);
283      file.open(QIODevice::ReadOnly | QIODevice::Text);
284      QTextStream in(&file);
285      qDebug() << "Error File Parsing not implemented yet. \n - File Contents:";
286      while(!in.atEnd()){
287        QString line = in.readLine();
288        //Now look for key information on this line
289        qDebug() << line;
290      }
291    //Now set the status and emit the signal
292    LOGS.insert(30, id);
293    LOGS.insert(31, dataset); //dataset
294    LOGS.insert(32, summary ); //summary
295    LOGS.insert(33, msg ); //message
296    LOGS.insert(34, timestamp); //full timestamp
297    LOGS.insert(35, time); // time only   
298    emit MessageAvailable("critical");
299  }
300}
Note: See TracBrowser for help on using the repository browser.