]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/qt-console/job/job.cpp
Modify Job view to follow backup progress in real-time
[bacula/bacula] / bacula / src / qt-console / job / job.cpp
index 3b170323e0aca78ea432c97e27da6ee368892463..73926fc0915bde54ea8cde4b0158144a47eeccb4 100644 (file)
@@ -6,7 +6,7 @@
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
    This program is Free Software; you can redistribute it and/or
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
    This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version two of the GNU General Public
+   modify it under the terms of version three of the GNU Affero General Public
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
@@ -15,7 +15,7 @@
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
+   You should have received a copy of the GNU Affero General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 #include "job.h"
 #include "util/fmtwidgetitem.h"
 #include "mediainfo/mediainfo.h"
 #include "job.h"
 #include "util/fmtwidgetitem.h"
 #include "mediainfo/mediainfo.h"
+#include "run/run.h"
 
 Job::Job(QString &jobId, QTreeWidgetItem *parentTreeWidgetItem)
 {
    setupUi(this);
 
 Job::Job(QString &jobId, QTreeWidgetItem *parentTreeWidgetItem)
 {
    setupUi(this);
-   m_closeable = true;
    pgInitialize(tr("Job"), parentTreeWidgetItem);
    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/joblog.png")));
    m_cursor = new QTextCursor(textJobLog->document());
 
    pgInitialize(tr("Job"), parentTreeWidgetItem);
    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/joblog.png")));
    m_cursor = new QTextCursor(textJobLog->document());
 
+   m_bwlimit = 0;
    m_jobId = jobId;
    m_jobId = jobId;
+   m_timer = NULL;
    getFont();
 
    connect(pbRefresh, SIGNAL(clicked()), this, SLOT(populateAll()));
    connect(pbDelete, SIGNAL(clicked()), this, SLOT(deleteJob()));
    getFont();
 
    connect(pbRefresh, SIGNAL(clicked()), this, SLOT(populateAll()));
    connect(pbDelete, SIGNAL(clicked()), this, SLOT(deleteJob()));
+   connect(pbCancel, SIGNAL(clicked()), this, SLOT(cancelJob()));
+   connect(pbRun, SIGNAL(clicked()), this, SLOT(rerun()));
    connect(list_Volume, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(showInfoVolume(QListWidgetItem *)));
    connect(list_Volume, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(showInfoVolume(QListWidgetItem *)));
+   connect(spin_Bwlimit, SIGNAL(valueChanged(int)), this, SLOT(storeBwLimit(int)));
 
    populateAll();
    dockPage();
    setCurrent();
 }
 
 
    populateAll();
    dockPage();
    setCurrent();
 }
 
+void Job::rerun()
+{
+   new runPage(label_Name->text(),
+               label_Level->text(),
+               label_Pool->text(),
+               QString(""),              // storage
+               label_Client->text(),
+               label_FileSet->text());
+}
+
 void Job::showInfoVolume(QListWidgetItem *item)
 {
    QString s= item->text();
 void Job::showInfoVolume(QListWidgetItem *item)
 {
    QString s= item->text();
@@ -81,6 +96,18 @@ void Job::deleteJob()
    closeStackPage();
 }
 
    closeStackPage();
 }
 
+void Job::cancelJob()
+{
+   if (QMessageBox::warning(this, "Bat",
+                            tr("Are you sure you want to cancel this job?"),
+                            QMessageBox::Ok | QMessageBox::Cancel)
+       == QMessageBox::Cancel) { return; }
+
+   QString cmd("cancel jobid=");
+   cmd += m_jobId;
+   consoleCommand(cmd, false);
+}
+
 void Job::getFont()
 {
    QFont font = textJobLog->font();
 void Job::getFont()
 {
    QFont font = textJobLog->font();
@@ -98,7 +125,7 @@ void Job::getFont()
 
 void Job::populateAll()
 {
 
 void Job::populateAll()
 {
-   Pmsg0(0, "populateAll()\n");
+// Pmsg0(50, "populateAll()\n");
    populateText();
    populateForm();
    populateVolumes();
    populateText();
    populateForm();
    populateVolumes();
@@ -124,9 +151,9 @@ void Job::populateText()
       if (!results.size()) {
          QMessageBox::warning(this, tr("Bat"),
             tr("There were no results!\n"
       if (!results.size()) {
          QMessageBox::warning(this, tr("Bat"),
             tr("There were no results!\n"
-              "It is possible you may need to add \"catalog = all\" "
-              "to the Messages resource for this job.\n"), QMessageBox::Ok);
-        return;
+               "It is possible you may need to add \"catalog = all\" "
+               "to the Messages resource for this job.\n"), QMessageBox::Ok);
+         return;
       } 
 
       QString jobstr("JobId "); /* FIXME: should this be translated ? */
       } 
 
       QString jobstr("JobId "); /* FIXME: should this be translated ? */
@@ -141,47 +168,47 @@ void Job::populateText()
       QString lastSvc;
       foreach (QString resultline, results) {
          fieldlist = resultline.split("\t");
       QString lastSvc;
       foreach (QString resultline, results) {
          fieldlist = resultline.split("\t");
-        
-        if (fieldlist.size() < 2)
-           continue;
-
-        QString curTime = fieldlist[0].trimmed();
-
-        field = fieldlist[1].trimmed();
-        int colon = field.indexOf(":");
-        if (colon > 0) {
-           /* string is like <service> <jobId xxxx>: ..." 
-            * we split at ':' then remove the jobId xxxx string (always the same) */ 
-           QString curSvc(field.left(colon).replace(jobstr,"").trimmed());
-           if (curSvc == lastSvc  && curTime == lastTime) {
-              curTime.clear();
-              curSvc.clear(); 
-           } else {
-              lastTime = curTime;
-              lastSvc = curSvc;
-           }
-//         htmlbuf += "<td>" + curTime + "</td>";
-           htmlbuf += "\n" + curSvc + " ";
-
-           /* rest of string is marked as pre-formatted (here trimming should
-            * be avoided, to preserve original formatting) */
-           QString msg(field.mid(colon+2));
-           if (msg.startsWith( tr("Error:")) ) { /* FIXME: should really be translated ? */
-              /* error msg, use a specific class */
-              htmlbuf += "</pre><pre class=err>" + msg + "</pre><pre>";
-           } else {
-              htmlbuf += msg ;
-           }
-        } else {
-           /* non standard string, place as-is */
-           if (curTime == lastTime) {
-              curTime.clear();
-           } else {
-              lastTime = curTime;
-           }
-//         htmlbuf += "<td>" + curTime + "</td>";
-           htmlbuf += "\n" + field ;
-        }
+         
+         if (fieldlist.size() < 2)
+            continue;
+
+         QString curTime = fieldlist[0].trimmed();
+
+         field = fieldlist[1].trimmed();
+         int colon = field.indexOf(":");
+         if (colon > 0) {
+            /* string is like <service> <jobId xxxx>: ..." 
+             * we split at ':' then remove the jobId xxxx string (always the same) */ 
+            QString curSvc(field.left(colon).replace(jobstr,"").trimmed());
+            if (curSvc == lastSvc  && curTime == lastTime) {
+               curTime.clear();
+               curSvc.clear(); 
+            } else {
+               lastTime = curTime;
+               lastSvc = curSvc;
+            }
+//          htmlbuf += "<td>" + curTime + "</td>";
+            htmlbuf += "\n" + curSvc + " ";
+
+            /* rest of string is marked as pre-formatted (here trimming should
+             * be avoided, to preserve original formatting) */
+            QString msg(field.mid(colon+2));
+            if (msg.startsWith( tr("Error:")) ) { /* FIXME: should really be translated ? */
+               /* error msg, use a specific class */
+               htmlbuf += "</pre><pre class=err>" + msg + "</pre><pre>";
+            } else {
+               htmlbuf += msg ;
+            }
+         } else {
+            /* non standard string, place as-is */
+            if (curTime == lastTime) {
+               curTime.clear();
+            } else {
+               lastTime = curTime;
+            }
+//          htmlbuf += "<td>" + curTime + "</td>";
+            htmlbuf += "\n" + field ;
+         }
   
       } /* foreach resultline */
 
   
       } /* foreach resultline */
 
@@ -197,6 +224,100 @@ void Job::populateText()
   
 }
 
   
 }
 
+void Job::storeBwLimit(int val)
+{
+   m_bwlimit = val;
+}
+
+void Job::updateRunInfo()
+{
+   QString cmd;
+   QStringList results;
+   QStringList lst;
+   bool parseit=false;
+   QChar equal = '=';
+
+   if (m_bwlimit >= 100) {
+      cmd = QString("setbandwidth limit=" + QString::number(m_bwlimit) 
+                    + " jobid=" + m_jobId);
+      m_console->dir_cmd(cmd, results);
+      results.clear();
+      m_bwlimit = 0;
+   }
+
+   cmd = QString(".status client=\"" + m_client + "\" running");
+
+   if (m_console->dir_cmd(cmd, results)) {
+      foreach (QString mline, results) {
+         foreach (QString line, mline.split("\n")) { 
+            line = line.trimmed();
+            lst = line.split(equal);
+            if (lst.count() != 2) {
+               Pmsg1(0, "bad count=%d\n",lst.count());
+               continue;
+            }
+            
+            if (lst[0] == "JobId") {
+               if (lst[1] == m_jobId) {
+                  parseit = true;
+               } else {
+                  parseit = false;
+               }
+            }
+            if (!parseit) {
+               continue;
+            }
+            
+//         } else if (lst[0] == "Job") {
+//            grpRun->setTitle(lst[1]);
+            
+//               
+//         } else if (lst[0] == "VSS") {
+
+//         } else if (lst[0] == "Level") {
+//            Info->setText(lst[1]);
+//
+//         } else if (lst[0] == "JobType") {
+//
+//         } else if (lst[0] == "JobStarted") {
+//            Started->setText(lst[1]);
+
+            if (lst[0] == "Bwlimit") {
+               int val = lst[1].toInt();
+               if (val > 0) {
+                  chk_Bwlimit->setChecked(true);
+                  spin_Bwlimit->setEnabled(true);
+                  spin_Bwlimit->setValue(lst[1].toInt()/1024);
+               } else {
+                  chk_Bwlimit->setEnabled(false);
+                  spin_Bwlimit->setEnabled(false);
+                  spin_Bwlimit->setValue(0);
+               }
+               
+//         } else if (lst[0] == "Errors") {
+//            Errors->setText(lst[1]);
+               
+            } else if (lst[0] == "Bytes/sec") {
+               label_Speed->setText(convertBytesSI(lst[1].toULongLong())+"/s");
+               
+            } else if (lst[0] == "Files") {
+               label_JobFiles->setText(lst[1]);
+               
+            } else if (lst[0] == "Bytes") {
+               label_JobBytes->setText(convertBytesSI(lst[1].toULongLong()));
+               
+            } else if (lst[0] == "FilesExamined") {
+               label_FilesExamined->setText(lst[1]);
+               
+            } else if (lst[0] == "ProcessingFile") {
+               label_CurrentFile->setText(lst[1]);
+               
+            }
+         }
+      }
+   }
+}
+
 /*
  * Populate the text in the window
  */
 /*
  * Populate the text in the window
  */
@@ -205,14 +326,16 @@ void Job::populateForm()
    QString stat, err;
    char buf[256];
    QString query = 
    QString stat, err;
    char buf[256];
    QString query = 
-      "SELECT JobId, Job.Name, Level, Client.Name, Pool.Name, FileSet, SchedTime, StartTime, EndTime, "
-      "EndTime - StartTime AS Duration, JobBytes, JobFiles, JobErrors, JobStatus, PurgedFiles "
-      "FROM Job JOIN Client USING (ClientId) LEFT JOIN Pool USING (PoolId) "
-      "LEFT JOIN FileSet USING (FileSetId)"
+      "SELECT JobId, Job.Name, Level, Client.Name, Pool.Name, FileSet,"
+      "SchedTime, StartTime, EndTime, EndTime-StartTime AS Duration, "
+      "JobBytes, JobFiles, JobErrors, JobStatus, PurgedFiles "
+      "FROM Job JOIN Client USING (ClientId) "
+        "LEFT JOIN Pool ON (Job.PoolId = Pool.PoolId) "
+        "LEFT JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId)"
       "WHERE JobId=" + m_jobId; 
    QStringList results;
    if (m_console->sql_cmd(query, results)) {
       "WHERE JobId=" + m_jobId; 
    QStringList results;
    if (m_console->sql_cmd(query, results)) {
-      QString resultline;
+      QString resultline, duration;
       QStringList fieldlist;
 
       foreach (resultline, results) { // should have only one result
       QStringList fieldlist;
 
       foreach (resultline, results) { // should have only one result
@@ -223,23 +346,53 @@ void Job::populateForm()
          
          label_Level->setText(job_level_to_str(fld.next()[0].toAscii()));
 
          
          label_Level->setText(job_level_to_str(fld.next()[0].toAscii()));
 
-         label_Client->setText(fld.next());
+         m_client = fld.next();
+         label_Client->setText(m_client);
          label_Pool->setText(fld.next());
          label_FileSet->setText(fld.next());
          label_SchedTime->setText(fld.next());
          label_StartTime->setText(fld.next());
          label_EndTime->setText(fld.next());
          label_Pool->setText(fld.next());
          label_FileSet->setText(fld.next());
          label_SchedTime->setText(fld.next());
          label_StartTime->setText(fld.next());
          label_EndTime->setText(fld.next());
-         label_Duration->setText(fld.next());
+         duration = fld.next();
+         /* 
+          * Note: if we have a negative duration, it is because the EndTime
+          *  is zero (i.e. the Job is still running).  We should use 
+          *  duration = StartTime - current_time
+          */
+         if (duration.left(1) == "-") {
+            duration = "0.0";
+         }
+         label_Duration->setText(duration);
 
          label_JobBytes->setText(convertBytesSI(fld.next().toULongLong()));
          label_JobFiles->setText(fld.next());
          err = fld.next();
          label_JobErrors->setText(err);
 
 
          label_JobBytes->setText(convertBytesSI(fld.next().toULongLong()));
          label_JobFiles->setText(fld.next());
          err = fld.next();
          label_JobErrors->setText(err);
 
-         stat=fld.next();
+         stat = fld.next();
          if (stat == "T" && err.toInt() > 0) {
             stat = "W";
          }
          if (stat == "T" && err.toInt() > 0) {
             stat = "W";
          }
+         if (stat == "R") {
+            pbDelete->setVisible(false);
+            pbCancel->setVisible(true);
+            grpRun->setVisible(true);
+            if (!m_timer) {
+               m_timer = new QTimer(this);
+               connect(m_timer, SIGNAL(timeout()), this, SLOT(populateAll()));
+               m_timer->start(30000);
+            }
+            updateRunInfo();
+         } else {
+            pbDelete->setVisible(true);
+            pbCancel->setVisible(false);
+            grpRun->setVisible(false);
+            if (m_timer) {
+               m_timer->stop();
+               delete m_timer;
+               m_timer = NULL;
+            }
+         }
          label_JobStatus->setPixmap(QPixmap(":/images/" + stat + ".png"));
          jobstatus_to_ascii_gui(stat[0].toAscii(), buf, sizeof(buf));
          stat = buf;
          label_JobStatus->setPixmap(QPixmap(":/images/" + stat + ".png"));
          jobstatus_to_ascii_gui(stat[0].toAscii(), buf, sizeof(buf));
          stat = buf;
@@ -257,7 +410,7 @@ void Job::populateVolumes()
       "SELECT DISTINCT VolumeName, InChanger, Slot "
       "FROM Job JOIN JobMedia USING (JobId) JOIN Media USING (MediaId) "
       "WHERE JobId=" + m_jobId + " ORDER BY VolumeName "; 
       "SELECT DISTINCT VolumeName, InChanger, Slot "
       "FROM Job JOIN JobMedia USING (JobId) JOIN Media USING (MediaId) "
       "WHERE JobId=" + m_jobId + " ORDER BY VolumeName "; 
-   Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
+   if (mainWin->m_sqlDebug) Pmsg1(0, "Query cmd : %s\n",query.toUtf8().data());
          
 
    QStringList results;
          
 
    QStringList results;