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.
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.
Switzerland, email:ftf@fsfeurope.org.
*/
-/*
- * Version $Id$
- *
- * Job Class
- *
- * Dirk Bartley, March 2007
- *
- */
-
#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);
- 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());
+ m_bwlimit = 0;
m_jobId = jobId;
+ m_timer = NULL;
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();
}
+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",
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::populateAll()
{
- Pmsg0(0, "populateAll()\n");
+// Pmsg0(50, "populateAll()\n");
populateText();
populateForm();
populateVolumes();
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 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 */
}
-// 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]);
+
+ }
+ }
+ }
+ }
}
/*
*/
void Job::populateForm()
{
- QString stat;
+ 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)) {
- QString resultline;
+ QString resultline, duration;
QStringList fieldlist;
foreach (resultline, results) { // should have only one result
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_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_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;
"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;