[pve-devel] [PATCH v2 manager 20/20] backup view: add prune window
Fabian Ebner
f.ebner at proxmox.com
Wed Sep 16 14:50:41 CEST 2020
adapted from PBS. Main differences are:
1. loading of the prune-backups options from the storage
configuration, when the window is opened
2. API has GET/DELETE distinction instead of 'dry-run'
3. API expects a single property string for the prune options
Signed-off-by: Fabian Ebner <f.ebner at proxmox.com>
---
Changes from v1:
* adapt to the new gropuing
* add button to group header
www/manager6/Makefile | 1 +
www/manager6/storage/BackupView.js | 34 ++++-
www/manager6/window/Prune.js | 230 +++++++++++++++++++++++++++++
3 files changed, 264 insertions(+), 1 deletion(-)
create mode 100644 www/manager6/window/Prune.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 4b9dcf58..b34bb41d 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -249,6 +249,7 @@ JSSRC= \
window/LoginWindow.js \
window/Migrate.js \
window/NotesEdit.js \
+ window/Prune.js \
window/Restore.js \
window/SafeDestroy.js \
window/Settings.js \
diff --git a/www/manager6/storage/BackupView.js b/www/manager6/storage/BackupView.js
index ad377de6..9b243974 100644
--- a/www/manager6/storage/BackupView.js
+++ b/www/manager6/storage/BackupView.js
@@ -6,7 +6,19 @@ Ext.define('PVE.storage.BackupView', {
features: [
{
ftype: 'grouping',
- groupHeaderTpl: '{name} ({rows.length} Backup{[values.rows.length > 1 ? "s" : ""]})',
+ groupHeaderTpl: new Ext.XTemplate(
+ '<tpl for=".">',
+ '{name} ({rows.length} Backup{[values.rows.length > 1 ? "s" : ""]})',
+ '<tpl if="name != \'other\'">',
+ ' | ' + gettext('Prune') + ': ',
+ '<button id="prune-btn" ' +
+ 'class="x-btn x-unselectable x-btn-custom-groupheader" ' +
+ 'data-qtip="Prune">',
+ '<i id="prune-btn-icon" class="fa fa-scissors"></i>',
+ '</button>',
+ '</tpl>',
+ '</tpl>',
+ ),
},
],
@@ -89,6 +101,26 @@ Ext.define('PVE.storage.BackupView', {
},
});
+ Ext.apply(me, {
+ listeners: {
+ groupclick: function (view, node, group, e, eOpts) {
+ if (e.getTarget().id.startsWith('prune-btn')) {
+ view.features[0].expand(group); // keep group to be pruned expanded
+
+ let [ type, vmid ] = group.split('/');
+ var win = Ext.create('PVE.window.Prune', {
+ nodename: nodename,
+ storage: storage,
+ backup_id: vmid,
+ backup_type: type,
+ });
+ win.show();
+ win.on('destroy', reload);
+ }
+ },
+ },
+ });
+
me.callParent();
},
});
diff --git a/www/manager6/window/Prune.js b/www/manager6/window/Prune.js
new file mode 100644
index 00000000..44ac8594
--- /dev/null
+++ b/www/manager6/window/Prune.js
@@ -0,0 +1,230 @@
+Ext.define('pve-prune-list', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'backup-type',
+ 'backup-id',
+ {
+ name: 'ctime',
+ type: 'date',
+ dateFormat: 'timestamp',
+ },
+ ],
+});
+
+Ext.define('PVE.PruneInputPanel', {
+ extend: 'Proxmox.panel.InputPanel',
+ alias: 'widget.pvePruneInputPanel',
+ mixins: ['Proxmox.Mixin.CBind'],
+
+ onGetValues: function(values) {
+ var me = this;
+
+ // the API expects a single prune-backups property string
+ let prune_opts = PVE.Parser.printPropertyString(values);
+ values = {};
+ values["prune-backups"] = prune_opts;
+ values.type = me.backup_type;
+ values.vmid = me.backup_id;
+
+ return values;
+ },
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ init: function(view) {
+ if (!view.url) {
+ throw "no url specified";
+ }
+ if (!view.backup_type) {
+ throw "no backup_type specified";
+ }
+ if (!view.backup_id) {
+ throw "no backup_id specified";
+ }
+
+ this.reload(); // initial load
+ },
+
+ reload: function() {
+ var view = this.getView();
+
+ let params = view.getValues();
+
+ Proxmox.Utils.API2Request({
+ url: view.url,
+ method: "GET",
+ params: params,
+ callback: function() {
+ return; // for easy breakpoint setting
+ },
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ success: function(response, options) {
+ var data = response.result.data;
+ view.pruneStore.setData(data);
+ },
+ });
+ },
+
+ control: {
+ field: { change: 'reload' },
+ },
+ },
+
+ column1: [
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'keep-last',
+ allowBlank: true,
+ fieldLabel: gettext('keep-last'),
+ value: 1,
+ minValue: 1,
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'keep-hourly',
+ allowBlank: true,
+ fieldLabel: gettext('keep-hourly'),
+ minValue: 1,
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'keep-daily',
+ allowBlank: true,
+ fieldLabel: gettext('keep-daily'),
+ minValue: 1,
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'keep-weekly',
+ allowBlank: true,
+ fieldLabel: gettext('keep-weekly'),
+ minValue: 1,
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'keep-monthly',
+ allowBlank: true,
+ fieldLabel: gettext('keep-monthly'),
+ minValue: 1,
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'keep-yearly',
+ allowBlank: true,
+ fieldLabel: gettext('keep-yearly'),
+ minValue: 1,
+ },
+ ],
+
+ initComponent: function() {
+ var me = this;
+
+ me.pruneStore = Ext.create('Ext.data.Store', {
+ model: 'pve-prune-list',
+ sorters: { property: 'ctime', direction: 'DESC' },
+ });
+
+ Proxmox.Utils.API2Request({
+ url: "/storage",
+ method: 'GET',
+ success: function(response, opts) {
+ let scfg = response.result.data.find(x => x.storage === me.storage);
+ if (!scfg || !scfg["prune-backups"]) {
+ return;
+ }
+ let prune_opts = PVE.Parser.parsePropertyString(scfg["prune-backups"]);
+ me.setValues(prune_opts);
+ },
+ });
+
+ me.column2 = [
+ {
+ xtype: 'grid',
+ height: 200,
+ store: me.pruneStore,
+ columns: [
+ {
+ header: gettext('Backup Time'),
+ sortable: true,
+ dataIndex: 'ctime',
+ renderer: function(value, metaData, record) {
+ let text = Ext.Date.format(value, 'Y-m-d H:i:s');
+ if (record.data.mark === 'remove') {
+ return '<div style="text-decoration: line-through;">'+ text +'</div>';
+ } else {
+ return text;
+ }
+ },
+ flex: 1,
+ },
+ {
+ text: "keep",
+ dataIndex: 'mark',
+ },
+ ],
+ },
+ ];
+
+ me.callParent();
+ },
+});
+
+Ext.define('PVE.window.Prune', {
+ extend: 'Proxmox.window.Edit',
+
+ method: 'DELETE',
+ submitText: gettext("Prune"),
+
+ isCreate: true,
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no nodename specified";
+ }
+ if (!me.storage) {
+ throw "no storage specified";
+ }
+ if (!me.backup_type) {
+ throw "no backup_type specified";
+ }
+ if (!me.backup_id) {
+ throw "no backup_id specified";
+ }
+
+ let backupGroupStr = me.backup_type + '/' + me.backup_id;
+
+ if (me.backup_type === 'CT') {
+ me.backup_type = 'lxc';
+ } else if (me.backup_type === 'VM') {
+ me.backup_type = 'qemu';
+ } else {
+ throw "unknown backup type";
+ }
+ let title = Ext.String.format(
+ gettext("Prune Backups for '{0}' on Storage '{1}'"),
+ backupGroupStr,
+ me.storage,
+ );
+
+ Ext.apply(me, {
+ url: '/api2/extjs/nodes/' + me.nodename + '/storage/' + me.storage + "/prunebackups",
+ title: title,
+ items: [
+ {
+ xtype: 'pvePruneInputPanel',
+ url: '/api2/extjs/nodes/' + me.nodename + '/storage/' + me.storage + "/prunebackups",
+ backup_type: me.backup_type,
+ backup_id: me.backup_id,
+ storage: me.storage,
+ },
+ ],
+ });
+
+ me.callParent();
+ },
+});
--
2.20.1
More information about the pve-devel
mailing list