]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/medialist/medialist.cpp
Remove jobq.c constraint that read and write SD must be
[bacula/bacula] / bacula / src / qt-console / medialist / medialist.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2008 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 /*
30  *   Version $Id$
31  *
32  *  MediaList Class
33  *
34  *   Dirk Bartley, March 2007
35  *
36  */ 
37
38 #include <QAbstractEventDispatcher>
39 #include <QMenu>
40 #include <math.h>
41 #include "bat.h"
42 #include "medialist.h"
43 #include "mediaedit/mediaedit.h"
44 #include "joblist/joblist.h"
45 #include "relabel/relabel.h"
46 #include "run/run.h"
47 #include "util/fmtwidgetitem.h"
48
49 MediaList::MediaList()
50 {
51    setupUi(this);
52    m_name = tr("Media");
53    pgInitialize();
54    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
55    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/cartridge.png")));
56
57    /* mp_treeWidget, Storage Tree Tree Widget inherited from ui_medialist.h */
58    m_populated = false;
59    m_checkcurwidget = true;
60    m_closeable = false;
61    /* add context sensitive menu items specific to this classto the page
62     * selector tree. m_contextActions is QList of QActions */
63    m_contextActions.append(actionRefreshMediaList);
64    dockPage();
65 }
66
67 MediaList::~MediaList()
68 {
69    if (m_populated)
70       writeExpandedSettings();
71 }
72
73 /*
74  * The main meat of the class!!  The function that querries the director and 
75  * creates the widgets with appropriate values.
76  */
77 void MediaList::populateTree()
78 {
79    if (m_populated)
80       writeExpandedSettings();
81    m_populated = true;
82
83    QTreeWidgetItem *pooltreeitem;
84
85    if (!m_console->preventInUseConnect())
86        return;
87
88    QStringList headerlist = (QStringList()
89       << tr("Volume Name") << tr("Id") << tr("Status") << tr("Enabled") << tr("Bytes") << tr("Files")
90       << tr("Jobs") << tr("Retention") << tr("Media Type") << tr("Slot") << tr("Use Duration")
91       << tr("Max Jobs") << tr("Max Files") << tr("Max Bytes") << tr("Recycle")
92       << tr("RecyclePool") << tr("Last Written"));
93
94    m_checkcurwidget = false;
95    mp_treeWidget->clear();
96    m_checkcurwidget = true;
97    mp_treeWidget->setColumnCount(headerlist.count());
98    m_topItem = new QTreeWidgetItem(mp_treeWidget);
99    m_topItem->setText(0, tr("Pools"));
100    m_topItem->setData(0, Qt::UserRole, 0);
101    m_topItem->setExpanded(true);
102    
103    mp_treeWidget->setHeaderLabels(headerlist);
104
105    QSettings settings(m_console->m_dir->name(), "bat");
106    settings.beginGroup("MediaListTreeExpanded");
107    QString query;
108
109    foreach (QString pool_listItem, m_console->pool_list) {
110       pooltreeitem = new QTreeWidgetItem(m_topItem);
111       pooltreeitem->setText(0, pool_listItem);
112       pooltreeitem->setData(0, Qt::UserRole, 1);
113       if(settings.contains(pool_listItem)) {
114          pooltreeitem->setExpanded(settings.value(pool_listItem).toBool());
115       } else {
116          pooltreeitem->setExpanded(true);
117       }
118
119       query =  "SELECT Media.VolumeName AS Media, "
120          " Media.MediaId AS Id, Media.VolStatus AS VolStatus,"
121          " Media.Enabled AS Enabled, Media.VolBytes AS Bytes,"
122          " Media.VolFiles AS FileCount, Media.VolJobs AS JobCount,"
123          " Media.VolRetention AS VolumeRetention, Media.MediaType AS MediaType,"
124          " Media.InChanger AS InChanger, Media.Slot AS Slot, "
125          " Media.VolUseDuration AS UseDuration,"
126          " Media.MaxVolJobs AS MaxJobs, Media.MaxVolFiles AS MaxFiles,"
127          " Media.MaxVolBytes AS MaxBytes, Media.Recycle AS Recycle,"
128          " Pol.Name AS RecyclePool, Media.LastWritten AS LastWritten"
129          " FROM Media"
130          " JOIN Pool ON (Media.PoolId=Pool.PoolId)"
131          " LEFT OUTER JOIN Pool AS Pol ON (Media.RecyclePoolId=Pol.PoolId)"
132          " WHERE";
133       query += " Pool.Name='" + pool_listItem + "'";
134       query += " ORDER BY Media";
135    
136       if (mainWin->m_sqlDebug) {
137          Pmsg1(000, "MediaList query cmd : %s\n",query.toUtf8().data());
138       }
139       QStringList results;
140       if (m_console->sql_cmd(query, results)) {
141          QStringList fieldlist;
142
143          /* Iterate through the lines of results. */
144          foreach (QString resultline, results) {
145             fieldlist = resultline.split("\t");
146
147             if (fieldlist.size() < 18)
148                continue; // some fields missing, ignore row
149
150             int index = 0;
151             TreeItemFormatter mediaitem(*pooltreeitem, 2);
152   
153             /* Iterate through fields in the record */
154             QStringListIterator fld(fieldlist);
155
156             /* volname */
157             mediaitem.setTextFld(index++, fld.next()); 
158
159             /* id */
160             mediaitem.setNumericFld(index++, fld.next()); 
161
162             /* status */
163             mediaitem.setVolStatusFld(index++, fld.next());
164
165             /* enabled */
166             mediaitem.setBoolFld(index++, fld.next());
167
168             /* bytes */
169             mediaitem.setBytesFld(index++, fld.next());
170
171             /* files */
172             mediaitem.setNumericFld(index++, fld.next()); 
173
174             /* jobs */
175             mediaitem.setNumericFld(index++, fld.next()); 
176
177             /* retention */
178             mediaitem.setDurationFld(index++, fld.next());
179
180             /* media type */
181             mediaitem.setTextFld(index++, fld.next()); 
182
183             /* inchanger + slot */
184             int inchanger = fld.next().toInt();
185             if (inchanger) {
186                mediaitem.setNumericFld(index++, fld.next()); 
187             }
188             else {
189                /* volume not in changer, show blank slot */
190                mediaitem.setNumericFld(index++, ""); 
191                fld.next();
192             }
193
194             /* use duration */
195             mediaitem.setDurationFld(index++, fld.next());
196
197             /* max jobs */
198             mediaitem.setNumericFld(index++, fld.next()); 
199
200             /* max files */
201             mediaitem.setNumericFld(index++, fld.next()); 
202
203             /* max bytes */
204             mediaitem.setBytesFld(index++, fld.next());
205
206             /* recycle */
207             mediaitem.setBoolFld(index++, fld.next());
208
209             /* recycle pool */
210             mediaitem.setTextFld(index++, fld.next()); 
211
212             /* last written */
213             mediaitem.setTextFld(index++, fld.next()); 
214
215          } /* foreach resultline */
216       } /* if results from query */
217    } /* foreach pool_listItem */
218    settings.endGroup();
219    /* Resize the columns */
220    for(int cnter=0; cnter<headerlist.count(); cnter++) {
221       mp_treeWidget->resizeColumnToContents(cnter);
222    }
223 }
224
225 /*
226  * Called from the signal of the context sensitive menu!
227  */
228 void MediaList::editVolume()
229 {
230    MediaEdit* edit = new MediaEdit(mainWin->getFromHash(this), m_currentVolumeId);
231    connect(edit, SIGNAL(destroyed()), this, SLOT(populateTree()));
232 }
233
234 /*
235  * Called from the signal of the context sensitive menu!
236  */
237 void MediaList::showJobs()
238 {
239    QTreeWidgetItem *parentItem = mainWin->getFromHash(this);
240    mainWin->createPageJobList(m_currentVolumeName, "", "", "", parentItem);
241 }
242
243 /*
244  * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
245  * The tree has been populated.
246  */
247 void MediaList::PgSeltreeWidgetClicked()
248 {
249    if (!m_populated) {
250       populateTree();
251       createContextMenu();
252    }
253 }
254
255 /*
256  * Added to set the context menu policy based on currently active treeWidgetItem
257  * signaled by currentItemChanged
258  */
259 void MediaList::treeItemChanged(QTreeWidgetItem *currentwidgetitem, QTreeWidgetItem *previouswidgetitem )
260 {
261    /* m_checkcurwidget checks to see if this is during a refresh, which will segfault */
262    if (m_checkcurwidget) {
263       /* The Previous item */
264       if (previouswidgetitem) { /* avoid a segfault if first time */
265          foreach(QAction* mediaAction, mp_treeWidget->actions()) {
266             mp_treeWidget->removeAction(mediaAction);
267          }
268       }
269
270       int treedepth = currentwidgetitem->data(0, Qt::UserRole).toInt();
271       m_currentVolumeName=currentwidgetitem->text(0);
272       mp_treeWidget->addAction(actionRefreshMediaList);
273       if (treedepth == 2){
274          m_currentVolumeId=currentwidgetitem->text(1);
275          mp_treeWidget->addAction(actionEditVolume);
276          mp_treeWidget->addAction(actionListJobsOnVolume);
277          mp_treeWidget->addAction(actionDeleteVolume);
278          mp_treeWidget->addAction(actionPruneVolume);
279          mp_treeWidget->addAction(actionPurgeVolume);
280          mp_treeWidget->addAction(actionRelabelVolume);
281          mp_treeWidget->addAction(actionVolumeFromPool);
282       } else if (treedepth == 1) {
283          mp_treeWidget->addAction(actionAllVolumesFromPool);
284       }
285    }
286 }
287
288 /* 
289  * Setup a context menu 
290  * Made separate from populate so that it would not create context menu over and
291  * over as the tree is repopulated.
292  */
293 void MediaList::createContextMenu()
294 {
295    mp_treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
296    connect(actionEditVolume, SIGNAL(triggered()), this, SLOT(editVolume()));
297    connect(actionListJobsOnVolume, SIGNAL(triggered()), this, SLOT(showJobs()));
298    connect(actionDeleteVolume, SIGNAL(triggered()), this, SLOT(deleteVolume()));
299    connect(actionPurgeVolume, SIGNAL(triggered()), this, SLOT(purgeVolume()));
300    connect(actionPruneVolume, SIGNAL(triggered()), this, SLOT(pruneVolume()));
301    connect(actionRelabelVolume, SIGNAL(triggered()), this, SLOT(relabelVolume()));
302    connect(mp_treeWidget, SIGNAL(
303            currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
304            this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
305    /* connect to the action specific to this pages class */
306    connect(actionRefreshMediaList, SIGNAL(triggered()), this,
307                 SLOT(populateTree()));
308    connect(actionAllVolumes, SIGNAL(triggered()), this, SLOT(allVolumes()));
309    connect(actionAllVolumesFromPool, SIGNAL(triggered()), this, SLOT(allVolumesFromPool()));
310    connect(actionVolumeFromPool, SIGNAL(triggered()), this, SLOT(volumeFromPool()));
311 }
312
313 /*
314  * Virtual function which is called when this page is visible on the stack
315  */
316 void MediaList::currentStackItem()
317 {
318    if(!m_populated) {
319       populateTree();
320       /* Create the context menu for the medialist tree */
321       createContextMenu();
322    }
323 }
324
325 /*
326  * Called from the signal of the context sensitive menu to delete a volume!
327  */
328 void MediaList::deleteVolume()
329 {
330    if (QMessageBox::warning(this, "Bat",
331       tr("Are you sure you want to delete??  !!!.\n"
332 "This delete command is used to delete a Volume record and all associated catalog"
333 " records that were created. This command operates only on the Catalog"
334 " database and has no effect on the actual data written to a Volume. This"
335 " command can be dangerous and we strongly recommend that you do not use"
336 " it unless you know what you are doing.  All Jobs and all associated"
337 " records (File and JobMedia) will be deleted from the catalog."
338       "Press OK to proceed with delete operation.?"),
339       QMessageBox::Ok | QMessageBox::Cancel)
340       == QMessageBox::Cancel) { return; }
341
342    QString cmd("delete volume=");
343    cmd += m_currentVolumeName;
344    consoleCommand(cmd);
345 }
346
347 /*
348  * Called from the signal of the context sensitive menu to purge!
349  */
350 void MediaList::purgeVolume()
351 {
352    if (QMessageBox::warning(this, "Bat",
353       tr("Are you sure you want to purge ??  !!!.\n"
354 "The Purge command will delete associated Catalog database records from Jobs and"
355 " Volumes without considering the retention period. Purge  works only on the"
356 " Catalog database and does not affect data written to Volumes. This command can"
357 " be dangerous because you can delete catalog records associated with current"
358 " backups of files, and we recommend that you do not use it unless you know what"
359 " you are doing.\n"
360       "Press OK to proceed with the purge operation?"),
361       QMessageBox::Ok | QMessageBox::Cancel)
362       == QMessageBox::Cancel) { return; }
363
364    QString cmd("purge volume=");
365    cmd += m_currentVolumeName;
366    consoleCommand(cmd);
367    populateTree();
368 }
369
370 /*
371  * Called from the signal of the context sensitive menu to prune!
372  */
373 void MediaList::pruneVolume()
374 {
375    new prunePage(m_currentVolumeName, "");
376 }
377
378 /*
379  * Called from the signal of the context sensitive menu to relabel!
380  */
381 void MediaList::relabelVolume()
382 {
383    setConsoleCurrent();
384    new relabelDialog(m_console, m_currentVolumeName);
385 }
386
387 /*
388  * Called from the signal of the context sensitive menu to purge!
389  */
390 void MediaList::allVolumesFromPool()
391 {
392    QString cmd = "update volume AllFromPool=" + m_currentVolumeName;
393    consoleCommand(cmd);
394    populateTree();
395 }
396
397 void MediaList::allVolumes()
398 {
399    QString cmd = "update volume allfrompools";
400    consoleCommand(cmd);
401    populateTree();
402 }
403
404 /*
405  * Called from the signal of the context sensitive menu to purge!
406  */
407 void MediaList::volumeFromPool()
408 {
409    QTreeWidgetItem *currentItem = mp_treeWidget->currentItem();
410    QTreeWidgetItem *parent = currentItem->parent();
411    QString pool = parent->text(0);
412    QString cmd;
413    cmd = "update volume=" + m_currentVolumeName + " frompool=" + pool;
414    consoleCommand(cmd);
415    populateTree();
416 }
417
418 /*
419  * Write settings to save expanded states of the pools
420  */
421 void MediaList::writeExpandedSettings()
422 {
423    QSettings settings(m_console->m_dir->name(), "bat");
424    settings.beginGroup("MediaListTreeExpanded");
425    int childcount = m_topItem->childCount();
426    for (int cnt=0; cnt<childcount; cnt++) {
427       QTreeWidgetItem *poolitem = m_topItem->child(cnt);
428       settings.setValue(poolitem->text(0), poolitem->isExpanded());
429    }
430    settings.endGroup();
431 }