ui: tape: add BackupOverview Panel
shows all tapes with the relevant info * which pool it belongs to * what backups are on it * which media-set * location * etc. This is very rough, and maybe not the best way to display this information. It may make sense to reverse the tree, i.e. having pools at top-level, then media-sets, then tapes, then snapshots.. Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
parent
80a3749088
commit
07d6c0967d
|
@ -20,6 +20,7 @@ TAPE_UI_FILES= \
|
||||||
tape/window/LabelMedia.js \
|
tape/window/LabelMedia.js \
|
||||||
tape/window/PoolEdit.js \
|
tape/window/PoolEdit.js \
|
||||||
tape/window/TapeBackup.js \
|
tape/window/TapeBackup.js \
|
||||||
|
tape/BackupOverview.js \
|
||||||
TapeManagement.js
|
TapeManagement.js
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
Ext.define('PBS.TapeManagement.BackupOverview', {
|
||||||
|
extend: 'Ext.tree.Panel',
|
||||||
|
alias: 'widget.pbsBackupOverview',
|
||||||
|
|
||||||
|
controller: {
|
||||||
|
xclass: 'Ext.app.ViewController',
|
||||||
|
|
||||||
|
backup: function() {
|
||||||
|
let me = this;
|
||||||
|
Ext.create('PBS.TapeManagement.TapeBackupWindow', {
|
||||||
|
listeners: {
|
||||||
|
destroy: function() {
|
||||||
|
me.reload();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).show();
|
||||||
|
},
|
||||||
|
|
||||||
|
reload: async function() {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
|
||||||
|
Proxmox.Utils.setErrorMask(view, true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let list_response = await PBS.Async.api2({
|
||||||
|
url: '/api2/extjs/tape/media/list',
|
||||||
|
});
|
||||||
|
let list = list_response.result.data.sort(
|
||||||
|
(a, b) => a['label-text'].localeCompare(b['label-text']),
|
||||||
|
);
|
||||||
|
|
||||||
|
let content = {};
|
||||||
|
|
||||||
|
let content_response = await PBS.Async.api2({
|
||||||
|
url: '/api2/extjs/tape/media/content',
|
||||||
|
});
|
||||||
|
|
||||||
|
let content_list = content_response.result.data.sort(
|
||||||
|
(a, b) => a.snapshot.localeCompare(b.snapshot),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let entry of content_list) {
|
||||||
|
let tape = entry['label-text'];
|
||||||
|
entry['label-text'] = entry.snapshot;
|
||||||
|
entry.leaf = true;
|
||||||
|
if (content[tape] === undefined) {
|
||||||
|
content[tape] = [entry];
|
||||||
|
} else {
|
||||||
|
content[tape].push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let child of list) {
|
||||||
|
let tape = child['label-text'];
|
||||||
|
if (content[tape]) {
|
||||||
|
child.children = content[tape];
|
||||||
|
child.leaf = false;
|
||||||
|
} else {
|
||||||
|
child.leaf = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
view.setRootNode({
|
||||||
|
expanded: true,
|
||||||
|
children: list,
|
||||||
|
});
|
||||||
|
|
||||||
|
Proxmox.Utils.setErrorMask(view, false);
|
||||||
|
} catch (error) {
|
||||||
|
Proxmox.Utils.setErrorMask(view, error.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
activate: 'reload',
|
||||||
|
},
|
||||||
|
|
||||||
|
store: {
|
||||||
|
sorters: 'label-text',
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
rootVisible: false,
|
||||||
|
|
||||||
|
tbar: [
|
||||||
|
{
|
||||||
|
text: gettext('Reload'),
|
||||||
|
handler: 'reload',
|
||||||
|
},
|
||||||
|
'-',
|
||||||
|
{
|
||||||
|
text: gettext('New Backup'),
|
||||||
|
handler: 'backup',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
xtype: 'treecolumn',
|
||||||
|
text: gettext('Tape/Backup'),
|
||||||
|
dataIndex: 'label-text',
|
||||||
|
flex: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: gettext('Location'),
|
||||||
|
dataIndex: 'location',
|
||||||
|
flex: 1,
|
||||||
|
renderer: function(value) {
|
||||||
|
if (!value) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
let result;
|
||||||
|
if ((result = /^online-(.+)$/.exec(value)) !== null) {
|
||||||
|
return Ext.htmlEncode(result[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: gettext('Status'),
|
||||||
|
dataIndex: 'status',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: gettext('Media Set'),
|
||||||
|
dataIndex: 'media-set-name',
|
||||||
|
flex: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: gettext('Pool'),
|
||||||
|
dataIndex: 'pool',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: gettext('Sequence Nr.'),
|
||||||
|
dataIndex: 'seq-nr',
|
||||||
|
flex: 0.5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: gettext('Backup Time'),
|
||||||
|
dataIndex: 'backup-time',
|
||||||
|
renderer: (time) => time !== undefined ? new Date(time*1000) : "",
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue