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