]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/job/job.cpp
Removed old sd plugins which doesn't work anymore.
[bacula/bacula] / bacula / src / qt-console / job / job.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28  
29 #include "bat.h"
30 #include "job.h"
31 #include "util/fmtwidgetitem.h"
32 #include "mediainfo/mediainfo.h"
33 #include "run/run.h"
34
35 Job::Job(QString &jobId, QTreeWidgetItem *parentTreeWidgetItem) : Pages()
36 {
37    setupUi(this);
38    pgInitialize(tr("Job"), parentTreeWidgetItem);
39    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
40    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/joblog.png")));
41    m_cursor = new QTextCursor(textJobLog->document());
42
43    m_jobId = jobId;
44    m_timer = NULL;
45    getFont();
46
47    connect(pbRefresh, SIGNAL(clicked()), this, SLOT(populateAll()));
48    connect(pbDelete, SIGNAL(clicked()), this, SLOT(deleteJob()));
49    connect(pbCancel, SIGNAL(clicked()), this, SLOT(cancelJob()));
50    connect(pbRun, SIGNAL(clicked()), this, SLOT(rerun()));
51    connect(list_Volume, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(showInfoVolume(QListWidgetItem *)));
52
53    populateAll();
54    dockPage();
55    setCurrent();
56 }
57
58 void Job::rerun()
59 {
60    new runPage(label_Name->text(),
61                label_Level->text(),
62                label_Pool->text(),
63                QString(""),              // storage
64                label_Client->text(),
65                label_FileSet->text());
66 }
67
68 void Job::showInfoVolume(QListWidgetItem *item)
69 {
70    QString s= item->text();
71    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
72
73    MediaInfo *m = new MediaInfo(pageSelectorTreeWidgetItem, s);
74    connect(m, SIGNAL(destroyed()), this, SLOT(populateTree()));
75 }
76
77 void Job::deleteJob()
78 {
79    if (QMessageBox::warning(this, "Bat",
80       tr("Are you sure you want to delete??  !!!.\n"
81 "This delete command is used to delete a Job record and all associated catalog"
82 " records that were created. This command operates only on the Catalog"
83 " database and has no effect on the actual data written to a Volume. This"
84 " command can be dangerous and we strongly recommend that you do not use"
85 " it unless you know what you are doing.  The Job and all its associated"
86 " records (File and JobMedia) will be deleted from the catalog."
87       "Press OK to proceed with delete operation.?"),
88       QMessageBox::Ok | QMessageBox::Cancel)
89       == QMessageBox::Cancel) { return; }
90
91    QString cmd("delete job jobid=");
92    cmd += m_jobId;
93    consoleCommand(cmd, false);
94    closeStackPage();
95 }
96
97 void Job::cancelJob()
98 {
99    if (QMessageBox::warning(this, "Bat",
100                             tr("Are you sure you want to cancel this job?"),
101                             QMessageBox::Ok | QMessageBox::Cancel)
102        == QMessageBox::Cancel) { return; }
103
104    QString cmd("cancel jobid=");
105    cmd += m_jobId;
106    consoleCommand(cmd, false);
107 }
108
109 void Job::getFont()
110 {
111    QFont font = textJobLog->font();
112
113    QString dirname;
114    m_console->getDirResName(dirname);
115    QSettings settings(dirname, "bat");
116    settings.beginGroup("Console");
117    font.setFamily(settings.value("consoleFont", "Courier").value<QString>());
118    font.setPointSize(settings.value("consolePointSize", 10).toInt());
119    font.setFixedPitch(settings.value("consoleFixedPitch", true).toBool());
120    settings.endGroup();
121    textJobLog->setFont(font);
122 }
123
124 void Job::populateAll()
125 {
126 // Pmsg0(50, "populateAll()\n");
127    populateText();
128    populateForm();
129    populateVolumes();
130 }
131
132 /*
133  * Populate the text in the window
134  * TODO: Just append new text instead of clearing the window
135  */
136 void Job::populateText()
137 {
138    textJobLog->clear();
139    QString query;
140    query = "SELECT Time, LogText FROM Log WHERE JobId='" + m_jobId + "' order by Time";
141
142    /* This could be a log item */
143    if (mainWin->m_sqlDebug) {
144       Pmsg1(000, "Log query cmd : %s\n", query.toUtf8().data());
145    }
146   
147    QStringList results;
148    if (m_console->sql_cmd(query, results)) {
149
150       if (!results.size()) {
151          QMessageBox::warning(this, tr("Bat"),
152             tr("There were no results!\n"
153                "It is possible you may need to add \"catalog = all\" "
154                "to the Messages resource for this job.\n"), QMessageBox::Ok);
155          return;
156       } 
157
158       QString jobstr("JobId "); /* FIXME: should this be translated ? */
159       jobstr += m_jobId;
160
161       QString htmlbuf("<html><body><pre>");
162   
163       /* Iterate through the lines of results. */
164       QString field;
165       QStringList fieldlist;
166       QString lastTime;
167       QString lastSvc;
168       foreach (QString resultline, results) {
169          fieldlist = resultline.split("\t");
170          
171          if (fieldlist.size() < 2)
172             continue;
173
174          QString curTime = fieldlist[0].trimmed();
175
176          field = fieldlist[1].trimmed();
177          int colon = field.indexOf(":");
178          if (colon > 0) {
179             /* string is like <service> <jobId xxxx>: ..." 
180              * we split at ':' then remove the jobId xxxx string (always the same) */ 
181             QString curSvc(field.left(colon).replace(jobstr,"").trimmed());
182             if (curSvc == lastSvc  && curTime == lastTime) {
183                curTime.clear();
184                curSvc.clear(); 
185             } else {
186                lastTime = curTime;
187                lastSvc = curSvc;
188             }
189 //          htmlbuf += "<td>" + curTime + "</td>";
190             htmlbuf += "\n" + curSvc + " ";
191
192             /* rest of string is marked as pre-formatted (here trimming should
193              * be avoided, to preserve original formatting) */
194             QString msg(field.mid(colon+2));
195             if (msg.startsWith( tr("Error:")) ) { /* FIXME: should really be translated ? */
196                /* error msg, use a specific class */
197                htmlbuf += "</pre><pre class=err>" + msg + "</pre><pre>";
198             } else {
199                htmlbuf += msg ;
200             }
201          } else {
202             /* non standard string, place as-is */
203             if (curTime == lastTime) {
204                curTime.clear();
205             } else {
206                lastTime = curTime;
207             }
208 //          htmlbuf += "<td>" + curTime + "</td>";
209             htmlbuf += "\n" + field ;
210          }
211   
212       } /* foreach resultline */
213
214       htmlbuf += "</pre></body></html>";
215
216       /* full text ready. Here a custom sheet is used to align columns */
217       QString logSheet(".err {color:#FF0000;}");
218       textJobLog->document()->setDefaultStyleSheet(logSheet);
219       textJobLog->document()->setHtml(htmlbuf); 
220       textJobLog->moveCursor(QTextCursor::Start);
221
222    } /* if results from query */
223   
224 }
225
226
227 void Job::updateRunInfo()
228 {
229    QString cmd;
230    QStringList results;
231    QStringList lst;
232    bool parseit=false;
233
234    cmd = QString(".status client=\"" + m_client + "\" running");
235 /*
236  *  JobId 5 Job backup.2010-12-21_09.28.17_03 is running.
237  *   VSS Full Backup Job started: 21-Dec-10 09:28
238  *   Files=4 Bytes=610,976 Bytes/sec=87,282 Errors=0
239  *   Files Examined=4
240  *   Processing file: /tmp/regress/build/po/de.po
241  *   SDReadSeqNo=5 fd=5
242  *
243  *  Or
244  *  JobId=5
245  *  Job=backup.2010-12-21_09.28.17_03
246  *  VSS=1
247  *  Files=4
248  *  Bytes=610976
249  *
250  */
251    QRegExp jobline("(JobId) (\\d+) Job ");
252    QRegExp itemline("([\\w /]+)[:=]\\s*(.+)");
253    QRegExp oldline("Files=([\\d,]+) Bytes=([\\d,]+) Bytes/sec=([\\d,]+) Errors=([\\d,]+)");
254    QString com(",");
255    QString empty("");
256    
257    if (m_console->dir_cmd(cmd, results)) {
258       foreach (QString mline, results) {
259          foreach (QString line, mline.split("\n")) { 
260             line = line.trimmed();
261             if (oldline.indexIn(line) >= 0) {
262                if (parseit) {
263                   lst = oldline.capturedTexts();
264                   label_JobErrors->setText(lst[4]);
265                   label_Speed->setText(convertBytesSI(lst[3].replace(com, empty).toULongLong())+"/s");
266                   label_JobFiles->setText(lst[1]);
267                   label_JobBytes->setText(convertBytesSI(lst[2].replace(com, empty).toULongLong()));
268                }
269                continue;
270
271             } else if (jobline.indexIn(line) >= 0) {
272                lst = jobline.capturedTexts();
273                lst.removeFirst();
274
275             } else if (itemline.indexIn(line) >= 0) {
276                lst = itemline.capturedTexts();
277                lst.removeFirst();
278
279             } else {
280                if (mainWin->m_miscDebug) 
281                   Pmsg1(0, "bad line=%s\n", line.toUtf8().data());
282                continue;
283             }
284             if (lst.count() < 2) {
285                if (mainWin->m_miscDebug) 
286                   Pmsg2(0, "bad line=%s count=%d\n", line.toUtf8().data(), lst.count());
287             }
288             if (lst[0] == "JobId") {
289                if (lst[1] == m_jobId) {
290                   parseit = true;
291                } else {
292                   parseit = false;
293                }
294             }
295             if (!parseit) {
296                continue;
297             }
298             
299 //         } else if (lst[0] == "Job") {
300 //            grpRun->setTitle(lst[1]);
301             
302 //               
303 //         } else if (lst[0] == "VSS") {
304
305 //         } else if (lst[0] == "Level") {
306 //            Info->setText(lst[1]);
307 //
308 //         } else if (lst[0] == "JobType") {
309 //
310 //         } else if (lst[0] == "JobStarted") {
311 //            Started->setText(lst[1]);
312
313             if (lst[0] == "Errors") {
314                label_JobErrors->setText(lst[1]);
315                
316             } else if (lst[0] == "Bytes/sec") {
317                label_Speed->setText(convertBytesSI(lst[1].toULongLong())+"/s");
318                
319             } else if (lst[0] == "Files") {
320                label_JobFiles->setText(lst[1]);
321                
322             } else if (lst[0] == "Bytes") {
323                label_JobBytes->setText(convertBytesSI(lst[1].toULongLong()));
324                
325             } else if (lst[0] == "Files Examined") {
326                label_FilesExamined->setText(lst[1]);
327                
328             } else if (lst[0] == "Processing file") {
329                label_CurrentFile->setText(lst[1]);
330             }
331          }
332       }
333    }
334 }
335
336 /*
337  * Populate the text in the window
338  */
339 void Job::populateForm()
340 {
341    QString stat, err;
342    char buf[256];
343    QString query = 
344       "SELECT JobId, Job.Name, Level, Client.Name, Pool.Name, FileSet,"
345       "SchedTime, StartTime, EndTime, EndTime-StartTime AS Duration, "
346       "JobBytes, JobFiles, JobErrors, JobStatus, PurgedFiles "
347       "FROM Job JOIN Client USING (ClientId) "
348         "LEFT JOIN Pool ON (Job.PoolId = Pool.PoolId) "
349         "LEFT JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId)"
350       "WHERE JobId=" + m_jobId; 
351    QStringList results;
352    if (m_console->sql_cmd(query, results)) {
353       QString resultline, duration;
354       QStringList fieldlist;
355
356       foreach (resultline, results) { // should have only one result
357          fieldlist = resultline.split("\t");
358          QStringListIterator fld(fieldlist);
359          label_JobId->setText(fld.next());
360          label_Name->setText(fld.next());
361          
362          label_Level->setText(job_level_to_str(fld.next()[0].toAscii()));
363
364          m_client = fld.next();
365          label_Client->setText(m_client);
366          label_Pool->setText(fld.next());
367          label_FileSet->setText(fld.next());
368          label_SchedTime->setText(fld.next());
369          label_StartTime->setText(fld.next());
370          label_EndTime->setText(fld.next());
371          duration = fld.next();
372          /* 
373           * Note: if we have a negative duration, it is because the EndTime
374           *  is zero (i.e. the Job is still running).  We should use 
375           *  duration = StartTime - current_time
376           */
377          if (duration.left(1) == "-") {
378             duration = "0.0";
379          }
380          label_Duration->setText(duration);
381
382          label_JobBytes->setText(convertBytesSI(fld.next().toULongLong()));
383          label_JobFiles->setText(fld.next());
384          err = fld.next();
385          label_JobErrors->setText(err);
386
387          stat = fld.next();
388          if (stat == "T" && err.toInt() > 0) {
389             stat = "W";
390          }
391          if (stat == "R") {
392             pbDelete->setVisible(false);
393             pbCancel->setVisible(true);
394             grpRun->setVisible(true);
395             if (!m_timer) {
396                m_timer = new QTimer(this);
397                connect(m_timer, SIGNAL(timeout()), this, SLOT(populateAll()));
398                m_timer->start(30000);
399             }
400             updateRunInfo();
401          } else {
402             pbDelete->setVisible(true);
403             pbCancel->setVisible(false);
404             grpRun->setVisible(false);
405             if (m_timer) {
406                m_timer->stop();
407                delete m_timer;
408                m_timer = NULL;
409             }
410          }
411          label_JobStatus->setPixmap(QPixmap(":/images/" + stat + ".png"));
412          jobstatus_to_ascii_gui(stat[0].toAscii(), buf, sizeof(buf));
413          stat = buf;
414          label_JobStatus->setToolTip(stat);
415
416          chkbox_PurgedFiles->setCheckState(fld.next().toInt()?Qt::Checked:Qt::Unchecked);
417       }
418    }
419 }
420   
421 void Job::populateVolumes()
422 {
423
424    QString query = 
425       "SELECT DISTINCT VolumeName, InChanger, Slot "
426       "FROM Job JOIN JobMedia USING (JobId) JOIN Media USING (MediaId) "
427       "WHERE JobId=" + m_jobId + " ORDER BY VolumeName "; 
428    if (mainWin->m_sqlDebug) Pmsg1(0, "Query cmd : %s\n",query.toUtf8().data());
429          
430
431    QStringList results;
432    if (m_console->sql_cmd(query, results)) {
433       QString resultline;
434       QStringList fieldlist;
435       list_Volume->clear();
436       foreach (resultline, results) { // should have only one result
437          fieldlist = resultline.split("\t");
438          QStringListIterator fld(fieldlist);
439 //         QListWidgetItem(QIcon(":/images/inchanger" + fld.next() + ".png"), 
440 //                         fld.next(), list_Volume);
441          list_Volume->addItem(fld.next());
442       }
443    }
444 }
445
446 //QListWidgetItem ( const QIcon & icon, const QString & text, QListWidget * parent = 0, int type = Type )