]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/medialist/mediaview.cpp
bat: Display a nice graphic on media usage depending on the average
[bacula/bacula] / bacula / src / qt-console / medialist / mediaview.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2009 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 and included
11    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 Kern Sibbald.
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 #include "bat.h"
30 #include <QAbstractEventDispatcher>
31 #include <QMenu>
32 #include <math.h>
33 #include "mediaview.h"
34 #include "mediaedit/mediaedit.h"
35 #include "mediainfo/mediainfo.h"
36 #include "joblist/joblist.h"
37 #include "relabel/relabel.h"
38 #include "run/run.h"
39 #include "util/fmtwidgetitem.h"
40
41 MediaView::MediaView()
42 {
43    setupUi(this);
44    m_name = tr("Media");
45    pgInitialize();
46    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
47    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/cartridge.png")));
48    connect(m_pbApply, SIGNAL(pressed()), this, SLOT(applyPushed()));
49    connect(m_pbEdit, SIGNAL(pressed()), this, SLOT(editPushed()));
50    connect(m_pbPurge, SIGNAL(pressed()), this, SLOT(purgePushed()));
51    connect(m_pbDelete, SIGNAL(pressed()), this, SLOT(deletePushed()));
52    connect(m_pbPrune, SIGNAL(pressed()), this, SLOT(prunePushed()));
53    connect(m_tableMedia, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), 
54            this, SLOT(showInfoForMedia(QTableWidgetItem *)));
55
56    /* mp_treeWidget, Storage Tree Tree Widget inherited from ui_medialist.h */
57    m_populated = false;
58    m_checkcurwidget = true;
59    m_closeable = false;
60 }
61
62 void MediaView::showInfoForMedia(QTableWidgetItem * item)
63 {
64    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
65    int row = item->row();
66    QString vol = m_tableMedia->item(row, 0)->text();
67    new MediaInfo(pageSelectorTreeWidgetItem, vol);
68 //   connect(j, SIGNAL(destroyed()), this, SLOT(populateTree()));
69 }
70
71 MediaView::~MediaView()
72 {
73 }
74
75 void MediaView::applyPushed()
76 {
77    populateTable();
78 }
79
80 void MediaView::editPushed()
81 {
82    QStringList sel;
83    QString cmd;
84    getSelection(sel);
85    
86    for(int i=0; i<sel.count(); i++) {
87       cmd = sel.at(i);
88       new MediaEdit(mainWin->getFromHash(this), cmd);
89    }
90 }
91
92 void MediaView::purgePushed()
93 {
94    if (QMessageBox::warning(this, "Bat",
95       tr("Are you sure you want to purge ??  !!!.\n"
96 "The Purge command will delete associated Catalog database records from Jobs and"
97 " Volumes without considering the retention period. Purge  works only on the"
98 " Catalog database and does not affect data written to Volumes. This command can"
99 " be dangerous because you can delete catalog records associated with current"
100 " backups of files, and we recommend that you do not use it unless you know what"
101 " you are doing.\n"
102       "Press OK to proceed with the purge operation?"),
103       QMessageBox::Ok | QMessageBox::Cancel)
104       == QMessageBox::Cancel) { return; }
105
106    QStringList lst;
107    QString cmd;
108    getSelection(lst);
109    for(int i=0; i<lst.count(); i++) {
110       cmd = "purge volume=" + lst.at(i);
111       consoleCommand(cmd);
112    }
113    populateTable();
114 }
115
116 bool MediaView::getSelection(QStringList &list)
117 {
118    QList<QTableWidgetItem*> items = m_tableMedia->selectedItems();
119    QTableWidgetItem *it;
120    int row;
121    int *tab;
122    int nb = items.count();
123    if (!nb) {
124       return false;
125    }
126    tab = (int *) malloc (nb * sizeof(int));
127    memset(tab, 0, sizeof(int)*nb);
128    for (int i = 0; i < nb; ++i) {
129       row = items[i]->row();
130       if (!tab[row]) {
131          tab[row]=1;
132          it = m_tableMedia->item(row, 0);
133          list.append(it->text());
134       }
135    }
136    free(tab);
137    return list.count() > 0;
138 }
139
140 void MediaView::prunePushed()
141 {
142    QStringList sel;
143    QString cmd;
144    getSelection(sel);
145
146    for(int i=0; i<sel.count(); i++) {
147       cmd = "prune volume=" + sel.at(i);
148       consoleCommand(cmd);
149    }
150 }
151
152
153 void MediaView::deletePushed()
154 {
155    if (QMessageBox::warning(this, "Bat",
156       tr("Are you sure you want to delete??  !!!.\n"
157 "This delete command is used to delete a Volume record and all associated catalog"
158 " records that were created. This command operates only on the Catalog"
159 " database and has no effect on the actual data written to a Volume. This"
160 " command can be dangerous and we strongly recommend that you do not use"
161 " it unless you know what you are doing.  All Jobs and all associated"
162 " records (File and JobMedia) will be deleted from the catalog."
163       "Press OK to proceed with delete operation.?"),
164       QMessageBox::Ok | QMessageBox::Cancel)
165       == QMessageBox::Cancel) { return; }
166
167    QStringList lst;
168    QString cmd;
169    getSelection(lst);
170    for(int i=0; i<lst.count(); i++) {
171       cmd = "delete volume=" + lst.at(i);
172       consoleCommand(cmd);
173    }
174    populateTable();
175 }
176
177 void MediaView::populateForm()
178 {
179    m_cbPool->clear();
180    m_cbPool->addItem("");
181    m_cbPool->addItems(m_console->pool_list);
182
183    m_cbStatus->clear();
184    m_cbStatus->addItem("");
185    m_cbStatus->addItems(m_console->volstatus_list);
186
187    m_cbMediaType->clear();
188    m_cbMediaType->addItem("");
189    m_cbMediaType->addItems(m_console->mediatype_list);
190
191    m_cbLocation->clear();
192    m_cbLocation->addItem("");
193    m_cbLocation->addItems(m_console->location_list);
194 }
195
196 /* 
197  * If chkExpired button is checked, we can remove all non Expired
198  * entries
199  */
200 void MediaView::filterExipired(QStringList &list)
201 {
202    utime_t t, now = time(NULL);
203    QString resultline, stat, LastWritten;
204    QStringList fieldlist;
205
206    /* We should now in advance how many rows we will have */
207    if (m_chkExpired->isChecked()) {
208       for (int i=list.size() -1; i >= 0; i--) {
209          fieldlist = list.at(i).split("\t");
210          ASSERT(fieldlist.size() != 9);
211          LastWritten = fieldlist.at(7);
212          if (LastWritten == "") {
213             list.removeAt(i);
214
215          } else {
216             stat = fieldlist.at(8);
217             t = str_to_utime(LastWritten.toAscii().data());
218             t = t + stat.toULongLong();
219             if (t > now) {
220                list.removeAt(i);
221             }
222          }
223       }
224    }
225 }
226
227 /*
228  * The main meat of the class!!  The function that querries the director and 
229  * creates the widgets with appropriate values.
230  */
231 void MediaView::populateTable()
232 {
233    utime_t t;
234    time_t ttime;
235    QString stat, resultline, query;
236    QString str_usage;
237    QHash<QString, float> hash_size;
238    QStringList fieldlist, results;
239    char buf[256];
240    float usage;
241    struct tm tm;
242
243    m_populated = true;
244
245    Freeze frz(*m_tableMedia); /* disable updating*/
246    QStringList where;
247    QString cmd;
248    if (m_cbPool->currentText() != "") {
249       cmd = " Pool.Name = '" + m_cbPool->currentText() + "'";
250       where.append(cmd);
251    } 
252
253    if (m_cbStatus->currentText() != "") {
254       cmd = " Media.VolStatus = '" + m_cbStatus->currentText() + "'";
255       where.append(cmd);
256    }
257
258    if (m_cbStatus->currentText() != "") {
259       cmd = " Media.VolStatus = '" + m_cbStatus->currentText() + "'";
260       where.append(cmd);
261    }
262
263    if (m_cbMediaType->currentText() != "") {
264       cmd = " Media.MediaType = '" + m_cbMediaType->currentText() + "'";
265       where.append(cmd);
266    }
267
268    if (m_cbLocation->currentText() != "") {
269       cmd = " Location.Location = '" + m_cbLocation->currentText() + "'";
270       where.append(cmd);
271    }
272
273    if (m_textName->text() != "") {
274       cmd = " Media.VolumeName like '%" + m_textName->text() + "%'";
275       where.append(cmd);
276    }
277
278    if (where.size() > 0) {
279       cmd = " WHERE " + where.join(" AND ");
280    } else {
281       cmd = "";
282    }
283
284    query =
285       "SELECT AVG(VolBytes) AS size, COUNT(1) as nb, "
286       "MediaType  FROM Media "
287       "WHERE VolStatus IN ('Full', 'Used') "
288       "GROUP BY MediaType";
289
290    if (m_console->sql_cmd(query, results)) {
291       foreach (resultline, results) {
292          fieldlist = resultline.split("\t");
293          if (fieldlist.at(1).toInt() >= 1) { 
294             //           MediaType
295             hash_size[fieldlist.at(2)] 
296                = fieldlist.at(0).toFloat(); 
297          }
298       }
299    }      
300    
301    m_tableMedia->clearContents();
302    query = 
303       "SELECT VolumeName, InChanger, "
304       "Slot, MediaType, VolStatus, VolBytes, Pool.Name,  "
305       "LastWritten, Media.VolRetention "
306       "FROM Media JOIN Pool USING (PoolId) "
307       "LEFT JOIN Location USING (LocationId) "
308       + cmd + 
309       " ORDER BY VolumeName LIMIT " + m_sbLimit->cleanText();
310
311 //   Pmsg1(000, "MediaView query cmd : %s\n",query.toUtf8().data());
312    m_tableMedia->setSortingEnabled(false); /* Don't sort during insert */
313    results.clear();
314    if (m_console->sql_cmd(query, results)) {
315       int row=0;
316       filterExipired(results);
317       m_tableMedia->setRowCount(results.size());
318
319       foreach (resultline, results) { // should have only one result
320          QString VolBytes, MediaType, LastWritten, VolStatus;
321          fieldlist = resultline.split("\t");
322          if (fieldlist.size() != 10) {
323             continue;
324          }
325          QStringListIterator fld(fieldlist);
326          int index=0;
327          TableItemFormatter mediaitem(*m_tableMedia, row);
328
329          /* VolumeName */
330          mediaitem.setTextFld(index++, fld.next()); 
331          
332          /* Online */
333          mediaitem.setInChanger(index++, fld.next());
334          fld.next();            // Slot
335
336          MediaType = fld.next();
337          VolStatus = fld.next();
338
339          /* Volume bytes */
340          VolBytes = fld.next();
341          mediaitem.setBytesFld(index++, VolBytes);
342
343          /* Usage */
344          usage = 0;
345          if (hash_size.contains(MediaType) &&
346              hash_size[MediaType] != 0) 
347          {
348             usage = VolBytes.toLongLong() * 100 / hash_size[MediaType];
349          }
350          mediaitem.setPercent(index++, usage);
351
352          /* Volstatus */
353          mediaitem.setVolStatusFld(index++, VolStatus);
354
355          /* Pool */
356          mediaitem.setTextFld(index++, fld.next());
357
358          /* MediaType */
359          mediaitem.setTextFld(index++, MediaType);
360
361          LastWritten = fld.next();
362          buf[0] = 0;
363          if (LastWritten != "") {
364             stat = fld.next();  // VolUseDuration
365             t = str_to_utime(LastWritten.toAscii().data());
366             t = t + stat.toULongLong();
367             ttime = t;
368             localtime_r(&ttime, &tm);         
369             strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
370          }
371          
372          /* LastWritten */
373          mediaitem.setTextFld(index++, LastWritten);
374
375          /* When expired */
376          mediaitem.setTextFld(index++, buf);
377          row++;
378       }
379    }
380    m_tableMedia->resizeColumnsToContents();
381    m_tableMedia->resizeRowsToContents();
382    m_tableMedia->verticalHeader()->hide();
383    m_tableMedia->setSortingEnabled(true);
384
385    /* make read only */
386    m_tableMedia->setEditTriggers(QAbstractItemView::NoEditTriggers);
387 }
388
389 /*
390  * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
391  * The tree has been populated.
392  */
393 void MediaView::PgSeltreeWidgetClicked()
394 {
395    if (!m_populated) {
396       populateForm();
397       populateTable();
398    }
399    dockPage();
400 }
401
402 /*
403  * Virtual function which is called when this page is visible on the stack
404  */
405 void MediaView::currentStackItem()
406 {
407    if(!m_populated) {
408       populateForm();
409       populateTable();
410    }
411 }
412
413 // /*
414 //  * Called from the signal of the context sensitive menu to relabel!
415 //  */
416 // void MediaView::relabelVolume()
417 // {
418 //    setConsoleCurrent();
419 //    new relabelDialog(m_console, m_currentVolumeName);
420 // }
421 // 
422 // /*
423 //  * Called from the signal of the context sensitive menu to purge!
424 //  */
425 // void MediaView::allVolumesFromPool()
426 // {
427 //    QString cmd = "update volume AllFromPool=" + m_currentVolumeName;
428 //    consoleCommand(cmd);
429 //    populateTable();
430 // }
431 // 
432 // void MediaView::allVolumes()
433 // {
434 //    QString cmd = "update volume allfrompools";
435 //    consoleCommand(cmd);
436 //    populateTable();
437 // }
438 // 
439 //  /*
440 //   * Called from the signal of the context sensitive menu to purge!
441 //   */
442 //  void MediaView::volumeFromPool()
443 //  {
444 //     QTreeWidgetItem *currentItem = mp_treeWidget->currentItem();
445 //     QTreeWidgetItem *parent = currentItem->parent();
446 //     QString pool = parent->text(0);
447 //     QString cmd;
448 //     cmd = "update volume=" + m_currentVolumeName + " frompool=" + pool;
449 //     consoleCommand(cmd);
450 //     populateTable();
451 //  }
452 //