]> 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 5c8568ba1e2c632f2bd2185725d350cf634c6ad8..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.
    Switzerland, email:ftf@fsfeurope.org.
 */
  
    Switzerland, email:ftf@fsfeurope.org.
 */
  
-/*
- *   Version $Id$
- *
- *  Job Class
- *
- *   Dirk Bartley, March 2007
- *
- */ 
-
 #include "bat.h"
 #include "job.h"
 #include "util/fmtwidgetitem.h"
 #include "bat.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(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();
+   QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
+
+   MediaInfo *m = new MediaInfo(pageSelectorTreeWidgetItem, s);
+   connect(m, SIGNAL(destroyed()), this, SLOT(populateTree()));
+}
+
 void Job::deleteJob()
 {
    if (QMessageBox::warning(this, "Bat",
 void Job::deleteJob()
 {
    if (QMessageBox::warning(this, "Bat",
@@ -79,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();
@@ -96,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();
@@ -122,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 ? */
@@ -139,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 */
 
@@ -195,50 +224,98 @@ void Job::populateText()
   
 }
 
   
 }
 
-// Need to use the fmtwidgetitem helper instead
-QString convertBytesSI(qint64 qfld)
+void Job::storeBwLimit(int val)
 {
 {
-   static const qint64 KB = Q_INT64_C(1000);
-   static const qint64 MB = (KB * KB);
-   static const qint64 GB = (MB * KB);
-   static const qint64 TB = (GB * KB);
-   static const qint64 PB = (TB * KB);
-   static const qint64 EB = (PB * KB);
-
-   /* note: division is integer, so to have some decimals we divide for a
-      smaller unit (e.g. GB for a TB number and so on) */
-   char suffix;
-   if (qfld >= EB) {
-      qfld /= PB; 
-      suffix = 'E';
-   }
-   else if (qfld >= PB) {
-      qfld /= TB; 
-      suffix = 'P';
-   }
-   else if (qfld >= TB) {
-      qfld /= GB; 
-      suffix = 'T';
-   }
-   else if (qfld >= GB) {
-      qfld /= MB;
-      suffix = 'G';
-   }
-   else if (qfld >= MB) {
-      qfld /= KB;
-      suffix = 'M';
-   }
-   else if (qfld >= KB) {
-      suffix = 'k'; /* SI uses lowercase k */
-   }
-   else  {
-      /* plain bytes, no need to reformat */
-      return QString("%1 B").arg(qfld); 
+   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;
    }
 
    }
 
-   /* having divided for a smaller unit, now we can safely convert to double and
-      use the extra room for decimals */
-   return QString("%1 %2B").arg(qfld / 1000.0, 0, 'f', 2).arg(suffix);
+   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]);
+               
+            }
+         }
+      }
+   }
 }
 
 /*
 }
 
 /*
@@ -246,17 +323,19 @@ QString convertBytesSI(qint64 qfld)
  */
 void Job::populateForm()
 {
  */
 void Job::populateForm()
 {
-   QString stat;
+   QString stat, err;
    char buf[256];
    QString query = 
    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
@@ -267,19 +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());
 
          label_JobBytes->setText(convertBytesSI(fld.next().toULongLong()));
          label_JobFiles->setText(fld.next());
-         label_JobErrors->setText(fld.next());
-
-         stat=fld.next();
+         err = fld.next();
+         label_JobErrors->setText(err);
+
+         stat = fld.next();
+         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;
@@ -297,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;