]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/joblist/joblist.cpp
Add feature to jump from fileset to joblist pre-populating the fileset of the
[bacula/bacula] / bacula / src / qt-console / joblist / joblist.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 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 two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed 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 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 John Walker.
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  *   Version $Id: joblist.h 4230 2007-02-21 20:07:37Z kerns $
30  *
31  *   Dirk Bartley, March 2007
32  */
33  
34 #include <QAbstractEventDispatcher>
35 #include <QTableWidgetItem>
36 #include "bat.h"
37 #include "joblist.h"
38 #include "restore.h"
39 #include "joblog/joblog.h"
40
41 /*
42  * Constructor for the class
43  */
44 JobList::JobList(const QString &mediaName, const QString &clientName,
45           const QString &jobName, const QString &filesetName, QTreeWidgetItem *parentTreeWidgetItem)
46 {
47    setupUi(this);
48    m_name = ""; /* treeWidgetName has a virtual override in this class */
49    m_mediaName = mediaName;
50    m_clientName = clientName;
51    m_jobName = jobName;
52    m_filesetName = filesetName;
53    m_filesetName = filesetName;
54    pgInitialize(parentTreeWidgetItem);
55    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
56    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/emblem-system.svg")));
57
58    m_resultCount = 0;
59    m_populated = false;
60    m_closeable = false;
61    if ((m_mediaName != "") || (m_clientName != "") || (m_jobName != "") || (m_filesetName != ""))
62       m_closeable=true;
63    m_checkCurrentWidget = true;
64    createConnections();
65
66    /* Set Defaults for check and spin for limits */
67    limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
68    limitSpinBox->setValue(mainWin->m_recordLimitVal);
69    daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
70    daysSpinBox->setValue(mainWin->m_daysLimitVal);
71    dockPage();
72 }
73
74 /*
75  * The Meat of the class.
76  * This function will populate the QTableWidget, mp_tablewidget, with
77  * QTableWidgetItems representing the results of a query for what jobs exist on
78  * the media name passed from the constructor stored in m_mediaName.
79  */
80 void JobList::populateTable()
81 {
82    QStringList results;
83    QString resultline;
84    QBrush blackBrush(Qt::black);
85
86    if (!m_console->preventInUseConnect())
87        return;
88
89    /* Can't do this in constructor because not neccesarily conected in constructor */
90    if (!m_populated) {
91       clientsComboBox->addItem("Any");
92       clientsComboBox->addItems(m_console->client_list);
93       int clientIndex = clientsComboBox->findText(m_clientName, Qt::MatchExactly);
94       if (clientIndex != -1)
95          clientsComboBox->setCurrentIndex(clientIndex);
96
97       QString query("SELECT VolumeName AS Media FROM Media ORDER BY Media");
98       if (mainWin->m_sqlDebug) {
99          Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
100       }
101       QStringList results, volumeList;
102       if (m_console->sql_cmd(query, results)) {
103          QString field;
104          QStringList fieldlist;
105          /* Iterate through the lines of results. */
106          foreach (QString resultline, results) {
107             fieldlist = resultline.split("\t");
108             volumeList.append(fieldlist[0]);
109          } /* foreach resultline */
110       } /* if results from query */
111       volumeComboBox->addItem("Any");
112       volumeComboBox->addItems(volumeList);
113       int volumeIndex = volumeComboBox->findText(m_mediaName, Qt::MatchExactly);
114       if (volumeIndex != -1) {
115          volumeComboBox->setCurrentIndex(volumeIndex);
116       }
117       jobComboBox->addItem("Any");
118       jobComboBox->addItems(m_console->job_list);
119       int jobIndex = jobComboBox->findText(m_jobName, Qt::MatchExactly);
120       if (jobIndex != -1) {
121          jobComboBox->setCurrentIndex(jobIndex);
122       }
123       levelComboBox->addItem("Any");
124       levelComboBox->addItems( QStringList() << "F" << "D" << "I");
125       purgedComboBox->addItem("Any");
126       purgedComboBox->addItems( QStringList() << "0" << "1");
127       statusComboBox->addItem("Any");
128       fileSetComboBox->addItem("Any");
129       fileSetComboBox->addItems(m_console->fileset_list);
130       int filesetIndex = fileSetComboBox->findText(m_filesetName, Qt::MatchExactly);
131       if (filesetIndex != -1) {
132          fileSetComboBox->setCurrentIndex(filesetIndex);
133       }
134       QString statusQuery("SELECT JobStatusLong FROM Status");
135       if (mainWin->m_sqlDebug) {
136          Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
137       }
138       QStringList statusResults, statusLongList;
139       if (m_console->sql_cmd(statusQuery, statusResults)) {
140          QString field;
141          QStringList fieldlist;
142          /* Iterate through the lines of results. */
143          foreach (QString resultline, statusResults) {
144             fieldlist = resultline.split("\t");
145             statusLongList.append(fieldlist[0]);
146          } /* foreach resultline */
147       } /* if results from statusquery */
148       statusComboBox->addItems(statusLongList);
149    }
150
151    /* Set up query */
152    QString query("");
153    int volumeIndex = volumeComboBox->currentIndex();
154    if (volumeIndex != -1)
155       m_mediaName = volumeComboBox->itemText(volumeIndex);
156    query += "SELECT DISTINCT Job.Jobid AS Id, Job.Name AS JobName, Client.Name AS Client,"
157             " Job.Starttime AS JobStart, Job.Type AS JobType,"
158             " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
159             " Job.JobBytes AS Bytes,"
160             " Job.JobStatus AS Status, Status.JobStatusLong AS StatusLong,"
161             " Job.PurgedFiles AS Purged, FileSet.FileSet"
162             " FROM Job"
163             " LEFT OUTER JOIN Client ON (Client.ClientId=Job.ClientId)"
164             " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId)"
165             " LEFT OUTER JOIN Status ON (Job.JobStatus=Status.JobStatus)"
166             " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId)"
167             " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId)";
168    QStringList conditions;
169    if (m_mediaName != "Any") {
170       conditions.append("Media.VolumeName='" + m_mediaName + "'");
171    }
172    int clientIndex = clientsComboBox->currentIndex();
173    if (clientIndex != -1)
174       m_clientName = clientsComboBox->itemText(clientIndex);
175    if (m_clientName != "Any") {
176       conditions.append("Client.Name='" + m_clientName + "'");
177    }
178    int jobIndex = jobComboBox->currentIndex();
179    if (jobIndex != -1)
180       m_jobName = jobComboBox->itemText(jobIndex);
181    if ((jobIndex != -1) && (jobComboBox->itemText(jobIndex) != "Any")) {
182       conditions.append("Job.Name='" + jobComboBox->itemText(jobIndex) + "'");
183    }
184    int levelIndex = levelComboBox->currentIndex();
185    if ((levelIndex != -1) && (levelComboBox->itemText(levelIndex) != "Any")) {
186       conditions.append("Job.Level='" + levelComboBox->itemText(levelIndex) + "'");
187    }
188    int statusIndex = statusComboBox->currentIndex();
189    if ((statusIndex != -1) && (statusComboBox->itemText(statusIndex) != "Any")) {
190       conditions.append("Status.JobStatusLong='" + statusComboBox->itemText(statusIndex) + "'");
191    }
192    int purgedIndex = purgedComboBox->currentIndex();
193    if ((purgedIndex != -1) && (purgedComboBox->itemText(purgedIndex) != "Any")) {
194       conditions.append("Job.PurgedFiles='" + purgedComboBox->itemText(purgedIndex) + "'");
195    }
196    int fileSetIndex = fileSetComboBox->currentIndex();
197    if (fileSetIndex != -1)
198       m_filesetName = fileSetComboBox->itemText(fileSetIndex);
199    if ((fileSetIndex != -1) && (fileSetComboBox->itemText(fileSetIndex) != "Any")) {
200       conditions.append("FileSet.FileSet='" + fileSetComboBox->itemText(fileSetIndex) + "'");
201    }
202    /* If Limit check box For limit by days is checked  */
203    if (daysCheckBox->checkState() == Qt::Checked) {
204       QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
205       QString since = stamp.toString(Qt::ISODate);
206       conditions.append("Job.Starttime>'" + since + "'");
207    }
208    bool first = true;
209    foreach (QString condition, conditions) {
210       if (first) {
211          query += " WHERE " + condition;
212          first = false;
213       } else {
214          query += " AND " + condition;
215       }
216    }
217    /* Descending */
218    query += " ORDER BY Job.Starttime DESC, Job.JobId DESC";
219    /* If Limit check box for limit records returned is checked  */
220    if (limitCheckBox->checkState() == Qt::Checked) {
221       QString limit;
222       limit.setNum(limitSpinBox->value());
223       query += " LIMIT " + limit;
224    }
225
226    /* Set up the Header for the table */
227    QStringList headerlist = (QStringList()
228       << "Job Id" << "Job Name" << "Client" << "Job Starttime" << "Job Type" 
229       << "Job Level" << "Job Files" << "Job Bytes" << "Job Status"  << "Purged" << "File Set" );
230    m_purgedIndex = headerlist.indexOf("Purged");
231    m_typeIndex = headerlist.indexOf("Job Type");
232    m_statusIndex = headerlist.indexOf("Job Status");
233
234    /* Initialize the QTableWidget */
235    m_checkCurrentWidget = false;
236    mp_tableWidget->clear();
237    m_checkCurrentWidget = true;
238    mp_tableWidget->setColumnCount(headerlist.size());
239    mp_tableWidget->setHorizontalHeaderLabels(headerlist);
240
241    if (mainWin->m_sqlDebug) {
242       Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
243    }
244    if (m_console->sql_cmd(query, results)) {
245       m_resultCount = results.count();
246
247       QTableWidgetItem* p_tableitem;
248       QString field;
249       QStringList fieldlist;
250       mp_tableWidget->setRowCount(results.size());
251
252       int row = 0;
253       /* Iterate through the record returned from the query */
254       foreach (resultline, results) {
255          fieldlist = resultline.split("\t");
256          int column = 0;
257          bool m_statusIndexDone = false;
258          QString statusCode("");
259          /* Iterate through fields in the record */
260          foreach (field, fieldlist) {
261             field = field.trimmed();  /* strip leading & trailing spaces */
262             if ((column == m_statusIndex) && (!m_statusIndexDone)){
263                m_statusIndexDone = true;
264                statusCode = field;
265             } else {
266                p_tableitem = new QTableWidgetItem(field,1);
267                p_tableitem->setFlags(0);
268                p_tableitem->setForeground(blackBrush);
269                mp_tableWidget->setItem(row, column, p_tableitem);
270                if (column == m_statusIndex)
271                   setStatusColor(p_tableitem, statusCode);
272                column++;
273             }
274          }
275          row++;
276       }
277    } 
278    /* Resize the columns */
279    mp_tableWidget->resizeColumnsToContents();
280    mp_tableWidget->resizeRowsToContents();
281    mp_tableWidget->verticalHeader()->hide();
282    if ((m_mediaName != "Any") && (m_resultCount == 0)){
283       /* for context sensitive searches, let the user know if there were no
284        * results */
285       QMessageBox::warning(this, tr("Bat"),
286           tr("The Jobs query returned no results.\n"
287          "Press OK to continue?"), QMessageBox::Ok );
288    }
289 }
290
291 void JobList::setStatusColor(QTableWidgetItem *item, QString &field)
292 {
293    QString greenchars("TCR");
294    QString redchars("BEf");
295    QString yellowchars("eDAFSMmsjdctp");
296    if (greenchars.contains(field, Qt::CaseSensitive)) {
297       item->setBackground(Qt::green);
298    } else if (redchars.contains(field, Qt::CaseSensitive)) {
299       item->setBackground(Qt::red);
300    } else if (yellowchars.contains(field, Qt::CaseSensitive)){ 
301       item->setBackground(Qt::yellow);
302    }
303 }
304
305 /*
306  * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
307  * The tree has been populated.
308  */
309 void JobList::PgSeltreeWidgetClicked()
310 {
311    if (!m_populated) {
312       populateTable();
313       m_populated=true;
314    }
315 }
316
317 /*
318  *  Virtual function override of pages function which is called when this page
319  *  is visible on the stack
320  */
321 void JobList::currentStackItem()
322 {
323    populateTable();
324    if (!m_populated) {
325       m_populated=true;
326    }
327 }
328
329 /*
330  * Virtual Function to return the name for the medialist tree widget
331  */
332 void JobList::treeWidgetName(QString &desc)
333 {
334    if ((m_mediaName == "") && (m_clientName == "") && (m_jobName == "") && (m_filesetName == "")) {
335       desc = "JobList";
336    } else {
337       desc = "JobList ";
338       if (m_mediaName != "" ) {
339          desc += "of Volume " + m_mediaName;
340       }
341       if (m_clientName != "" ) {
342          desc += "of Client " + m_clientName;
343       }
344       if (m_jobName != "" ) {
345          desc += "of Job " + m_jobName;
346       }
347       if (m_filesetName != "" ) {
348          desc += "of fileset " + m_filesetName;
349       }
350    }
351 }
352
353 /*
354  * This functions much line tableItemChanged for trees like the page selector,
355  * but I will do much less here
356  */
357 void JobList::tableItemChanged(QTableWidgetItem *currentItem, QTableWidgetItem * /*previousItem*/)
358 {
359    if (m_checkCurrentWidget) {
360       int row = currentItem->row();
361       QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
362       m_currentJob = jobitem->text();
363
364       /* include purged action or not */
365       jobitem = mp_tableWidget->item(row, m_purgedIndex);
366       QString purged = jobitem->text();
367       mp_tableWidget->removeAction(actionPurgeFiles);
368       if (purged == "0") {
369          mp_tableWidget->addAction(actionPurgeFiles);
370       }
371       /* include restore from time and job action or not */
372       jobitem = mp_tableWidget->item(row, m_typeIndex);
373       QString type = jobitem->text();
374       mp_tableWidget->removeAction(actionRestoreFromJob);
375       mp_tableWidget->removeAction(actionRestoreFromTime);
376       if (type == "B") {
377          mp_tableWidget->addAction(actionRestoreFromJob);
378          mp_tableWidget->addAction(actionRestoreFromTime);
379       }
380       /* include cancel action or not */
381       jobitem = mp_tableWidget->item(row, m_statusIndex);
382       QString status = jobitem->text();
383       mp_tableWidget->removeAction(actionCancelJob);
384       if (status == "Running") {
385          mp_tableWidget->addAction(actionCancelJob);
386       }
387    }
388 }
389
390 /*
391  * Function to create connections for context sensitive menu for this and
392  * the page selector
393  */
394 void JobList::createConnections()
395 {
396    /* connect to the action specific to this pages class that shows up in the 
397     * page selector tree */
398    connect(actionRefreshJobList, SIGNAL(triggered()), this,
399                 SLOT(populateTable()));
400    connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
401    /* for the tableItemChanged to maintain m_currentJob */
402    connect(mp_tableWidget, SIGNAL(
403            currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
404            this, SLOT(tableItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
405
406    /* Do what is required for the local context sensitive menu */
407
408
409    /* setContextMenuPolicy is required */
410    mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
411
412    /* Add Actions */
413    mp_tableWidget->addAction(actionRefreshJobList);
414    mp_tableWidget->addAction(actionListJobid);
415    mp_tableWidget->addAction(actionListFilesOnJob);
416    mp_tableWidget->addAction(actionListJobMedia);
417    mp_tableWidget->addAction(actionListVolumes);
418    mp_tableWidget->addAction(actionDeleteJob);
419    mp_tableWidget->addAction(actionPurgeFiles);
420    mp_tableWidget->addAction(actionRestoreFromJob);
421    mp_tableWidget->addAction(actionRestoreFromTime);
422    mp_tableWidget->addAction(actionShowLogForJob);
423
424    /* Make Connections */
425    connect(actionListJobid, SIGNAL(triggered()), this,
426                 SLOT(consoleListJobid()));
427    connect(actionListFilesOnJob, SIGNAL(triggered()), this,
428                 SLOT(consoleListFilesOnJob()));
429    connect(actionListJobMedia, SIGNAL(triggered()), this,
430                 SLOT(consoleListJobMedia()));
431    connect(actionListVolumes, SIGNAL(triggered()), this,
432                 SLOT(consoleListVolumes()));
433    connect(actionDeleteJob, SIGNAL(triggered()), this,
434                 SLOT(consoleDeleteJob()));
435    connect(actionPurgeFiles, SIGNAL(triggered()), this,
436                 SLOT(consolePurgeFiles()));
437    connect(actionRestoreFromJob, SIGNAL(triggered()), this,
438                 SLOT(preRestoreFromJob()));
439    connect(actionRestoreFromTime, SIGNAL(triggered()), this,
440                 SLOT(preRestoreFromTime()));
441    connect(actionShowLogForJob, SIGNAL(triggered()), this,
442                 SLOT(showLogForJob()));
443    connect(actionCancelJob, SIGNAL(triggered()), this,
444                 SLOT(consoleCancelJob()));
445    connect(actionListJobTotals, SIGNAL(triggered()), this,
446                 SLOT(consoleListJobTotals()));
447
448    m_contextActions.append(actionRefreshJobList);
449    m_contextActions.append(actionListJobTotals);
450 }
451
452 /*
453  * Functions to respond to local context sensitive menu sending console commands
454  * If I could figure out how to make these one function passing a string, Yaaaaaa
455  */
456 void JobList::consoleListJobid()
457 {
458    QString cmd("list jobid=");
459    cmd += m_currentJob;
460    if (mainWin->m_longList) { cmd.prepend("l"); }
461    consoleCommand(cmd);
462 }
463 void JobList::consoleListFilesOnJob()
464 {
465    QString cmd("list files jobid=");
466    cmd += m_currentJob;
467    if (mainWin->m_longList) { cmd.prepend("l"); }
468    consoleCommand(cmd);
469 }
470 void JobList::consoleListJobMedia()
471 {
472    QString cmd("list jobmedia jobid=");
473    cmd += m_currentJob;
474    if (mainWin->m_longList) { cmd.prepend("l"); }
475    consoleCommand(cmd);
476 }
477 void JobList::consoleListVolumes()
478 {
479    QString cmd("list volumes jobid=");
480    cmd += m_currentJob;
481    if (mainWin->m_longList) { cmd.prepend("l"); }
482    consoleCommand(cmd);
483 }
484 void JobList::consoleListJobTotals()
485 {
486    QString cmd("list jobtotals");
487    cmd += m_currentJob;
488    if (mainWin->m_longList) { cmd.prepend("l"); }
489    consoleCommand(cmd);
490 }
491 void JobList::consoleDeleteJob()
492 {
493    if (QMessageBox::warning(this, tr("Bat"),
494       tr("Are you sure you want to delete??  !!!.\n"
495 "This delete command is used to delete a Job record and all associated catalog"
496 " records that were created. This command operates only on the Catalog"
497 " database and has no effect on the actual data written to a Volume. This"
498 " command can be dangerous and we strongly recommend that you do not use"
499 " it unless you know what you are doing.  The Job and all its associated"
500 " records (File and JobMedia) will be deleted from the catalog."
501       "Press OK to proceed with delete operation.?"),
502       QMessageBox::Ok | QMessageBox::Cancel)
503       == QMessageBox::Cancel) { return; }
504
505    QString cmd("delete job jobid=");
506    cmd += m_currentJob;
507    consoleCommand(cmd);
508 }
509 void JobList::consolePurgeFiles()
510 {
511    if (QMessageBox::warning(this, tr("Bat"),
512       tr("Are you sure you want to purge ??  !!!.\n"
513 "The Purge command will delete associated Catalog database records from Jobs and"
514 " Volumes without considering the retention period. Purge  works only on the"
515 " Catalog database and does not affect data written to Volumes. This command can"
516 " be dangerous because you can delete catalog records associated with current"
517 " backups of files, and we recommend that you do not use it unless you know what"
518 " you are doing.\n"
519       "Press OK to proceed with the purge operation?"),
520       QMessageBox::Ok | QMessageBox::Cancel)
521       == QMessageBox::Cancel) { return; }
522
523    QString cmd("purge files jobid=");
524    cmd += m_currentJob;
525    consoleCommand(cmd);
526 }
527
528 /*
529  * Subroutine to call preRestore to restore from a select job
530  */
531 void JobList::preRestoreFromJob()
532 {
533    new prerestorePage(m_currentJob, R_JOBIDLIST);
534 }
535
536 /*
537  * Subroutine to call preRestore to restore from a select job
538  */
539 void JobList::preRestoreFromTime()
540 {
541    new prerestorePage(m_currentJob, R_JOBDATETIME);
542 }
543
544 /*
545  * Subroutine to call class to show the log in the database from that job
546  */
547 void JobList::showLogForJob()
548 {
549    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
550    new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
551 }
552
553 /*
554  * Cancel a running job
555  */
556 void JobList::consoleCancelJob()
557 {
558    QString cmd("cancel jobid=");
559    cmd += m_currentJob;
560    consoleCommand(cmd);
561 }