From e10419de5304b47edd0609364a29e3d2da978a90 Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Thu, 8 Oct 2009 17:44:02 +0200 Subject: [PATCH] ebl Add new autochanger command to get all information about content --- bacula/src/dird/ua_label.c | 231 ++++++++++++++++------ bacula/src/dird/ua_status.c | 1 + bacula/src/qt-console/jobs/jobs.cpp | 15 +- bacula/src/qt-console/storage/content.cpp | 85 ++++++-- bacula/src/qt-console/storage/content.h | 1 + bacula/src/stored/autochanger.c | 2 +- bacula/src/stored/dircmd.c | 5 +- 7 files changed, 255 insertions(+), 85 deletions(-) diff --git a/bacula/src/dird/ua_label.c b/bacula/src/dird/ua_label.c index 83a1e7563f..6848c2b55b 100644 --- a/bacula/src/dird/ua_label.c +++ b/bacula/src/dird/ua_label.c @@ -987,9 +987,6 @@ int get_num_drives_from_SD(UAContext *ua) return drives; } - - - /* * Check if this is a cleaning tape by comparing the Volume name * with the Cleaning Prefix. If they match, this is a cleaning @@ -1016,6 +1013,157 @@ static bool is_cleaning_tape(UAContext *ua, MEDIA_DBR *mr, POOL_DBR *pr) strlen(ua->jcr->pool->cleaning_prefix)) == 0; } +static void content_send_info(UAContext *ua, char type, int Slot, char *vol_name) +{ + char ed1[50], ed2[50], ed3[50]; + POOL_DBR pr; + MEDIA_DBR mr; + /* Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */ + const char *slot_api_full_format="%c|%i|%i|%s|%s|%s|%s|%s|%s|%s\n"; + const char *slot_api_empty_format="%c|%i||||||||\n"; + + if (is_volume_name_legal(NULL, vol_name)) { + memset(&mr, 0, sizeof(mr)); + bstrncpy(mr.VolumeName, vol_name, sizeof(mr.VolumeName)); + if (db_get_media_record(ua->jcr, ua->db, &mr)) { + memset(&pr, 0, sizeof(POOL_DBR)); + pr.PoolId = mr.PoolId; + if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { + strcpy(pr.Name, "?"); + } + ua->send_msg(slot_api_full_format, type, + Slot, mr.Slot, mr.VolumeName, + edit_uint64(mr.VolBytes, ed1), + mr.VolStatus, mr.MediaType, pr.Name, + edit_uint64(mr.LastWritten, ed2), + edit_uint64(mr.LastWritten+mr.VolRetention, ed3)); + + } else { /* Media unknown */ + ua->send_msg(slot_api_full_format, + type, Slot, 0, mr.VolumeName, "?", "?", "?", "?", + "0", "0"); + + } + } else { + ua->send_msg(slot_api_empty_format, type, Slot); + } +} + +/* + * Input (output of mxt-changer listall): + * + * Drive content: D:Drive num:F:Slot loaded:Volume Name + * D:0:F:2:vol2 or D:Drive num:E + * D:1:F:42:vol42 + * D:3:E + * + * Slot content: + * S:1:F:vol1 S:Slot num:F:Volume Name + * S:2:E or S:Slot num:E + * S:3:F:vol4 + * + * If a drive is loaded, the slot *should* be empty + * + * Output: + * + * Drive list: D|Drive num|Slot loaded|Volume Name + * D|0|45|vol45 + * D|1|42|vol42 + * D|3|| + * + * Slot list: Type|Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire + * + * S|1|1|vol1|31417344|Full|LTO1-ANSI|Inc|1250858902|1282394902 + * S|2|||||||| + * S|3|3|vol4|15869952|Append|LTO1-ANSI|Inc|1250858907|1282394907 + * + * Type can be S or I (Slot or Import/Export) + * + * TODO: need to merge with status_slots() + */ +void status_content(UAContext *ua, STORE *store) +{ + int Slot, Drive; + char type; + char dev_name[MAX_NAME_LENGTH]; + char vol_name[MAX_NAME_LENGTH]; + BSOCK *sd; + vol_list_t *vl=NULL, *vol_list = NULL; + + if (!(sd=open_sd_bsock(ua))) { + return; + } + + if (!open_client_db(ua)) { + return; + } + + bstrncpy(dev_name, store->dev_name(), sizeof(dev_name)); + bash_spaces(dev_name); + /* Ask for autochanger list of volumes */ + bnet_fsend(sd, NT_("autochanger listall %s \n"), dev_name); + + /* Read and organize list of Drive, Slots and I/O Slots */ + while (bnet_recv(sd) >= 0) { + strip_trailing_junk(sd->msg); + + /* Check for returned SD messages */ + if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) && + B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) && + sd->msg[4] == ' ') { + ua->send_msg("%s\n", sd->msg); /* pass them on to user */ + continue; + } + + Drive = Slot = -1; + *vol_name = 0; + + if (sscanf(sd->msg, "D:%d:F:%d:%127s", &Drive, &Slot, vol_name) == 3) { + ua->send_msg("D|%d|%d|%s\n", Drive, Slot, vol_name); + + /* we print information on the slot if we have a volume name */ + if (*vol_name) { + /* Add Slot and VolumeName to list */ + vl = (vol_list_t *)malloc(sizeof(vol_list_t)); + vl->Slot = Slot; + vl->VolName = bstrdup(vol_name); + vl->next = vol_list; + vol_list = vl; + } + + } else if (sscanf(sd->msg, "D:%d:E", &Drive) == 1) { + ua->send_msg("D|%d||\n", Drive); + + } else if (sscanf(sd->msg, "%c:%d:F:%127s", &type, &Slot, vol_name)== 3) { + content_send_info(ua, type, Slot, vol_name); + + } else if (sscanf(sd->msg, "%c:%d:E", &type, &Slot) == 2) { + /* type can be S (slot) or I (Import/Export slot) */ + vol_list_t *prev=NULL; + for (vl = vol_list; vl; vl = vl->next) { + if (vl->Slot == Slot) { + bstrncpy(vol_name, vl->VolName, MAX_NAME_LENGTH); + + /* remove the node */ + if (prev) { + prev->next = vl->next; + } else { + vol_list = vl->next; + } + free(vl->VolName); + free(vl); + break; + } + prev = vl; + } + content_send_info(ua, type, Slot, vol_name); + + } else { + Dmsg1(10, "Discarding msg=%s\n", sd->msg); + } + } + close_sd_bsock(ua); +} /* * Print slots from AutoChanger @@ -1026,20 +1174,18 @@ void status_slots(UAContext *ua, STORE *store_r) POOL_DBR pr; vol_list_t *vl, *vol_list = NULL; MEDIA_DBR mr; - char ed1[50], ed2[50], ed3[50]; char *slot_list; int max_slots; int drive; int i=1; - /* output format */ - const char *slot_api_empty_format="%i||||||||\n"; - - /* Slot|RealSlot|Volume|Bytes|Status|MediaType|Pool|LastW|Expire */ - const char *slot_api_full_format="%i|%i|%s|%s|%s|%s|%s|%s|%s\n"; - /* Slot | Volume | Status | MediaType | Pool */ const char *slot_hformat=" %4i%c| %16s | %9s | %20s | %18s |\n"; + if (ua->api) { + status_content(ua, store_r); + return; + } + if (!open_client_db(ua)) { return; } @@ -1067,10 +1213,9 @@ void status_slots(UAContext *ua, STORE *store_r) ua->warning_msg(_("No Volumes found, or no barcodes.\n")); goto bail_out; } - if (!ua->api) { - ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n")); - ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n")); - } + ua->send_msg(_(" Slot | Volume Name | Status | Media Type | Pool |\n")); + ua->send_msg(_("------+------------------+-----------+----------------------+--------------------|\n")); + /* Walk through the list getting the media records */ for (vl=vol_list; vl; vl=vl->next) { @@ -1089,27 +1234,17 @@ void status_slots(UAContext *ua, STORE *store_r) if (!vl->VolName) { Dmsg1(100, "No VolName for Slot=%d.\n", vl->Slot); - if (ua->api) { - ua->send_msg(slot_api_empty_format, vl->Slot); - - } else { - ua->send_msg(slot_hformat, - vl->Slot, '*', - "?", "?", "?", "?"); - } + ua->send_msg(slot_hformat, + vl->Slot, '*', + "?", "?", "?", "?"); continue; } /* Hope that slots are ordered */ for (; i < vl->Slot; i++) { if (slot_list[i]) { - if (ua->api) { - ua->send_msg(slot_api_empty_format, i); - - } else { - ua->send_msg(slot_hformat, - i, ' ', "", "", "", ""); - } + ua->send_msg(slot_hformat, + i, ' ', "", "", "", ""); slot_list[i]=0; } } @@ -1125,30 +1260,15 @@ void status_slots(UAContext *ua, STORE *store_r) } db_unlock(ua->db); - if (ua->api) { - ua->send_msg(slot_api_full_format, - vl->Slot, mr.Slot, mr.VolumeName, - edit_uint64(mr.VolBytes, ed1), - mr.VolStatus, mr.MediaType, pr.Name, - edit_uint64(mr.LastWritten, ed2), - edit_uint64(mr.LastWritten+mr.VolRetention, ed3)); - } else { - /* Print information */ - ua->send_msg(slot_hformat, - vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'), - mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name); - } - + /* Print information */ + ua->send_msg(slot_hformat, + vl->Slot, ((vl->Slot==mr.Slot)?' ':'*'), + mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name); continue; } else { /* TODO: get information from catalog */ - if (ua->api) { - ua->send_msg(slot_api_empty_format, vl->Slot); - - } else { - ua->send_msg(slot_hformat, - vl->Slot, '*', - mr.VolumeName, "?", "?", "?"); - } + ua->send_msg(slot_hformat, + vl->Slot, '*', + mr.VolumeName, "?", "?", "?"); } db_unlock(ua->db); } @@ -1157,13 +1277,8 @@ void status_slots(UAContext *ua, STORE *store_r) */ for (; i <= max_slots; i++) { if (slot_list[i]) { - if (!ua->api) { - ua->send_msg(slot_api_empty_format, i); - - } else { - ua->send_msg(slot_hformat, - i, ' ', "", "", "", ""); - } + ua->send_msg(slot_hformat, + i, ' ', "", "", "", ""); slot_list[i]=0; } } diff --git a/bacula/src/dird/ua_status.c b/bacula/src/dird/ua_status.c index bbcbb6d003..889cbb2015 100644 --- a/bacula/src/dird/ua_status.c +++ b/bacula/src/dird/ua_status.c @@ -48,6 +48,7 @@ static void do_client_status(UAContext *ua, CLIENT *client, char *cmd); static void do_director_status(UAContext *ua); static void do_all_status(UAContext *ua); void status_slots(UAContext *ua, STORE *store); +void status_content(UAContext *ua, STORE *store); static char OKqstatus[] = "1000 OK .status\n"; static char DotStatusJob[] = "JobId=%s JobStatus=%c JobErrors=%d\n"; diff --git a/bacula/src/qt-console/jobs/jobs.cpp b/bacula/src/qt-console/jobs/jobs.cpp index 5e9390c15a..9029eef9b1 100644 --- a/bacula/src/qt-console/jobs/jobs.cpp +++ b/bacula/src/qt-console/jobs/jobs.cpp @@ -56,6 +56,9 @@ Jobs::Jobs() * selector tree. m_contextActions is QList of QActions */ m_contextActions.append(actionRefreshJobs); createContextMenu(); + + connect(tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), + this, SLOT(runJob())); } Jobs::~Jobs() @@ -124,16 +127,8 @@ void Jobs::populateTable() tableWidget->resizeRowsToContents(); /* make read only */ - int rcnt = tableWidget->rowCount(); - int ccnt = tableWidget->columnCount(); - for(int r=0; r < rcnt; r++) { - for(int c=0; c < ccnt; c++) { - QTableWidgetItem* item = tableWidget->item(r, c); - if (item) { - item->setFlags(Qt::ItemFlags(item->flags() & (~Qt::ItemIsEditable))); - } - } - } + tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + mainWin->waitExit(); dockPage(); } diff --git a/bacula/src/qt-console/storage/content.cpp b/bacula/src/qt-console/storage/content.cpp index 6b186c228c..93e779f4a8 100644 --- a/bacula/src/qt-console/storage/content.cpp +++ b/bacula/src/qt-console/storage/content.cpp @@ -31,6 +31,7 @@ #include #include "content.h" #include "label/label.h" +#include "mediainfo/mediainfo.h" #include "mount/mount.h" #include "util/fmtwidgetitem.h" #include "status/storstat.h" @@ -72,10 +73,27 @@ Content::Content(QString storage, QTreeWidgetItem *parentWidget) connect(pbRelease, SIGNAL(clicked()), this, SLOT(consoleRelease())); + connect(tableContent, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, + SLOT(showMediaInfo(QTableWidgetItem *))); + dockPage(); setCurrent(); } + +/* + * Subroutine to call class to show the log in the database from that job + */ +void Content::showMediaInfo(QTableWidgetItem * item) +{ + QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this); + int row = item->row(); + QString vol = tableContent->item(row, 1)->text(); + if (vol != "") { + new MediaInfo(pageSelectorTreeWidgetItem, vol); + } +} + void table_get_selection(QTableWidget *table, QString &sel) { QTableWidgetItem *item; @@ -170,25 +188,42 @@ void Content::populateContent() m_console->dir_cmd(cmd, results_all); Freeze frz(*tableContent); /* disable updating*/ + Freeze frz2(*tableTray); + Freeze frz3(*tableDrive); + tableContent->clearContents(); + tableTray->clearContents(); + tableTray->clearContents(); - // take only valid records - QStringList results = results_all.filter(QRegExp("^[0-9]+\\|")); + // take only valid records, TODO: Add D to get drive status + QStringList results = results_all.filter(QRegExp("^[IS]\\|[0-9]+\\|")); tableContent->setRowCount(results.size()); + QStringList io_results = results_all.filter(QRegExp("^I\\|[0-9]+\\|")); + tableTray->setRowCount(io_results.size()); + QString resultline; QStringList fieldlist; - int row = 0; + int row = 0, row_io=0; foreach (resultline, results) { fieldlist = resultline.split("|"); - if (fieldlist.size() < 9) + if (fieldlist.size() < 10) { + Pmsg1(0, "Discarding %s\n", resultline.data()); continue; /* some fields missing, ignore row */ + } int index=0; QStringListIterator fld(fieldlist); TableItemFormatter slotitem(*tableContent, row); + /* Slot type */ + if (fld.next() == "I") { + TableItemFormatter ioitem(*tableTray, row_io++); + ioitem.setNumericFld(0, fieldlist[1]); + ioitem.setTextFld(1, fieldlist[3]); + } + /* Slot */ slotitem.setNumericFld(index++, fld.next()); @@ -234,21 +269,41 @@ void Content::populateContent() tableContent->resizeColumnsToContents(); tableContent->resizeRowsToContents(); - /* make read only */ - int rcnt = tableContent->rowCount(); - int ccnt = tableContent->columnCount(); - for(int r=0; r < rcnt; r++) { - for(int c=0; c < ccnt; c++) { - QTableWidgetItem* item = tableContent->item(r, c); - if (item) { - item->setFlags(Qt::ItemFlags(item->flags() & (~Qt::ItemIsEditable))); - } - } - } + tableContent->setEditTriggers(QAbstractItemView::NoEditTriggers); m_populated = true; tableTray->verticalHeader()->hide(); + tableTray->setEditTriggers(QAbstractItemView::NoEditTriggers); + tableDrive->verticalHeader()->hide(); + QStringList drives = results_all.filter(QRegExp("^D\\|[0-9]+\\|")); + row = 0; + foreach (resultline, drives) { + fieldlist = resultline.split("|"); + if (fieldlist.size() < 4) + continue; /* some fields missing, ignore row */ + + int index=0; + QStringListIterator fld(fieldlist); + TableItemFormatter slotitem(*tableDrive, row); + + /* Drive type */ + fld.next(); + + /* Number */ + slotitem.setNumericFld(index++, fld.next()); + + /* Slot */ + fld.next(); + + /* Volume */ + slotitem.setTextFld(index++, fld.next()); + + row++; + } + + tableDrive->resizeRowsToContents(); + tableDrive->setEditTriggers(QAbstractItemView::NoEditTriggers); } /* diff --git a/bacula/src/qt-console/storage/content.h b/bacula/src/qt-console/storage/content.h index 94eb35ed08..dd433b57e2 100644 --- a/bacula/src/qt-console/storage/content.h +++ b/bacula/src/qt-console/storage/content.h @@ -51,6 +51,7 @@ public slots: void consoleMountStorage(); void statusStorageWindow(); void consoleUnMountStorage(); + void showMediaInfo(QTableWidgetItem * item); private slots: void populateContent(); diff --git a/bacula/src/stored/autochanger.c b/bacula/src/stored/autochanger.c index 3cec6dd19d..b42c2110d5 100644 --- a/bacula/src/stored/autochanger.c +++ b/bacula/src/stored/autochanger.c @@ -559,7 +559,7 @@ bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd) dir->fsend(_("3996 Open bpipe failed.\n")); goto bail_out; } - if (strcmp(cmd, "list") == 0) { + if (bstrcmp(cmd, "list") || bstrcmp(cmd, "listall")) { /* Get output from changer */ while (fgets(dir->msg, len, bpipe->rfd)) { dir->msglen = strlen(dir->msg); diff --git a/bacula/src/stored/dircmd.c b/bacula/src/stored/dircmd.c index b011802780..efc933e4bd 100644 --- a/bacula/src/stored/dircmd.c +++ b/bacula/src/stored/dircmd.c @@ -1019,7 +1019,10 @@ static bool changer_cmd(JCR *jcr) */ bool safe_cmd = false; - if (sscanf(dir->msg, "autochanger list %127s", devname.c_str()) == 1) { + if (sscanf(dir->msg, "autochanger listall %127s", devname.c_str()) == 1) { + cmd = "listall"; + safe_cmd = ok = true; + } else if (sscanf(dir->msg, "autochanger list %127s", devname.c_str()) == 1) { cmd = "list"; safe_cmd = ok = true; } else if (sscanf(dir->msg, "autochanger slots %127s", devname.c_str()) == 1) { -- 2.39.5