]> git.proxmox.com Git - pve-manager.git/commitdiff
fix #4474: ui: guest stop: offer to overrule active shutdown tasks
authorFriedrich Weber <f.weber@proxmox.com>
Fri, 12 Apr 2024 14:15:53 +0000 (16:15 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Sat, 20 Apr 2024 18:32:32 +0000 (20:32 +0200)
Implement a new "guest stop" confirmation message box which first
checks if there is an active shutdown task for the same guest that is
visible to the logged-in user. If there is at least one, the dialog
displays an additional default-on checkbox for overruling active
shutdown tasks. If the user confirms and the checkbox is checked, the
UI sends a guest stop API request with the `overrule-shutdown`
parameter set to 1. If there are no active shutdown tasks, or the
checkbox is unchecked, the UI sends a guest stop API request without
`overrule-shutdown`.

To avoid an additional API request for querying active shutdown tasks,
check the UI's current view of cluster tasks instead, which is fetched
from the `pve-cluster-tasks` store.

As the UI might hold an outdated task list, there are some
opportunities for races, e.g., the UI may miss a new shutdown task or
consider a shutdown task active even though it has already terminated.
These races either result in a surviving shutdown task that the user
still needs to abort manually, or a superfluous `override-shutdown=1`
parameter that does not actually abort any tasks. Since "stop
overrules shutdown" is merely a convenience feature, both outcomes
seem bearable.

The confirmation message box is now always marked as dangerous (with a
warning sign icon), whereas previously it was only marked dangerous if
the stop issued from the guest panel, but not when issued from the
resource tree command menu.

Signed-off-by: Friedrich Weber <f.weber@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
 [ TL: squash in some slightly opinionated code/style clean-ups ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
www/manager6/Makefile
www/manager6/lxc/CmdMenu.js
www/manager6/lxc/Config.js
www/manager6/qemu/CmdMenu.js
www/manager6/qemu/Config.js
www/manager6/window/GuestStop.js [new file with mode: 0644]

index 8658b154eaf5e78177ead00126e962e8edf1a2d4..66ad35b87a3e2bbaa6e890131f70b8c03c1921c7 100644 (file)
@@ -132,6 +132,7 @@ JSSRC=                                                      \
        window/ScheduleSimulator.js                     \
        window/Wizard.js                                \
        window/GuestDiskReassign.js                             \
+       window/GuestStop.js                             \
        window/TreeSettingsEdit.js                      \
        window/PCIMapEdit.js                            \
        window/USBMapEdit.js                            \
index b1403fc641fa89e9c28617e5df6159cc75322ccd..76b39423b21d85094986652a94da55cb0ed67da5 100644 (file)
@@ -66,7 +66,13 @@ Ext.define('PVE.lxc.CmdMenu', {
                iconCls: 'fa fa-fw fa-stop',
                disabled: stopped,
                tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'),
-               handler: () => confirmedVMCommand('stop'),
+               handler: () => {
+                   Ext.create('PVE.GuestStop', {
+                       nodename: info.node,
+                       vm: info,
+                       autoShow: true,
+                   });
+               },
            },
            {
                text: gettext('Reboot'),
index 8ef66f399c9882acc3cdfd7f558820ece540b9f1..d0e40fc466c16b76928424eb412fd23c81574c2d 100644 (file)
@@ -77,11 +77,13 @@ Ext.define('PVE.lxc.Config', {
                {
                    text: gettext('Stop'),
                    disabled: !caps.vms['VM.PowerMgmt'],
-                   confirmMsg: Proxmox.Utils.format_task_description('vzstop', vmid),
                    tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'CT'),
-                   dangerous: true,
                    handler: function() {
-                       vm_command("stop");
+                       Ext.create('PVE.GuestStop', {
+                           nodename: nodename,
+                           vm: vm,
+                           autoShow: true,
+                       });
                    },
                    iconCls: 'fa fa-stop',
                }],
index 4f59d5f7c4794894df9c956093e5992a477a953b..834577e7e72dfba2795332a91b764a6a406547c6 100644 (file)
@@ -93,7 +93,13 @@ Ext.define('PVE.qemu.CmdMenu', {
                iconCls: 'fa fa-fw fa-stop',
                disabled: stopped,
                tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'),
-               handler: () => confirmedVMCommand('stop'),
+               handler: () => {
+                   Ext.create('PVE.GuestStop', {
+                       nodename: info.node,
+                       vm: info,
+                       autoShow: true,
+                   });
+               },
            },
            {
                text: gettext('Reboot'),
index 2f9605a49e007ede157fc0bfcf43f215b0c9d0d2..f28ee67bb748b857dd9d021580c4106ccf46a98a 100644 (file)
@@ -176,11 +176,13 @@ Ext.define('PVE.qemu.Config', {
                }, {
                    text: gettext('Stop'),
                    disabled: !caps.vms['VM.PowerMgmt'],
-                   dangerous: true,
                    tooltip: Ext.String.format(gettext('Stop {0} immediately'), 'VM'),
-                   confirmMsg: Proxmox.Utils.format_task_description('qmstop', vmid),
                    handler: function() {
-                       vm_command("stop", { timeout: 30 });
+                       Ext.create('PVE.GuestStop', {
+                           nodename: nodename,
+                           vm: vm,
+                           autoShow: true,
+                       });
                    },
                    iconCls: 'fa fa-stop',
                }, {
diff --git a/www/manager6/window/GuestStop.js b/www/manager6/window/GuestStop.js
new file mode 100644 (file)
index 0000000..82aed1a
--- /dev/null
@@ -0,0 +1,75 @@
+Ext.define('PVE.GuestStop', {
+    extend: 'Ext.window.MessageBox',
+
+    closeAction: 'destroy',
+
+    initComponent: function() {
+       let me = this;
+
+       if (!me.nodename) {
+           throw "no node name specified";
+       }
+       if (!me.vm) {
+           throw "no vm specified";
+       }
+
+       let isQemuVM = me.vm.type === 'qemu';
+       let overruleTaskType = isQemuVM ? 'qmshutdown' : 'vzshutdown';
+
+       me.taskType = isQemuVM ? 'qmstop' : 'vzstop';
+       me.url = `/nodes/${me.nodename}/${me.vm.type}/${me.vm.vmid}/status/stop`;
+
+       let caps = Ext.state.Manager.get('GuiCap');
+       let hasSysModify = !!caps.nodes['Sys.Modify'];
+
+       // offer to overrule if there is at least one matching shutdown task and the guest is not
+       // HA-enabled. Also allow users to abort tasks started by one of their API tokens.
+       let shutdownTaskIdx = Ext.getStore('pve-cluster-tasks')?.findBy(task =>
+           (hasSysModify || task.data.user === Proxmox.UserName) &&
+           task.data.id === me.vm.vmid.toString() &&
+           task.data.status === undefined &&
+           task.data.type === overruleTaskType,
+       );
+       let haEnabled = me.vm.hastate && me.vm.hastate !== 'unmanaged';
+       me.askOverrule = !haEnabled && shutdownTaskIdx >= 0;
+
+       me.callParent();
+
+       // message box has its actual content in a sub-container, the top one is just for layouting
+       me.promptContainer.add({
+           xtype: 'proxmoxcheckbox',
+           name: 'overrule-shutdown',
+           checked: true,
+           boxLabel: gettext('Overrule active shutdown tasks'),
+           hidden: !me.askOverrule,
+       });
+    },
+
+    handler: function(btn) {
+       let me = this;
+       if (btn === 'yes') {
+           let checkbox = me.promptContainer.down('proxmoxcheckbox[name=overrule-shutdown]');
+           let overruleShutdown = me.askOverrule && checkbox.getSubmitValue();
+           let params = overruleShutdown ? { 'overrule-shutdown': 1 } : undefined;
+           Proxmox.Utils.API2Request({
+               url: me.url,
+               waitMsgTarget: me,
+               method: 'POST',
+               params: params,
+               failure: response => Ext.Msg.alert('Error', response.htmlStatus),
+           });
+       }
+    },
+
+    show: function() {
+       let me = this;
+       let cfg = {
+           title: gettext('Confirm'),
+           icon: Ext.Msg.WARNING,
+           msg: Proxmox.Utils.format_task_description(me.taskType, me.vm.vmid),
+           buttons: Ext.Msg.YESNO,
+           callback: btn => me.handler(btn),
+       };
+       me.callParent([cfg]);
+    },
+});