proxmox-backup/www/tape/BackupOverview.js

298 lines
6.7 KiB
JavaScript
Raw Normal View History

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();
},
},
autoShow: true,
});
},
restoreBackups: function(view, rI, cI, item, e, rec) {
let me = this;
let mediaset = rec.data.is_media_set ? rec.data.text : rec.data['media-set'];
Ext.create('PBS.TapeManagement.TapeRestoreWindow', {
autoShow: true,
uuid: rec.data['media-set-uuid'],
prefilter: rec.data.prefilter,
mediaset,
});
},
loadContent: async function() {
let me = this;
let content_response = await PBS.Async.api2({
url: '/api2/extjs/tape/media/list?update-status=false',
});
let data = {};
for (const entry of content_response.result.data) {
let pool = entry.pool;
if (pool === undefined) {
continue; // pools not belonging to a pool cannot contain data
}
let media_set = entry['media-set-name'];
if (media_set === undefined) {
continue; // tape does not belong to media-set (yet))
}
if (data[pool] === undefined) {
data[pool] = {};
}
if (data[pool][media_set] === undefined) {
data[pool][media_set] = entry;
data[pool][media_set].text = media_set;
data[pool][media_set].restore =true;
data[pool][media_set].tapes = 1;
data[pool][media_set]['seq-nr'] = undefined;
data[pool][media_set].is_media_set = true;
} else {
data[pool][media_set].tapes++;
}
}
let list = [];
for (const [pool, media_sets] of Object.entries(data)) {
let pool_entry = Ext.create('Ext.data.TreeModel', {
text: pool,
leaf: false,
});
let children = [];
for (const media_set of Object.values(media_sets)) {
let entry = Ext.create('Ext.data.TreeModel', media_set);
entry.on('beforeexpand', (node) => me.beforeExpand(node));
children.push(entry);
}
pool_entry.set('children', children);
list.push(pool_entry);
}
return list;
},
reload: async function() {
let me = this;
let view = me.getView();
Proxmox.Utils.setErrorMask(view, true);
try {
let list = await me.loadContent();
view.setRootNode({
expanded: true,
children: list,
});
Proxmox.Utils.setErrorMask(view, false);
} catch (error) {
Proxmox.Utils.setErrorMask(view, error.toString());
}
},
loadMediaSet: async function(node) {
let me = this;
let view = me.getView();
Proxmox.Utils.setErrorMask(view, true);
const media_set_uuid = node.data['media-set-uuid'];
const media_set = node.data.text;
try {
let list = await PBS.Async.api2({
method: 'GET',
url: `/api2/extjs/tape/media/content`,
params: {
'media-set': media_set_uuid,
},
});
list.result.data.sort(function(a, b) {
let storeRes = a.store.localeCompare(b.store);
if (storeRes === 0) {
return a.snapshot.localeCompare(b.snapshot);
} else {
return storeRes;
}
});
let stores = {};
for (let entry of list.result.data) {
entry.text = entry.snapshot;
entry.restore = true;
entry.leaf = true;
entry.children = [];
entry['media-set'] = media_set;
entry.prefilter = {
store: entry.store,
snapshot: entry.snapshot,
};
let iconCls = PBS.Utils.get_type_icon_cls(entry.snapshot);
if (iconCls !== '') {
entry.iconCls = `fa ${iconCls}`;
}
let store = entry.store;
let tape = entry['label-text'];
if (stores[store] === undefined) {
stores[store] = {
text: store,
'media-set-uuid': entry['media-set-uuid'],
iconCls: 'fa fa-database',
tapes: {},
};
}
if (stores[store].tapes[tape] === undefined) {
stores[store].tapes[tape] = {
text: tape,
'media-set-uuid': entry['media-set-uuid'],
'seq-nr': entry['seq-nr'],
iconCls: 'pbs-icon-tape',
expanded: true,
children: [],
};
}
let [type, group, _id] = PBS.Utils.parse_snapshot_id(entry.snapshot);
let children = stores[store].tapes[tape].children;
let text = `${type}/${group}`;
if (children.length < 1 || children[children.length - 1].text !== text) {
children.push({
text,
'media-set-uuid': entry['media-set-uuid'],
leaf: false,
restore: true,
prefilter: {
store,
snapshot: `${type}/${group}/`,
},
'media-set': media_set,
iconCls: `fa ${iconCls}`,
children: [],
});
}
children[children.length - 1].children.push(entry);
}
let storeList = Object.values(stores);
let storeNameList = Object.keys(stores);
let expand = storeList.length === 1;
for (const store of storeList) {
store.children = Object.values(store.tapes);
store.expanded = expand;
delete store.tapes;
node.appendChild(store);
}
if (list.result.data.length === 0) {
node.set('leaf', true);
}
node.set('loaded', true);
node.set('datastores', storeNameList);
Proxmox.Utils.setErrorMask(view, false);
node.expand();
} catch (error) {
Proxmox.Utils.setErrorMask(view, false);
Ext.Msg.alert('Error', error.toString());
}
},
beforeExpand: function(node, e) {
let me = this;
if (node.isLoaded()) {
return true;
}
me.loadMediaSet(node);
return false;
},
},
listeners: {
activate: 'reload',
},
store: {
data: [],
sorters: function(a, b) {
if (a.data.is_media_set && b.data.is_media_set) {
return a.data['media-set-ctime'] - b.data['media-set-ctime'];
} else {
return a.data.text.localeCompare(b.data.text);
}
},
},
rootVisible: false,
tbar: [
{
text: gettext('Reload'),
handler: 'reload',
},
'-',
{
text: gettext('New Backup'),
handler: 'backup',
},
],
columns: [
{
xtype: 'treecolumn',
text: gettext('Pool/Media Set/Snapshot'),
dataIndex: 'text',
sortable: false,
flex: 3,
},
{
header: gettext('Actions'),
xtype: 'actioncolumn',
items: [
{
handler: 'restoreBackups',
tooltip: gettext('Restore'),
getClass: (v, m, rec) => rec.data.restore ? 'fa fa-fw fa-undo' : 'pmx-hidden',
isDisabled: (v, r, c, i, rec) => !rec.data.restore,
},
],
},
{
text: gettext('Tapes'),
dataIndex: 'tapes',
sortable: false,
},
{
text: gettext('Seq. Nr.'),
dataIndex: 'seq-nr',
sortable: false,
},
{
text: gettext('Media Set UUID'),
dataIndex: 'media-set-uuid',
hidden: true,
sortable: false,
width: 280,
},
],
});