The new detail view for backups shows the settings of the backup similar to the edit dialog but read only. Additionally it does show a list of all included guests with their disks and if the disks are included in the backup.
Signed-off-by: Aaron Lauterer <a.laute...@proxmox.com> --- I am not too happy with the detail view so far. On the one hand as a User I would expect to see the details of the backup job if I click the Detail button, one the other hand it does not look too nice and takes away a lot of space. Maybe someone got a better idea on how to bring these two points together. Regarding tooltips @Thomas: I added a tooltip for the detail button but as far as I can tell this pattern isn't used anywhere else for such buttons. I am not sure if we want to introduce it at this point. I kept my own code to determine the Icon in the Tree for VM, LXC and disk. The only other place I found was the definition that is used in the ResourceTree. Refactoring that and adding a disk icon did seem wrong to me. v1 -> v2: * added backup job details above the list of guests and their disk status * code cleanup and alleviating nit picks from Thomas www/manager6/Utils.js | 30 ++++ www/manager6/dc/Backup.js | 307 +++++++++++++++++++++++++++++++++++++- 2 files changed, 336 insertions(+), 1 deletion(-) diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js index efa8108e..ccf28a9a 100644 --- a/www/manager6/Utils.js +++ b/www/manager6/Utils.js @@ -207,6 +207,30 @@ Ext.define('PVE.Utils', { utilities: { }, + render_disk_backup_status: function(value, meta, record) { + if (typeof value == 'undefined') { + return ""; + } + + let iconCls = 'check-circle good'; + let text = gettext('Yes'); + + if (!PVE.Parser.parseBoolean(value.toString())) { + iconCls = 'times-circle critical'; + + let reason = PVE.Utils.backup_reasons_table[record.get('reason')]; + + if (typeof reason == 'undefined') { + reason = record.get('reason'); + } + + text = gettext('No'); + text = `${text} - ${reason}`; + } + + return `<i class="fa fa-${iconCls}"></i> ${text}`; + }, + render_backup_days_of_week: function(val) { var dows = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']; var selected = []; @@ -268,6 +292,12 @@ Ext.define('PVE.Utils', { utilities: { return "-"; }, + backup_reasons_table: { + 'default exclude': gettext('Not included by default'), + 'not a volume': gettext('Not a volume'), + manual: gettext('Manually disabled'), + }, + get_kvm_osinfo: function(value) { var info = { base: 'Other' }; // default if (value) { diff --git a/www/manager6/dc/Backup.js b/www/manager6/dc/Backup.js index f7792002..f9da66d9 100644 --- a/www/manager6/dc/Backup.js +++ b/www/manager6/dc/Backup.js @@ -377,6 +377,264 @@ Ext.define('PVE.dc.BackupEdit', { }); +Ext.define('PVE.dc.BackupDiskTree', { + extend: 'Ext.tree.Panel', + alias: 'widget.pveBackupDiskTree', + + folderSort: true, + rootVisible: false, + + store: { + sorters: 'id', + data: {}, + }, + + tools: [ + { + type: 'expand', + tooltip: gettext('Expand All'), + scope: this, + callback: function(panel) { + panel.expandAll(); + }, + }, + { + type: 'collapse', + tooltip: gettext('Collapse All'), + scope: this, + callback: function(panel) { + panel.collapseAll(); + } + }, + ], + + columns: [ + { + xtype: 'treecolumn', + text: gettext('Guest Image'), + dataIndex: 'id', + flex: 6, + }, + { + text: gettext('Type'), + dataIndex: 'type', + flex: 1, + }, + { + text: gettext('Backup Job'), + renderer: PVE.Utils.render_disk_backup_status, + dataIndex: 'status', + flex: 3, + }, + ], + + reload: function() { + var me = this; + var sm = me.getSelectionModel(); + + Proxmox.Utils.API2Request({ + url: "/cluster/backup/" + me.jobid + "/included_disks", + waitMsgTarget: me, + method: 'GET', + failure: function(response, opts) { + Proxmox.Utils.setErrorMask(me, response.htmlStatus); + }, + success: function(response, opts) { + sm.deselectAll(); + me.setRootNode(response.result.data); + me.expandAll(); + }, + }); + }, + + initComponent: function() { + var me = this; + + if (!me.jobid) { + throw "no job id specified"; + } + + var sm = Ext.create('Ext.selection.TreeModel', {}); + + Ext.apply(me, { + selModel: sm, + fields: ['id', 'type', + { + type: 'string', + name: 'iconCls', + calculate: function(data) { + var txt = 'fa x-fa-tree fa-'; + if (data.leaf) { + return txt + 'hdd-o'; + } else if (data.type === 'VM') { + return txt + 'desktop'; + } else if (data.type === 'CT') { + return txt + 'cube'; + } + } + } + ], + }); + + me.callParent(); + + me.reload(); + } +}); + +Ext.define('PVE.dc.BackupInfo', { + extend: 'Proxmox.panel.InputPanel', + alias: 'widget.pveBackupInfo', + + padding: 10, + + column1: [ + { + name: 'node', + fieldLabel: gettext('Node'), + xtype: 'displayfield', + renderer: function (value) { + if (!value) { + return '-- ' + gettext('All') + ' --'; + } else { + return value; + } + }, + }, + { + name: 'storage', + fieldLabel: gettext('Storage'), + xtype: 'displayfield', + }, + { + name: 'dow', + fieldLabel: gettext('Day of week'), + xtype: 'displayfield', + renderer: PVE.Utils.render_backup_days_of_week, + }, + { + name: 'starttime', + fieldLabel: gettext('Start Time'), + xtype: 'displayfield', + }, + { + name: 'selMode', + fieldLabel: gettext('Selection mode'), + xtype: 'displayfield', + }, + { + name: 'pool', + fieldLabel: gettext('Pool to backup'), + xtype: 'displayfield', + } + ], + column2: [ + { + name: 'mailto', + fieldLabel: gettext('Send email to'), + xtype: 'displayfield', + }, + { + name: 'mailnotification', + fieldLabel: gettext('Email notification'), + xtype: 'displayfield', + renderer: function (value) { + let msg; + switch (value) { + case 'always': + msg = gettext('Always'); + break; + case 'failure': + msg = gettext('On failure only'); + break; + } + return msg; + }, + }, + { + name: 'compress', + fieldLabel: gettext('Compression'), + xtype: 'displayfield', + }, + { + name: 'mode', + fieldLabel: gettext('Mode'), + xtype: 'displayfield', + renderer: function (value) { + let msg; + switch (value) { + case 'snapshot': + msg = gettext('Snapshot'); + break; + case 'suspend': + msg = gettext('Suspend'); + break; + case 'stop': + msg = gettext('Stop'); + break; + } + return msg; + }, + }, + { + name: 'enabled', + fieldLabel: gettext('Enabled'), + xtype: 'displayfield', + renderer: function (value) { + if (PVE.Parser.parseBoolean(value.toString())) { + return gettext('Yes'); + } else { + return gettext('No'); + } + }, + }, + ], + + setValues: function(values) { + var me = this; + + Ext.iterate(values, function(fieldId, val) { + let field = me.query('[isFormField][name=' + fieldId + ']')[0]; + if (field) { + field.setValue(val); + } + }); + + // selection Mode depends on the presence/absence of several keys + let selModeField = me.query('[isFormField][name=selMode]')[0]; + let selMode = 'none'; + if (values.exclude) { + selMode = gettext('Exclude selected VMs'); + } + if (values.vmid) { + selMode = gettext('Include selected VMs'); + } + if (values.all) { + selMode = gettext('All'); + } + if (values.pool) { + selMode = gettext('Pool based'); + } + selModeField.setValue(selMode); + + if (!values.pool) { + let poolField = me.query('[isFormField][name=pool]')[0]; + poolField.setVisible(0); + } + }, + + initComponent: function() { + var me = this; + + if (!me.record) { + throw "no data provided"; + } + me.callParent(); + + me.setValues(me.record); + } +}); + Ext.define('PVE.dc.BackupView', { extend: 'Ext.grid.GridPanel', @@ -416,6 +674,43 @@ Ext.define('PVE.dc.BackupView', { win.show(); }; + var run_detail = function() { + let rec = sm.getSelection()[0] + if (!rec) { + return; + } + var me = this; + var infoview = Ext.create('PVE.dc.BackupInfo', { + flex: 0, + layout: 'fit', + record: rec.data, + }); + var disktree = Ext.create('PVE.dc.BackupDiskTree', { + title: gettext('Included disks'), + flex: 1, + jobid: rec.data.id, + }); + + var win = Ext.create('Ext.window.Window', { + modal: true, + width: 600, + height: 500, + resizable: true, + layout: 'fit', + title: gettext('Backup Details'), + + items:[{ + xtype: 'panel', + region: 'center', + layout: { + type: 'vbox', + align: 'stretch' + }, + items: [infoview, disktree], + }] + }).show(); + }; + var run_backup_now = function(job) { job = Ext.clone(job); @@ -516,6 +811,14 @@ Ext.define('PVE.dc.BackupView', { } }); + var detail_btn = new Proxmox.button.Button({ + text: gettext('Detail'), + disabled: true, + tooltip: gettext('Show job details and which guests and volumes are affected by the backup job'), + selModel: sm, + handler: run_detail, + }); + Proxmox.Utils.monStoreErrors(me, store); Ext.apply(me, { @@ -539,7 +842,9 @@ Ext.define('PVE.dc.BackupView', { remove_btn, edit_btn, '-', - run_btn + run_btn, + '-', + detail_btn, ], columns: [ { -- 2.20.1 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel