[pve-devel] [PATCH manager 2/3] gui: refator SnapshotTree
Dominik Csapak
d.csapak at proxmox.com
Thu Jan 30 16:58:52 CET 2020
using the better View, ViewModel, Controller style,
while doing this, make it generic so that we can use it for qemu and lxc
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
www/manager6/Makefile | 3 +-
www/manager6/lxc/Config.js | 3 +-
www/manager6/lxc/SnapshotTree.js | 330 ---------------------------
www/manager6/qemu/Config.js | 3 +-
www/manager6/qemu/SnapshotTree.js | 320 --------------------------
www/manager6/tree/SnapshotTree.js | 361 ++++++++++++++++++++++++++++++
6 files changed, 366 insertions(+), 654 deletions(-)
delete mode 100644 www/manager6/lxc/SnapshotTree.js
delete mode 100644 www/manager6/qemu/SnapshotTree.js
create mode 100644 www/manager6/tree/SnapshotTree.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 3c99ec6c..eb7ac004 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -98,6 +98,7 @@ JSSRC= \
grid/FirewallAliases.js \
grid/FirewallOptions.js \
tree/ResourceTree.js \
+ tree/SnapshotTree.js \
panel/IPSet.js \
panel/ConfigPanel.js \
grid/BackupView.js \
@@ -147,7 +148,6 @@ JSSRC= \
qemu/ScsiHwEdit.js \
qemu/QemuBiosEdit.js \
qemu/Options.js \
- qemu/SnapshotTree.js \
qemu/Config.js \
qemu/CreateWizard.js \
qemu/USBEdit.js \
@@ -167,7 +167,6 @@ JSSRC= \
lxc/DNS.js \
lxc/Config.js \
lxc/CreateWizard.js \
- lxc/SnapshotTree.js \
lxc/ResourceEdit.js \
lxc/MPResize.js \
lxc/MPEdit.js \
diff --git a/www/manager6/lxc/Config.js b/www/manager6/lxc/Config.js
index e14b5ad2..29464706 100644
--- a/www/manager6/lxc/Config.js
+++ b/www/manager6/lxc/Config.js
@@ -266,7 +266,8 @@ Ext.define('PVE.lxc.Config', {
me.items.push({
title: gettext('Snapshots'),
iconCls: 'fa fa-history',
- xtype: 'pveLxcSnapshotTree',
+ xtype: 'pveGuestSnapshotTree',
+ type: 'lxc',
itemId: 'snapshot'
});
}
diff --git a/www/manager6/lxc/SnapshotTree.js b/www/manager6/lxc/SnapshotTree.js
deleted file mode 100644
index 0c1d9a6b..00000000
--- a/www/manager6/lxc/SnapshotTree.js
+++ /dev/null
@@ -1,330 +0,0 @@
-Ext.define('PVE.lxc.SnapshotTree', {
- extend: 'Ext.tree.Panel',
- alias: ['widget.pveLxcSnapshotTree'],
-
- onlineHelp: 'pct_snapshots',
-
- load_delay: 3000,
-
- old_digest: 'invalid',
-
- stateful: true,
- stateId: 'grid-lxc-snapshots',
-
- sorterFn: function(rec1, rec2) {
- var v1 = rec1.data.snaptime;
- var v2 = rec2.data.snaptime;
-
- if (rec1.data.name === 'current') {
- return 1;
- }
- if (rec2.data.name === 'current') {
- return -1;
- }
-
- return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
- },
-
- reload: function(repeat) {
- var me = this;
-
- Proxmox.Utils.API2Request({
- url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot',
- method: 'GET',
- failure: function(response, opts) {
- Proxmox.Utils.setErrorMask(me, response.htmlStatus);
- me.load_task.delay(me.load_delay);
- },
- success: function(response, opts) {
- Proxmox.Utils.setErrorMask(me, false);
- var digest = 'invalid';
- var idhash = {};
- var root = { name: '__root', expanded: true, children: [] };
- Ext.Array.each(response.result.data, function(item) {
- item.leaf = true;
- item.children = [];
- if (item.name === 'current') {
- digest = item.digest + item.running;
- if (item.running) {
- item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running';
- } else {
- item.iconCls = 'fa fa-fw fa-desktop x-fa-tree';
- }
- } else {
- item.iconCls = 'fa fa-fw fa-history x-fa-tree';
- }
- idhash[item.name] = item;
- });
-
- if (digest !== me.old_digest) {
- me.old_digest = digest;
-
- Ext.Array.each(response.result.data, function(item) {
- if (item.parent && idhash[item.parent]) {
- var parent_item = idhash[item.parent];
- parent_item.children.push(item);
- parent_item.leaf = false;
- parent_item.expanded = true;
- parent_item.expandable = false;
- } else {
- root.children.push(item);
- }
- });
-
- me.setRootNode(root);
- }
-
- me.load_task.delay(me.load_delay);
- }
- });
-
- Proxmox.Utils.API2Request({
- url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/feature',
- params: { feature: 'snapshot' },
- method: 'GET',
- success: function(response, options) {
- var res = response.result.data;
- if (res.hasFeature) {
- var snpBtns = Ext.ComponentQuery.query('#snapshotBtn');
- snpBtns.forEach(function(item){
- item.enable();
- });
- }
- }
- });
-
-
- },
-
- listeners: {
- beforestatesave: function(grid, state, eopts) {
- // extjs cannot serialize functions,
- // so a the sorter with only the sorterFn will
- // not be a valid sorter when restoring the state
- delete state.storeState.sorters;
- }
- },
-
- initComponent: function() {
- var me = this;
-
- me.nodename = me.pveSelNode.data.node;
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- me.vmid = me.pveSelNode.data.vmid;
- if (!me.vmid) {
- throw "no VM ID specified";
- }
-
- me.load_task = new Ext.util.DelayedTask(me.reload, me);
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var valid_snapshot = function(record) {
- return record && record.data && record.data.name &&
- record.data.name !== 'current';
- };
-
- var valid_snapshot_rollback = function(record) {
- return record && record.data && record.data.name &&
- record.data.name !== 'current' && !record.data.snapstate;
- };
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (valid_snapshot(rec)) {
- var win = Ext.create('PVE.window.LxcSnapshot', {
- type: 'lxc',
- snapname: rec.data.name,
- nodename: me.nodename,
- vmid: me.vmid
- });
- win.show();
- me.mon(win, 'close', me.reload, me);
- }
- };
-
- var editBtn = new Proxmox.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- enableFn: valid_snapshot,
- handler: run_editor
- });
-
- var rollbackBtn = new Proxmox.button.Button({
- text: gettext('Rollback'),
- disabled: true,
- dangerous: true,
- selModel: sm,
- enableFn: valid_snapshot_rollback,
- confirmMsg: function(rec) {
- var taskdescription = Proxmox.Utils.format_task_description('vzrollback', me.vmid);
- var snaptime = Ext.Date.format(rec.data.snaptime,'Y-m-d H:i:s');
- var snapname = rec.data.name;
-
- var msg = Ext.String.format(gettext('{0} to {1} ({2})'),
- taskdescription, snapname, snaptime);
- msg += '<p>' + gettext('Note: Rollback stops CT') + '</p>';
-
- return msg;
- },
- handler: function(btn, event) {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var snapname = rec.data.name;
-
- Proxmox.Utils.API2Request({
- url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot/' + snapname + '/rollback',
- method: 'POST',
- waitMsgTarget: me,
- callback: function() {
- me.reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, options) {
- var upid = response.result.data;
- var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid });
- win.show();
- }
- });
- }
- });
-
- var removeBtn = new Proxmox.button.Button({
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- confirmMsg: function(rec) {
- var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
- "'" + rec.data.name + "'");
- return msg;
- },
- enableFn: valid_snapshot,
- handler: function(btn, event) {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var snapname = rec.data.name;
-
- Proxmox.Utils.API2Request({
- url: '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/snapshot/' + snapname,
- method: 'DELETE',
- waitMsgTarget: me,
- callback: function() {
- me.reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, options) {
- var upid = response.result.data;
- var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid });
- win.show();
- }
- });
- }
- });
-
- var snapshotBtn = Ext.create('Ext.Button', {
- itemId: 'snapshotBtn',
- text: gettext('Take Snapshot'),
- disabled: true,
- handler: function() {
- var win = Ext.create('PVE.window.LxcSnapshot', {
- type: 'lxc',
- isCreate: true,
- submitText: gettext('Take Snapshot'),
- nodename: me.nodename,
- vmid: me.vmid
- });
- win.show();
- }
- });
-
- Ext.apply(me, {
- layout: 'fit',
- rootVisible: false,
- animate: false,
- sortableColumns: false,
- selModel: sm,
- tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ],
- fields: [
- 'name', 'description', 'snapstate', 'vmstate', 'running',
- { name: 'snaptime', type: 'date', dateFormat: 'timestamp' }
- ],
- columns: [
- {
- xtype: 'treecolumn',
- text: gettext('Name'),
- dataIndex: 'name',
- width: 200,
- renderer: function(value, metaData, record) {
- if (value === 'current') {
- return "NOW";
- } else {
- return value;
- }
- }
- },
-// {
-// text: gettext('RAM'),
-// align: 'center',
-// resizable: false,
-// dataIndex: 'vmstate',
-// width: 50,
-// renderer: function(value, metaData, record) {
-// if (record.data.name !== 'current') {
-// return Proxmox.Utils.format_boolean(value);
-// }
-// }
-// },
- {
- text: gettext('Date') + "/" + gettext("Status"),
- dataIndex: 'snaptime',
- resizable: false,
- width: 150,
- renderer: function(value, metaData, record) {
- if (record.data.snapstate) {
- return record.data.snapstate;
- }
- if (value) {
- return Ext.Date.format(value,'Y-m-d H:i:s');
- }
- }
- },
- {
- text: gettext('Description'),
- dataIndex: 'description',
- flex: 1,
- renderer: function(value, metaData, record) {
- if (record.data.name === 'current') {
- return gettext("You are here!");
- } else {
- return Ext.String.htmlEncode(value);
- }
- }
- }
- ],
- columnLines: true,
- listeners: {
- activate: me.reload,
- destroy: me.load_task.cancel,
- itemdblclick: run_editor
- }
- });
-
- me.callParent();
-
- me.store.sorters.add(new Ext.util.Sorter({
- sorterFn: me.sorterFn
- }));
- }
-});
diff --git a/www/manager6/qemu/Config.js b/www/manager6/qemu/Config.js
index 195db3a2..ea8b6137 100644
--- a/www/manager6/qemu/Config.js
+++ b/www/manager6/qemu/Config.js
@@ -298,7 +298,8 @@ Ext.define('PVE.qemu.Config', {
me.items.push({
title: gettext('Snapshots'),
iconCls: 'fa fa-history',
- xtype: 'pveQemuSnapshotTree',
+ type: 'qemu',
+ xtype: 'pveGuestSnapshotTree',
itemId: 'snapshot'
});
}
diff --git a/www/manager6/qemu/SnapshotTree.js b/www/manager6/qemu/SnapshotTree.js
deleted file mode 100644
index a3891433..00000000
--- a/www/manager6/qemu/SnapshotTree.js
+++ /dev/null
@@ -1,320 +0,0 @@
-Ext.define('PVE.qemu.SnapshotTree', {
- extend: 'Ext.tree.Panel',
- alias: ['widget.pveQemuSnapshotTree'],
-
- load_delay: 3000,
-
- old_digest: 'invalid',
-
- stateful: true,
- stateId: 'grid-qemu-snapshots',
-
- sorterFn: function(rec1, rec2) {
- var v1 = rec1.data.snaptime;
- var v2 = rec2.data.snaptime;
-
- if (rec1.data.name === 'current') {
- return 1;
- }
- if (rec2.data.name === 'current') {
- return -1;
- }
-
- return (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
- },
-
- reload: function(repeat) {
- var me = this;
-
- Proxmox.Utils.API2Request({
- url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot',
- method: 'GET',
- failure: function(response, opts) {
- Proxmox.Utils.setErrorMask(me, response.htmlStatus);
- me.load_task.delay(me.load_delay);
- },
- success: function(response, opts) {
- Proxmox.Utils.setErrorMask(me, false);
- var digest = 'invalid';
- var idhash = {};
- var root = { name: '__root', expanded: true, children: [] };
- Ext.Array.each(response.result.data, function(item) {
- item.leaf = true;
- item.children = [];
- if (item.name === 'current') {
- digest = item.digest + item.running;
- if (item.running) {
- item.iconCls = 'fa fa-fw fa-desktop x-fa-tree-running';
- } else {
- item.iconCls = 'fa fa-fw fa-desktop x-fa-tree';
- }
- } else {
- item.iconCls = 'fa fa-fw fa-history x-fa-tree';
- }
- idhash[item.name] = item;
- });
-
- if (digest !== me.old_digest) {
- me.old_digest = digest;
-
- Ext.Array.each(response.result.data, function(item) {
- if (item.parent && idhash[item.parent]) {
- var parent_item = idhash[item.parent];
- parent_item.children.push(item);
- parent_item.leaf = false;
- parent_item.expanded = true;
- parent_item.expandable = false;
- } else {
- root.children.push(item);
- }
- });
-
- me.setRootNode(root);
- }
-
- me.load_task.delay(me.load_delay);
- }
- });
-
- Proxmox.Utils.API2Request({
- url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/feature',
- params: { feature: 'snapshot' },
- method: 'GET',
- success: function(response, options) {
- var res = response.result.data;
- if (res.hasFeature) {
- var snpBtns = Ext.ComponentQuery.query('#snapshotBtn');
- snpBtns.forEach(function(item){
- item.enable();
- });
- }
- }
- });
-
-
- },
-
- listeners: {
- beforestatesave: function(grid, state, eopts) {
- // extjs cannot serialize functions,
- // so a the sorter with only the sorterFn will
- // not be a valid sorter when restoring the state
- delete state.storeState.sorters;
- }
- },
-
- initComponent: function() {
- var me = this;
-
- me.nodename = me.pveSelNode.data.node;
- if (!me.nodename) {
- throw "no node name specified";
- }
-
- me.vmid = me.pveSelNode.data.vmid;
- if (!me.vmid) {
- throw "no VM ID specified";
- }
-
- me.load_task = new Ext.util.DelayedTask(me.reload, me);
-
- var sm = Ext.create('Ext.selection.RowModel', {});
-
- var valid_snapshot = function(record) {
- return record && record.data && record.data.name &&
- record.data.name !== 'current';
- };
-
- var valid_snapshot_rollback = function(record) {
- return record && record.data && record.data.name &&
- record.data.name !== 'current' && !record.data.snapstate;
- };
-
- var run_editor = function() {
- var rec = sm.getSelection()[0];
- if (valid_snapshot(rec)) {
- var win = Ext.create('PVE.window.Snapshot', {
- type: 'qemu',
- snapname: rec.data.name,
- nodename: me.nodename,
- vmid: me.vmid
- });
- win.show();
- me.mon(win, 'close', me.reload, me);
- }
- };
-
- var editBtn = new Proxmox.button.Button({
- text: gettext('Edit'),
- disabled: true,
- selModel: sm,
- enableFn: valid_snapshot,
- handler: run_editor
- });
-
- var rollbackBtn = new Proxmox.button.Button({
- text: gettext('Rollback'),
- disabled: true,
- selModel: sm,
- enableFn: valid_snapshot_rollback,
- confirmMsg: function(rec) {
- return Proxmox.Utils.format_task_description('qmrollback', me.vmid) +
- " '" + rec.data.name + "'";
- },
- handler: function(btn, event) {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var snapname = rec.data.name;
-
- Proxmox.Utils.API2Request({
- url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname + '/rollback',
- method: 'POST',
- waitMsgTarget: me,
- callback: function() {
- me.reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, options) {
- var upid = response.result.data;
- var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid });
- win.show();
- }
- });
- }
- });
-
- var removeBtn = new Proxmox.button.Button({
- text: gettext('Remove'),
- disabled: true,
- selModel: sm,
- confirmMsg: function(rec) {
- var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
- "'" + rec.data.name + "'");
- return msg;
- },
- enableFn: valid_snapshot,
- handler: function(btn, event) {
- var rec = sm.getSelection()[0];
- if (!rec) {
- return;
- }
- var snapname = rec.data.name;
-
- Proxmox.Utils.API2Request({
- url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/snapshot/' + snapname,
- method: 'DELETE',
- waitMsgTarget: me,
- callback: function() {
- me.reload();
- },
- failure: function (response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, options) {
- var upid = response.result.data;
- var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid });
- win.show();
- }
- });
- }
- });
-
- var snapshotBtn = Ext.create('Ext.Button', {
- itemId: 'snapshotBtn',
- text: gettext('Take Snapshot'),
- disabled: true,
- handler: function() {
- var win = Ext.create('PVE.window.Snapshot', {
- isCreate: true,
- type: 'qemu',
- submitText: gettext('Take Snapshot'),
- nodename: me.nodename,
- vmid: me.vmid
- });
- win.show();
- }
- });
-
- Ext.apply(me, {
- layout: 'fit',
- rootVisible: false,
- animate: false,
- sortableColumns: false,
- selModel: sm,
- tbar: [ snapshotBtn, rollbackBtn, removeBtn, editBtn ],
- fields: [
- 'name', 'description', 'snapstate', 'vmstate', 'running',
- { name: 'snaptime', type: 'date', dateFormat: 'timestamp' }
- ],
- columns: [
- {
- xtype: 'treecolumn',
- text: gettext('Name'),
- dataIndex: 'name',
- width: 200,
- renderer: function(value, metaData, record) {
- if (value === 'current') {
- return "NOW";
- } else {
- return value;
- }
- }
- },
- {
- text: gettext('RAM'),
- align: 'center',
- resizable: false,
- dataIndex: 'vmstate',
- width: 50,
- renderer: function(value, metaData, record) {
- if (record.data.name !== 'current') {
- return Proxmox.Utils.format_boolean(value);
- }
- }
- },
- {
- text: gettext('Date') + "/" + gettext("Status"),
- dataIndex: 'snaptime',
- width: 150,
- renderer: function(value, metaData, record) {
- if (record.data.snapstate) {
- return record.data.snapstate;
- }
- if (value) {
- return Ext.Date.format(value,'Y-m-d H:i:s');
- }
- }
- },
- {
- text: gettext('Description'),
- dataIndex: 'description',
- flex: 1,
- renderer: function(value, metaData, record) {
- if (record.data.name === 'current') {
- return gettext("You are here!");
- } else {
- return Ext.String.htmlEncode(value);
- }
- }
- }
- ],
- columnLines: true, // will work in 4.1?
- listeners: {
- activate: me.reload,
- destroy: me.load_task.cancel,
- itemdblclick: run_editor
- }
- });
-
- me.callParent();
-
- me.store.sorters.add(new Ext.util.Sorter({
- sorterFn: me.sorterFn
- }));
- }
-});
-
diff --git a/www/manager6/tree/SnapshotTree.js b/www/manager6/tree/SnapshotTree.js
new file mode 100644
index 00000000..d4007efa
--- /dev/null
+++ b/www/manager6/tree/SnapshotTree.js
@@ -0,0 +1,361 @@
+Ext.define('PVE.guest.SnapshotTree', {
+ extend: 'Ext.tree.Panel',
+ xtype: 'pveGuestSnapshotTree',
+
+ stateful: true,
+ stateId: 'grid-snapshots',
+
+ viewModel: {
+ data: {
+ // should be 'qemu' or 'lxc'
+ type: undefined,
+ nodename: undefined,
+ vmid: undefined,
+ snapshotAllowed: false,
+ rollbackAllowed: false,
+ snapshotFeature: false,
+ selected: '',
+ load_delay: 3000,
+ },
+ formulas: {
+ canSnapshot: function(get) {
+ return get('snapshotAllowed') && get('snapshotFeature');
+ },
+ canRollback: function(get) {
+ return get('rollbackAllowed') &&
+ get('selected') && get('selected') !== 'current';
+ },
+ canRemove: function(get) {
+ return get('snapshotAllowed') &&
+ get('selected') && get('selected') !== 'current';
+ },
+ isSnapshot: function(get) {
+ return get('selected') && get('selected') !== 'current';
+ },
+ buttonText: function(get) {
+ return get('snapshotAllowed') ? gettext('Edit') : gettext('View');
+ },
+ showMemory: function(get) {
+ return get('type') === 'qemu';
+ },
+ },
+ },
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ newSnapshot: function() {
+ this.run_editor(false);
+ },
+
+ editSnapshot: function() {
+ this.run_editor(true);
+ },
+
+ run_editor: function(edit) {
+ let me = this;
+ let vm = me.getViewModel();
+ let snapname;
+ if (edit) {
+ snapname = vm.get('selected');
+ if (!snapname || snapname === 'current') { return; }
+ }
+ let win = Ext.create('PVE.window.Snapshot', {
+ nodename: vm.get('nodename'),
+ vmid: vm.get('vmid'),
+ viewonly: !vm.get('snapshotAllowed'),
+ type: vm.get('type'),
+ isCreate: !edit,
+ submitText: !edit ? gettext('Take Snapshot') : undefined,
+ snapname: snapname,
+ });
+ win.show();
+ me.mon(win, 'destroy', me.reload, me);
+ },
+
+ snapshotAction: function(action, method) {
+ let me = this;
+ let view = me.getView();
+ let vm = me.getViewModel();
+ let snapname = vm.get('selected');
+ if (!snapname) { return; }
+
+ let nodename = vm.get('nodename');
+ let type = vm.get('type');
+ let vmid = vm.get('vmid');
+
+ Proxmox.Utils.API2Request({
+ url: `/nodes/${nodename}/${type}/${vmid}/snapshot/${snapname}/${action}`,
+ method: method,
+ waitMsgTarget: view,
+ callback: function() {
+ me.reload();
+ },
+ failure: function (response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ success: function(response, options) {
+ var upid = response.result.data;
+ var win = Ext.create('Proxmox.window.TaskProgress', { upid: upid });
+ win.show();
+ }
+ });
+ },
+
+ rollback: function() { this.snapshotAction('rollback', 'POST'); },
+ remove: function() { this.snapshotAction('', 'DELETE'); },
+
+ cancel: function() {
+ this.load_task.cancel();
+ },
+
+ reload: function() {
+ let me = this;
+ let view = me.getView();
+ let vm = me.getViewModel();
+ let nodename = vm.get('nodename');
+ let vmid = vm.get('vmid');
+ let type = vm.get('type');
+ let load_delay = vm.get('load_delay');
+
+ Proxmox.Utils.API2Request({
+ url: `/nodes/${nodename}/${type}/${vmid}/snapshot`,
+ method: 'GET',
+ failure: function(response, opts) {
+ Proxmox.Utils.setErrorMask(view, response.htmlStatus);
+ me.load_task.delay(load_delay);
+ },
+ success: function(response, opts) {
+ Proxmox.Utils.setErrorMask(view, false);
+ var digest = 'invalid';
+ var idhash = {};
+ var root = { name: '__root', expanded: true, children: [] };
+ Ext.Array.each(response.result.data, function(item) {
+ item.leaf = true;
+ item.children = [];
+ if (item.name === 'current') {
+ digest = item.digest + item.running;
+ item.iconCls = PVE.Utils.get_object_icon_class(vm.get('type'), item);
+ } else {
+ item.iconCls = 'fa fa-fw fa-history x-fa-tree';
+ }
+ idhash[item.name] = item;
+ });
+
+ if (digest !== me.old_digest) {
+ me.old_digest = digest;
+
+ Ext.Array.each(response.result.data, function(item) {
+ if (item.parent && idhash[item.parent]) {
+ var parent_item = idhash[item.parent];
+ parent_item.children.push(item);
+ parent_item.leaf = false;
+ parent_item.expanded = true;
+ parent_item.expandable = false;
+ } else {
+ root.children.push(item);
+ }
+ });
+
+ me.getView().setRootNode(root);
+ }
+
+ me.load_task.delay(load_delay);
+ }
+ });
+
+ // if we do not have the permissions, we don't have to check
+ // if we can create a snapshot, since the butten stays disabled
+ if (!vm.get('snapshotAllowed')) {
+ return;
+ }
+
+ Proxmox.Utils.API2Request({
+ url: `/nodes/${nodename}/${type}/${vmid}/feature`,
+ params: { feature: 'snapshot' },
+ method: 'GET',
+ success: function(response, options) {
+ var res = response.result.data; vm.set('snapshotFeature', !!res.hasFeature); }
+ });
+ },
+
+ select: function(grid, val) {
+ let vm = this.getViewModel();
+ if (val.length < 1) {
+ vm.set('selected', '');
+ return;
+ }
+ vm.set('selected', val[0].data.name);
+ },
+
+ init: function(view) {
+ let me = this;
+ let vm = me.getViewModel();
+ me.load_task = new Ext.util.DelayedTask(me.reload, me);
+
+ if (!view.type) {
+ throw 'guest type not set';
+ }
+ vm.set('type', view.type);
+
+ if (!view.pveSelNode.data.node) {
+ throw "no node name specified";
+ }
+ vm.set('nodename', view.pveSelNode.data.node);
+
+ if (!view.pveSelNode.data.vmid) {
+ throw "no VM ID specified";
+ }
+ vm.set('vmid', view.pveSelNode.data.vmid);
+
+ let caps = Ext.state.Manager.get('GuiCap');
+ vm.set('snapshotAllowed', !!caps.vms['VM.Snapshot']);
+ vm.set('rollbackAllowed', !!caps.vms['VM.Snapshot.Rollback']);
+
+ view.getStore().sorters.add({
+ property: 'order',
+ direction: 'ASC',
+ });
+
+ me.reload();
+ },
+ },
+
+ listeners: {
+ selectionchange: 'select',
+ itemdblclick: 'editSnapshot',
+ destroy: 'cancel',
+ },
+
+ layout: 'fit',
+ rootVisible: false,
+ animate: false,
+ sortableColumns: false,
+
+ tbar: [
+ {
+ xtype: 'proxmoxButton',
+ text: gettext('Take Snapshot'),
+ disabled: true,
+ bind: {
+ disabled: "{!canSnapshot}",
+ },
+ handler: 'newSnapshot',
+ },
+ {
+ xtype: 'proxmoxButton',
+ text: gettext('Rollback'),
+ disabled: true,
+ bind: {
+ disabled: '{!canRollback}',
+ },
+ confirmMsg: function() {
+ let view = this.up('treepanel');
+ let rec = view.getSelection()[0];
+ let vmid = view.getViewModel().get('vmid');
+ return Proxmox.Utils.format_task_description('qmrollback', vmid) +
+ " '" + rec.data.name + "'";
+ },
+ handler: 'rollback',
+ },
+ {
+ xtype: 'proxmoxButton',
+ text: gettext('Remove'),
+ disabled: true,
+ bind: {
+ disabled: '{!canRemove}',
+ },
+ confirmMsg: function() {
+ let view = this.up('treepanel');
+ let rec = view.getSelection()[0];
+ return Ext.String.format(
+ gettext('Are you sure you want to remove entry {0}'),
+ `'${rec.data.name}'`
+ );
+ },
+ handler: 'remove',
+ },
+ {
+ xtype: 'proxmoxButton',
+ text: gettext('Edit'),
+ bind: {
+ text: '{buttonText}',
+ disabled: '{!isSnapshot}',
+ },
+ disabled: true,
+ edit: true,
+ handler: 'editSnapshot',
+ }
+ ],
+
+ columnLines: true,
+
+ fields: [
+ 'name', 'description', 'snapstate', 'vmstate', 'running',
+ { name: 'snaptime', type: 'date', dateFormat: 'timestamp' },
+ {
+ name: 'order',
+ calculate: function(data) {
+ return data.snaptime || (data.name === 'current' ? 'ZZZ' : data.snapstate);
+ }
+ }
+ ],
+
+ columns: [
+ {
+ xtype: 'treecolumn',
+ text: gettext('Name'),
+ dataIndex: 'name',
+ width: 200,
+ renderer: function(value, metaData, record) {
+ if (value === 'current') {
+ return gettext('NOW');
+ } else {
+ return value;
+ }
+ }
+ },
+ {
+ text: gettext('RAM'),
+ hidden: true,
+ bind: {
+ hidden: '{!showMemory}',
+ },
+ align: 'center',
+ resizable: false,
+ dataIndex: 'vmstate',
+ width: 50,
+ renderer: function(value, metaData, record) {
+ if (record.data.name !== 'current') {
+ return Proxmox.Utils.format_boolean(value);
+ }
+ }
+ },
+ {
+ text: gettext('Date') + "/" + gettext("Status"),
+ dataIndex: 'snaptime',
+ width: 150,
+ renderer: function(value, metaData, record) {
+ if (record.data.snapstate) {
+ return record.data.snapstate;
+ }
+ if (value) {
+ return Ext.Date.format(value,'Y-m-d H:i:s');
+ }
+ }
+ },
+ {
+ text: gettext('Description'),
+ dataIndex: 'description',
+ flex: 1,
+ renderer: function(value, metaData, record) {
+ if (record.data.name === 'current') {
+ return gettext("You are here!");
+ } else {
+ return Ext.String.htmlEncode(value);
+ }
+ }
+ }
+ ],
+
+});
--
2.20.1
More information about the pve-devel
mailing list