From f1c3f66086ad63ad8facb4b7bfbf9f31d4ec8753 Mon Sep 17 00:00:00 2001 From: Michal Fox Date: Sat, 3 Oct 2026 03:28:43 +0200 Subject: [PATCH] fix #5871: ui: resize disk: show current and new size The resize dialogs of VMs and containers only ask for the size increment, so users have to look up the current size of the disk in the config to know which size the disk will have afterwards. Load the config of the guest in the dialogs and show the current size of the disk, and the size it will have with the entered increment, which gets updated while typing. As the dialogs load the size themselves, callers do not need to change. The size of the config is in the 'disk-size' format, where the units are binary, while Proxmox.Utils.parse_size_unit() takes 'G' as GB, so add a small parser for it, like parse_size() of PVE::JSONSchema. Signed-off-by: Michal Fox --- www/manager6/Utils.js | 10 +++++++++ www/manager6/lxc/MPResize.js | 41 ++++++++++++++++++++++++++++++++++ www/manager6/qemu/HDResize.js | 42 ++++++++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js index 8b99371..a9260ec 100644 --- a/www/manager6/Utils.js +++ b/www/manager6/Utils.js @@ -1148,6 +1148,16 @@ Ext.define('PVE.Utils', { return `${per.toFixed(1)}% of ${maxcpu} ${cpu_label}`; }, + // parses a size in the 'disk-size' format of the API, like '32G', into bytes + parse_disk_size: function (value) { + let match = /^(\d+(?:\.\d+)?)(?:([KMGT])(?:iB)?)?$/.exec(value); + if (!match) { + return undefined; + } + let exponent = ['', 'K', 'M', 'G', 'T'].indexOf(match[2] ?? ''); + return parseFloat(match[1]) * 1024 ** exponent; + }, + render_bandwidth: function (value) { if (!Ext.isNumeric(value)) { return ''; diff --git a/www/manager6/lxc/MPResize.js b/www/manager6/lxc/MPResize.js index 658123a..ec56adb 100644 --- a/www/manager6/lxc/MPResize.js +++ b/www/manager6/lxc/MPResize.js @@ -24,6 +24,17 @@ Ext.define('PVE.window.MPResize', { }); }, + updateNewSize: function () { + let me = this; + let newSize = ''; + if (me.currentSize !== undefined && me.hdsizesel.isValid()) { + newSize = Proxmox.Utils.format_size( + me.currentSize + me.hdsizesel.getValue() * 1024 ** 3, + ); + } + me.down('#newSize').setValue(newSize); + }, + initComponent: function () { var me = this; @@ -44,6 +55,11 @@ Ext.define('PVE.window.MPResize', { vtype: 'StorageId', allowBlank: false, }, + { + xtype: 'displayfield', + itemId: 'currentSize', + fieldLabel: gettext('Current Size'), + }, ]; me.hdsizesel = Ext.createWidget('numberfield', { @@ -54,9 +70,17 @@ Ext.define('PVE.window.MPResize', { value: '0', fieldLabel: `${gettext('Size Increment')} (${gettext('GiB')})`, allowBlank: false, + listeners: { + change: () => me.updateNewSize(), + }, }); items.push(me.hdsizesel); + items.push({ + xtype: 'displayfield', + itemId: 'newSize', + fieldLabel: gettext('New Size'), + }); me.formPanel = Ext.create('Ext.form.Panel', { bodyPadding: 10, @@ -92,5 +116,22 @@ Ext.define('PVE.window.MPResize', { }); me.callParent(); + + Proxmox.Utils.API2Request({ + url: `/nodes/${me.nodename}/lxc/${me.vmid}/config`, + method: 'GET', + waitMsgTarget: me, + failure: (response) => Ext.Msg.alert(gettext('Error'), response.htmlStatus), + success: function (response) { + let size = PVE.Parser.parseLxcMountPoint(response.result.data[me.disk])?.size; + me.currentSize = size ? PVE.Utils.parse_disk_size(size) : undefined; + me.down('#currentSize').setValue( + me.currentSize !== undefined + ? Proxmox.Utils.format_size(me.currentSize) + : Proxmox.Utils.unknownText, + ); + me.updateNewSize(); + }, + }); }, }); diff --git a/www/manager6/qemu/HDResize.js b/www/manager6/qemu/HDResize.js index 574bfb5..4c2ac58 100644 --- a/www/manager6/qemu/HDResize.js +++ b/www/manager6/qemu/HDResize.js @@ -25,6 +25,17 @@ Ext.define('PVE.window.HDResize', { }); }, + updateNewSize: function () { + let me = this; + let newSize = ''; + if (me.currentSize !== undefined && me.hdsizesel.isValid()) { + newSize = Proxmox.Utils.format_size( + me.currentSize + me.hdsizesel.getValue() * 1024 ** 3, + ); + } + me.down('#newSize').setValue(newSize); + }, + initComponent: function () { var me = this; @@ -45,6 +56,11 @@ Ext.define('PVE.window.HDResize', { vtype: 'StorageId', allowBlank: false, }, + { + xtype: 'displayfield', + itemId: 'currentSize', + fieldLabel: gettext('Current Size'), + }, ]; me.hdsizesel = Ext.createWidget('numberfield', { @@ -55,9 +71,17 @@ Ext.define('PVE.window.HDResize', { value: '0', fieldLabel: `${gettext('Size Increment')} (${gettext('GiB')})`, allowBlank: false, + listeners: { + change: () => me.updateNewSize(), + }, }); items.push(me.hdsizesel); + items.push({ + xtype: 'displayfield', + itemId: 'newSize', + fieldLabel: gettext('New Size'), + }); me.formPanel = Ext.create('Ext.form.Panel', { bodyPadding: 10, @@ -87,7 +111,6 @@ Ext.define('PVE.window.HDResize', { Ext.apply(me, { modal: true, width: 250, - height: 150, border: false, layout: 'fit', buttons: [submitBtn], @@ -95,5 +118,22 @@ Ext.define('PVE.window.HDResize', { }); me.callParent(); + + Proxmox.Utils.API2Request({ + url: `/nodes/${me.nodename}/qemu/${me.vmid}/config`, + method: 'GET', + waitMsgTarget: me, + failure: (response) => Ext.Msg.alert(gettext('Error'), response.htmlStatus), + success: function (response) { + let size = PVE.Parser.parseQemuDrive(me.disk, response.result.data[me.disk])?.size; + me.currentSize = size ? PVE.Utils.parse_disk_size(size) : undefined; + me.down('#currentSize').setValue( + me.currentSize !== undefined + ? Proxmox.Utils.format_size(me.currentSize) + : Proxmox.Utils.unknownText, + ); + me.updateNewSize(); + }, + }); }, });