From: Timo Meschkat <timo.meschkat@datenzelt.de>
To: pve-devel@lists.proxmox.com
Subject: [RFC PATCH manager 1/4] fix #3760: ui: qemu: allow selecting the memory unit
Date: Mon, 26 Oct 2026 23:04:20 +0100 [thread overview]
Message-ID: <20261026220423.127776-5-timo.meschkat@datenzelt.de> (raw)
In-Reply-To: <20261026220423.127776-1-timo.meschkat@datenzelt.de>
Memory could only be entered in MiB so far, so larger values had to be
converted by hand. Allow entering it in MiB, GiB or TiB, or typing it
with a suffix like '8G'. MiB stays the default and is still what gets
sent to the API.
The minimum memory now follows both value and unit of the memory, as
long as the user did not change it.
The restore and guest import windows use the same field for their
memory override.
Signed-off-by: Timo Meschkat <timo.meschkat@datenzelt.de>
---
www/manager6/form/MemoryField.js | 59 ++++++++++++++----------------
www/manager6/qemu/MemoryEdit.js | 29 ++++++++++-----
www/manager6/window/GuestImport.js | 5 ++-
www/manager6/window/Restore.js | 4 ++
4 files changed, 55 insertions(+), 42 deletions(-)
diff --git a/www/manager6/form/MemoryField.js b/www/manager6/form/MemoryField.js
index 1ca533b7..75c1a23a 100644
--- a/www/manager6/form/MemoryField.js
+++ b/www/manager6/form/MemoryField.js
@@ -1,7 +1,15 @@
Ext.define('PVE.form.MemoryField', {
- extend: 'Ext.form.field.Number',
+ extend: 'Proxmox.form.SizeUnitField',
alias: 'widget.pveMemoryField',
+ backendUnit: 'MiB',
+
+ units: ['MiB', 'GiB', 'TiB'],
+
+ unitSteps: {
+ MiB: 32,
+ },
+
allowBlank: false,
hotplug: false,
@@ -10,21 +18,9 @@ Ext.define('PVE.form.MemoryField', {
maxValue: 4178944,
- step: 32,
-
value: '512', // qm backend default
- allowDecimals: false,
-
- allowExponential: false,
-
computeUpDown: function (value) {
- var me = this;
-
- if (!me.hotplug) {
- return { up: value + me.step, down: value - me.step };
- }
-
var dimm_size = 512;
var prev_dimm_size = 0;
var min_size = 1024;
@@ -50,20 +46,15 @@ Ext.define('PVE.form.MemoryField', {
return { up: value_up, down: value_down, start: value_start };
},
- onSpinUp: function () {
- var me = this;
- if (!me.readOnly) {
- let res = me.computeUpDown(me.getValue());
- me.setValue(Ext.Number.constrain(res.up, me.minValue, me.maxValue));
- }
- },
+ getSpinValue: function (value, up) {
+ let me = this;
- onSpinDown: function () {
- var me = this;
- if (!me.readOnly) {
- let res = me.computeUpDown(me.getValue());
- me.setValue(Ext.Number.constrain(res.down, me.minValue, me.maxValue));
+ if (!me.hotplug) {
+ return me.callParent([value, up]);
}
+
+ let res = me.computeUpDown(value);
+ return up ? res.up : res.down;
},
initComponent: function () {
@@ -71,17 +62,23 @@ Ext.define('PVE.form.MemoryField', {
if (me.hotplug) {
me.minValue = 1024;
+ }
- me.on('blur', function (field) {
- var value = me.getValue();
- var res = me.computeUpDown(value);
+ me.callParent();
+
+ if (me.hotplug) {
+ // memory hotplug only works in DIMM sized steps
+ me.inputField.on('blur', function () {
+ let value = me.getValue();
+ if (value === null) {
+ return;
+ }
+ let res = me.computeUpDown(value);
if (value === res.start || value === res.up || value === res.down) {
return;
}
- field.setValue(res.up);
+ me.setValue(res.up);
});
}
-
- me.callParent();
},
});
diff --git a/www/manager6/qemu/MemoryEdit.js b/www/manager6/qemu/MemoryEdit.js
index 0f6bd656..d8c54e08 100644
--- a/www/manager6/qemu/MemoryEdit.js
+++ b/www/manager6/qemu/MemoryEdit.js
@@ -79,22 +79,29 @@ Ext.define('PVE.qemu.MemoryInputPanel', {
{
xtype: 'pveMemoryField',
labelWidth: labelWidth,
- fieldLabel: gettext('Memory') + ' (MiB)',
+ fieldLabel: gettext('Memory'),
+ fieldDescription: gettext('Amount of memory (RAM) of the virtual machine'),
name: 'memory',
value: '512', // better defaults get set via the view controllers afterrender
minValue: 1,
- step: 32,
hotplug: me.hotplug,
listeners: {
change: function (f, value, old) {
- var bf = me.down('field[name=balloon]');
- var balloon = bf.getValue();
+ let bf = me.down('pveMemoryField[name=balloon]');
bf.setMaxValue(value);
- if (balloon === old) {
+ // follow memory until the user sets the minimum
+ if (bf.getValue() === old && !bf.hasUserInput()) {
bf.setValue(value);
+ bf.setUnit(f.getUnit());
}
bf.validate();
},
+ unitchange: function (f, unit) {
+ let bf = me.down('pveMemoryField[name=balloon]');
+ if (bf.getValue() === f.getValue() && !bf.hasUserInput()) {
+ bf.setUnit(unit);
+ }
+ },
},
},
];
@@ -106,14 +113,16 @@ Ext.define('PVE.qemu.MemoryInputPanel', {
minValue: 1,
maxValue: me.insideWizard ? 2048 : 512,
value: '512', // better defaults get set (indirectly) via the view controllers afterrender
- step: 32,
- fieldLabel: gettext('Minimum memory') + ' (MiB)',
+ fieldLabel: gettext('Minimum memory'),
+ fieldDescription: gettext(
+ 'Minimum amount of memory (RAM) the virtual machine keeps with ballooning',
+ ),
hotplug: me.hotplug,
labelWidth: labelWidth,
allowBlank: false,
listeners: {
change: function (f, value) {
- var memory = me.down('field[name=memory]').getValue();
+ var memory = me.down('pveMemoryField[name=memory]').getValue();
var shares = me.down('field[name=shares]');
shares.setDisabled(value === memory);
},
@@ -141,9 +150,9 @@ Ext.define('PVE.qemu.MemoryInputPanel', {
fieldLabel: gettext('Ballooning Device'),
listeners: {
change: function (f, value) {
- var bf = me.down('field[name=balloon]');
+ var bf = me.down('pveMemoryField[name=balloon]');
var shares = me.down('field[name=shares]');
- var memory = me.down('field[name=memory]');
+ var memory = me.down('pveMemoryField[name=memory]');
bf.setDisabled(!value);
shares.setDisabled(!value || bf.getValue() === memory.getValue());
},
diff --git a/www/manager6/window/GuestImport.js b/www/manager6/window/GuestImport.js
index 1013fe14..0b43ff76 100644
--- a/www/manager6/window/GuestImport.js
+++ b/www/manager6/window/GuestImport.js
@@ -515,7 +515,10 @@ Ext.define('PVE.window.GuestImport', {
},
{
xtype: 'pveMemoryField',
- fieldLabel: gettext('Memory') + ' (MiB)',
+ fieldLabel: gettext('Memory'),
+ fieldDescription: gettext(
+ 'Amount of memory (RAM) of the virtual machine',
+ ),
name: 'memory',
reference: 'memoryField',
value: 512,
diff --git a/www/manager6/window/Restore.js b/www/manager6/window/Restore.js
index e8d8de4e..f704fc40 100644
--- a/www/manager6/window/Restore.js
+++ b/www/manager6/window/Restore.js
@@ -359,6 +359,10 @@ Ext.define('PVE.window.Restore', {
{
xtype: 'pveMemoryField',
fieldLabel: gettext('Memory'),
+ fieldDescription:
+ me.vmtype === 'lxc'
+ ? gettext('Amount of memory (RAM) of the container')
+ : gettext('Amount of memory (RAM) of the virtual machine'),
name: 'memory',
reference: 'memoryField',
value: '',
--
2.47.3
next prev parent reply other threads:[~2026-10-26 22:04 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-26 22:04 [RFC PATCH widget-toolkit/manager 0/7] ui: allow selecting the unit for memory and disk sizes Timo Meschkat
2026-10-26 22:04 ` [RFC PATCH widget-toolkit 1/3] form: add size field with selectable unit Timo Meschkat
2026-10-26 22:04 ` [RFC PATCH widget-toolkit 2/3] form: size field: accept a unit suffix like 8G Timo Meschkat
2026-10-26 22:04 ` [RFC PATCH widget-toolkit 3/3] form: size field: add optional field description Timo Meschkat
2026-10-26 22:04 ` Timo Meschkat [this message]
2026-10-26 22:04 ` [RFC PATCH manager 2/4] ui: lxc: allow selecting the unit for memory and swap Timo Meschkat
2026-10-26 22:04 ` [RFC PATCH manager 3/4] ui: disk storage selector: allow selecting the size unit Timo Meschkat
2026-10-26 22:04 ` [RFC PATCH manager 4/4] ui: disk resize: " Timo Meschkat
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261026220423.127776-5-timo.meschkat@datenzelt.de \
--to=timo.meschkat@datenzelt.de \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox