* [pve-devel] [PATCH manager] ui: disk edit: Split out bandwidth limits
@ 2021-07-08 8:40 Dominic Jäger
0 siblings, 0 replies; only message in thread
From: Dominic Jäger @ 2021-07-08 8:40 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Dominic Jäger <d.jaeger@proxmox.com>
Here two inputpanels
1. diskData with volid, storage...
2. diskBasicOptions with the checkboxes
are on the first tab. This required the least changes to get a working version
so that we can quickly get on the same page about what we want it to look like.
I can also put it in a single inputpanel. This might make it slightly more
readable.
Other notable changes
- Return objects in onGetValues and put them into a string later
- Guarantee up-to-date confid as variable in the panels instead of using getters
- Make fields static as far as possible
---
www/manager6/Makefile | 5 +-
www/manager6/form/ControllerSelector.js | 7 +
www/manager6/qemu/CreateWizard.js | 8 +-
www/manager6/qemu/HDEdit.js | 409 ------------------
www/manager6/qemu/HardwareView.js | 9 +-
www/manager6/qemu/disk/Disk.js | 193 +++++++++
.../qemu/disk/DiskBandwidthOptions.js | 132 ++++++
www/manager6/qemu/disk/DiskBasicOptions.js | 102 +++++
www/manager6/qemu/disk/DiskData.js | 174 ++++++++
9 files changed, 624 insertions(+), 415 deletions(-)
delete mode 100644 www/manager6/qemu/HDEdit.js
create mode 100644 www/manager6/qemu/disk/Disk.js
create mode 100644 www/manager6/qemu/disk/DiskBandwidthOptions.js
create mode 100644 www/manager6/qemu/disk/DiskBasicOptions.js
create mode 100644 www/manager6/qemu/disk/DiskData.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 75d355a5..2bda765a 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -201,7 +201,10 @@ JSSRC= \
qemu/Config.js \
qemu/CreateWizard.js \
qemu/DisplayEdit.js \
- qemu/HDEdit.js \
+ qemu/disk/Disk.js \
+ qemu/disk/DiskData.js \
+ qemu/disk/DiskBasicOptions.js \
+ qemu/disk/DiskBandwidthOptions.js \
qemu/HDEfi.js \
qemu/HDMove.js \
qemu/HDResize.js \
diff --git a/www/manager6/form/ControllerSelector.js b/www/manager6/form/ControllerSelector.js
index daca2432..4b2be35d 100644
--- a/www/manager6/form/ControllerSelector.js
+++ b/www/manager6/form/ControllerSelector.js
@@ -72,6 +72,13 @@ Ext.define('PVE.form.ControllerSelector', {
deviceid.validate();
},
+ // confid = controller + deviceid as string (e.g. virtio1)
+ getConfid: function() {
+ const names = ['controller', 'deviceid']; // order must be guaranteed
+ const values = names.map(n => this.down(`field[name=${n}]`).getValue());
+ return values.join('');
+ },
+
initComponent: function() {
var me = this;
diff --git a/www/manager6/qemu/CreateWizard.js b/www/manager6/qemu/CreateWizard.js
index d4535c9d..4b23fea8 100644
--- a/www/manager6/qemu/CreateWizard.js
+++ b/www/manager6/qemu/CreateWizard.js
@@ -154,11 +154,12 @@ Ext.define('PVE.qemu.CreateWizard', {
insideWizard: true,
},
{
- xtype: 'pveQemuHDInputPanel',
+ xtype: 'pveQemuDisk',
bind: {
nodename: '{nodename}',
},
title: gettext('Hard Disk'),
+ plain: true,
isCreate: true,
insideWizard: true,
},
@@ -251,6 +252,11 @@ Ext.define('PVE.qemu.CreateWizard', {
},
},
],
+
+ getValues: function() {
+ const values = this.callParent();
+ return PVE.qemu.Disk.mergeDiskValues(values);
+ },
});
diff --git a/www/manager6/qemu/HDEdit.js b/www/manager6/qemu/HDEdit.js
deleted file mode 100644
index 95a98b0b..00000000
--- a/www/manager6/qemu/HDEdit.js
+++ /dev/null
@@ -1,409 +0,0 @@
-/* 'change' property is assigned a string and then a function */
-Ext.define('PVE.qemu.HDInputPanel', {
- extend: 'Proxmox.panel.InputPanel',
- alias: 'widget.pveQemuHDInputPanel',
- onlineHelp: 'qm_hard_disk',
-
- insideWizard: false,
-
- unused: false, // ADD usused disk imaged
-
- vmconfig: {}, // used to select usused disks
-
- viewModel: {},
-
- controller: {
-
- xclass: 'Ext.app.ViewController',
-
- onControllerChange: function(field) {
- var value = field.getValue();
-
- var allowIOthread = value.match(/^(virtio|scsi)/);
- this.lookup('iothread').setDisabled(!allowIOthread);
- if (!allowIOthread) {
- this.lookup('iothread').setValue(false);
- }
-
- var virtio = value.match(/^virtio/);
- this.lookup('ssd').setDisabled(virtio);
- if (virtio) {
- this.lookup('ssd').setValue(false);
- }
-
- this.lookup('scsiController').setVisible(value.match(/^scsi/));
- },
-
- control: {
- 'field[name=controller]': {
- change: 'onControllerChange',
- afterrender: 'onControllerChange',
- },
- 'field[name=iothread]': {
- change: function(f, value) {
- if (!this.getView().insideWizard) {
- return;
- }
- var vmScsiType = value ? 'virtio-scsi-single': 'virtio-scsi-pci';
- this.lookupReference('scsiController').setValue(vmScsiType);
- },
- },
- },
-
- init: function(view) {
- var vm = this.getViewModel();
- if (view.isCreate) {
- vm.set('isIncludedInBackup', true);
- }
- },
- },
-
- onGetValues: function(values) {
- var me = this;
-
- var params = {};
- var confid = me.confid || values.controller + values.deviceid;
-
- if (me.unused) {
- me.drive.file = me.vmconfig[values.unusedId];
- confid = values.controller + values.deviceid;
- } else if (me.isCreate) {
- if (values.hdimage) {
- me.drive.file = values.hdimage;
- } else {
- me.drive.file = values.hdstorage + ":" + values.disksize;
- }
- me.drive.format = values.diskformat;
- }
-
- PVE.Utils.propertyStringSet(me.drive, !values.backup, 'backup', '0');
- PVE.Utils.propertyStringSet(me.drive, values.noreplicate, 'replicate', 'no');
- PVE.Utils.propertyStringSet(me.drive, values.discard, 'discard', 'on');
- PVE.Utils.propertyStringSet(me.drive, values.ssd, 'ssd', 'on');
- PVE.Utils.propertyStringSet(me.drive, values.iothread, 'iothread', 'on');
- PVE.Utils.propertyStringSet(me.drive, values.cache, 'cache');
-
- var names = ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr'];
- Ext.Array.each(names, function(name) {
- var burst_name = name + '_max';
- PVE.Utils.propertyStringSet(me.drive, values[name], name);
- PVE.Utils.propertyStringSet(me.drive, values[burst_name], burst_name);
- });
-
-
- params[confid] = PVE.Parser.printQemuDrive(me.drive);
-
- return params;
- },
-
- setVMConfig: function(vmconfig) {
- var me = this;
-
- me.vmconfig = vmconfig;
-
- if (me.bussel) {
- me.bussel.setVMConfig(vmconfig);
- me.scsiController.setValue(vmconfig.scsihw);
- }
- if (me.unusedDisks) {
- var disklist = [];
- Ext.Object.each(vmconfig, function(key, value) {
- if (key.match(/^unused\d+$/)) {
- disklist.push([key, value]);
- }
- });
- me.unusedDisks.store.loadData(disklist);
- me.unusedDisks.setValue(me.confid);
- }
- },
-
- setDrive: function(drive) {
- var me = this;
-
- me.drive = drive;
-
- var values = {};
- var match = drive.file.match(/^([^:]+):/);
- if (match) {
- values.hdstorage = match[1];
- }
-
- values.hdimage = drive.file;
- values.backup = PVE.Parser.parseBoolean(drive.backup, 1);
- values.noreplicate = !PVE.Parser.parseBoolean(drive.replicate, 1);
- values.diskformat = drive.format || 'raw';
- values.cache = drive.cache || '__default__';
- values.discard = drive.discard === 'on';
- values.ssd = PVE.Parser.parseBoolean(drive.ssd);
- values.iothread = PVE.Parser.parseBoolean(drive.iothread);
-
- values.mbps_rd = drive.mbps_rd;
- values.mbps_wr = drive.mbps_wr;
- values.iops_rd = drive.iops_rd;
- values.iops_wr = drive.iops_wr;
- values.mbps_rd_max = drive.mbps_rd_max;
- values.mbps_wr_max = drive.mbps_wr_max;
- values.iops_rd_max = drive.iops_rd_max;
- values.iops_wr_max = drive.iops_wr_max;
-
- me.setValues(values);
- },
-
- setNodename: function(nodename) {
- var me = this;
- me.down('#hdstorage').setNodename(nodename);
- me.down('#hdimage').setStorage(undefined, nodename);
- },
-
- initComponent: function() {
- var me = this;
-
- var labelWidth = 140;
-
- me.drive = {};
-
- me.column1 = [];
- me.column2 = [];
-
- me.advancedColumn1 = [];
- me.advancedColumn2 = [];
-
- if (!me.confid || me.unused) {
- me.bussel = Ext.create('PVE.form.ControllerSelector', {
- vmconfig: me.insideWizard ? { ide2: 'cdrom' } : {},
- });
- me.column1.push(me.bussel);
-
- me.scsiController = Ext.create('Ext.form.field.Display', {
- fieldLabel: gettext('SCSI Controller'),
- reference: 'scsiController',
- bind: me.insideWizard ? {
- value: '{current.scsihw}',
- } : undefined,
- renderer: PVE.Utils.render_scsihw,
- submitValue: false,
- hidden: true,
- });
- me.column1.push(me.scsiController);
- }
-
- if (me.unused) {
- me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', {
- name: 'unusedId',
- fieldLabel: gettext('Disk image'),
- matchFieldWidth: false,
- listConfig: {
- width: 350,
- },
- data: [],
- allowBlank: false,
- });
- me.column1.push(me.unusedDisks);
- } else if (me.isCreate) {
- me.column1.push({
- xtype: 'pveDiskStorageSelector',
- storageContent: 'images',
- name: 'disk',
- nodename: me.nodename,
- autoSelect: me.insideWizard,
- });
- } else {
- me.column1.push({
- xtype: 'textfield',
- disabled: true,
- submitValue: false,
- fieldLabel: gettext('Disk image'),
- name: 'hdimage',
- });
- }
-
- me.column2.push(
- {
- xtype: 'CacheTypeSelector',
- name: 'cache',
- value: '__default__',
- fieldLabel: gettext('Cache'),
- },
- {
- xtype: 'proxmoxcheckbox',
- fieldLabel: gettext('Discard'),
- reference: 'discard',
- name: 'discard',
- },
- );
-
- me.advancedColumn1.push(
- {
- xtype: 'proxmoxcheckbox',
- disabled: me.confid && me.confid.match(/^virtio/),
- fieldLabel: gettext('SSD emulation'),
- labelWidth: labelWidth,
- name: 'ssd',
- reference: 'ssd',
- },
- {
- xtype: 'proxmoxcheckbox',
- disabled: me.confid && !me.confid.match(/^(virtio|scsi)/),
- fieldLabel: 'IO thread',
- labelWidth: labelWidth,
- reference: 'iothread',
- name: 'iothread',
- },
- {
- xtype: 'numberfield',
- name: 'mbps_rd',
- minValue: 1,
- step: 1,
- fieldLabel: gettext('Read limit') + ' (MB/s)',
- labelWidth: labelWidth,
- emptyText: gettext('unlimited'),
- },
- {
- xtype: 'numberfield',
- name: 'mbps_wr',
- minValue: 1,
- step: 1,
- fieldLabel: gettext('Write limit') + ' (MB/s)',
- labelWidth: labelWidth,
- emptyText: gettext('unlimited'),
- },
- {
- xtype: 'proxmoxintegerfield',
- name: 'iops_rd',
- minValue: 10,
- step: 10,
- fieldLabel: gettext('Read limit') + ' (ops/s)',
- labelWidth: labelWidth,
- emptyText: gettext('unlimited'),
- },
- {
- xtype: 'proxmoxintegerfield',
- name: 'iops_wr',
- minValue: 10,
- step: 10,
- fieldLabel: gettext('Write limit') + ' (ops/s)',
- labelWidth: labelWidth,
- emptyText: gettext('unlimited'),
- },
- );
-
- me.advancedColumn2.push(
- {
- xtype: 'proxmoxcheckbox',
- fieldLabel: gettext('Backup'),
- autoEl: {
- tag: 'div',
- 'data-qtip': gettext('Include volume in backup job'),
- },
- labelWidth: labelWidth,
- name: 'backup',
- bind: {
- value: '{isIncludedInBackup}',
- },
- },
- {
- xtype: 'proxmoxcheckbox',
- fieldLabel: gettext('Skip replication'),
- labelWidth: labelWidth,
- name: 'noreplicate',
- },
- {
- xtype: 'numberfield',
- name: 'mbps_rd_max',
- minValue: 1,
- step: 1,
- fieldLabel: gettext('Read max burst') + ' (MB)',
- labelWidth: labelWidth,
- emptyText: gettext('default'),
- },
- {
- xtype: 'numberfield',
- name: 'mbps_wr_max',
- minValue: 1,
- step: 1,
- fieldLabel: gettext('Write max burst') + ' (MB)',
- labelWidth: labelWidth,
- emptyText: gettext('default'),
- },
- {
- xtype: 'proxmoxintegerfield',
- name: 'iops_rd_max',
- minValue: 10,
- step: 10,
- fieldLabel: gettext('Read max burst') + ' (ops)',
- labelWidth: labelWidth,
- emptyText: gettext('default'),
- },
- {
- xtype: 'proxmoxintegerfield',
- name: 'iops_wr_max',
- minValue: 10,
- step: 10,
- fieldLabel: gettext('Write max burst') + ' (ops)',
- labelWidth: labelWidth,
- emptyText: gettext('default'),
- },
- );
-
- me.callParent();
- },
-});
-
-Ext.define('PVE.qemu.HDEdit', {
- extend: 'Proxmox.window.Edit',
-
- isAdd: true,
-
- backgroundDelay: 5,
-
- initComponent: function() {
- var me = this;
-
- var nodename = me.pveSelNode.data.node;
- if (!nodename) {
- throw "no node name specified";
- }
-
- var unused = me.confid && me.confid.match(/^unused\d+$/);
-
- me.isCreate = me.confid ? unused : true;
-
- var ipanel = Ext.create('PVE.qemu.HDInputPanel', {
- confid: me.confid,
- nodename: nodename,
- unused: unused,
- isCreate: me.isCreate,
- });
-
- if (unused) {
- me.subject = gettext('Unused Disk');
- } else if (me.isCreate) {
- me.subject = gettext('Hard Disk');
- } else {
- me.subject = gettext('Hard Disk') + ' (' + me.confid + ')';
- }
-
- me.items = [ipanel];
-
- me.callParent();
- /* 'data' is assigned an empty array in same file, and here we
- * use it like an object
- */
- me.load({
- success: function(response, options) {
- ipanel.setVMConfig(response.result.data);
- if (me.confid) {
- var value = response.result.data[me.confid];
- var drive = PVE.Parser.parseQemuDrive(me.confid, value);
- if (!drive) {
- Ext.Msg.alert(gettext('Error'), 'Unable to parse drive options');
- me.close();
- return;
- }
- ipanel.setDrive(drive);
- me.isValid(); // trigger validation
- }
- },
- });
- },
-});
diff --git a/www/manager6/qemu/HardwareView.js b/www/manager6/qemu/HardwareView.js
index bfe0a222..fde0938b 100644
--- a/www/manager6/qemu/HardwareView.js
+++ b/www/manager6/qemu/HardwareView.js
@@ -220,7 +220,7 @@ Ext.define('PVE.qemu.HardwareView', {
rows[confid] = {
group: 10,
iconCls: 'hdd-o',
- editor: 'PVE.qemu.HDEdit',
+ editor: 'PVE.qemu.DiskWindow',
isOnStorageBus: true,
header: gettext('Hard Disk') + ' (' + confid +')',
cdheader: gettext('CD/DVD Drive') + ' (' + confid +')',
@@ -290,7 +290,7 @@ Ext.define('PVE.qemu.HardwareView', {
order: i,
iconCls: 'hdd-o',
del_extra_msg: gettext('This will permanently erase all data.'),
- editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined,
+ editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.DiskWindow' : undefined,
header: gettext('Unused Disk') + ' ' + i.toString(),
};
}
@@ -629,9 +629,10 @@ Ext.define('PVE.qemu.HardwareView', {
iconCls: 'fa fa-fw fa-hdd-o black',
disabled: !caps.vms['VM.Config.Disk'],
handler: function() {
- let win = Ext.create('PVE.qemu.HDEdit', {
+ let win = Ext.create('PVE.qemu.DiskWindow', {
url: '/api2/extjs/' + baseurl,
- pveSelNode: me.pveSelNode,
+ nodename: me.pveSelNode.data.node,
+ isCreate: true,
});
win.on('destroy', me.reload, me);
win.show();
diff --git a/www/manager6/qemu/disk/Disk.js b/www/manager6/qemu/disk/Disk.js
new file mode 100644
index 00000000..591ba5c3
--- /dev/null
+++ b/www/manager6/qemu/disk/Disk.js
@@ -0,0 +1,193 @@
+/* 'change' property is assigned a string and then a function */
+Ext.define('PVE.qemu.Disk', {
+ extend: 'Ext.tab.Panel',
+ alias: 'widget.pveQemuDisk',
+ onlineHelp: 'qm_hard_disk',
+
+ insideWizard: false,
+
+ isCreate: false,
+
+ bodyPadding: 10,
+
+ setDrive: function(drive) {
+ [
+ 'pveQemuDiskData',
+ 'pveQemuDiskBasicOptions',
+ 'pveQemuDiskBandwidthOptions',
+ ].forEach(p => this.down(p).setDrive(drive));
+ },
+
+ setNodename: function(nodename) {
+ const me = this;
+ const hdstorage = me.down('#hdstorage');
+ if (hdstorage) {
+ hdstorage.setNodename(nodename);
+ }
+ const hdimage = me.down('#hdimage');
+ if (hdimage) {
+ hdimage.setStorage(undefined, nodename);
+ }
+ },
+
+ // going over the items with "down" is not yet possible in initComponent => use beforeRender
+ beforeRender: function() {
+ const me = this;
+ // any other panel because this has no height yet
+ if (me.insideWizard) {
+ const panelHeight = me.up('#wizcontent').down('inputpanel').getHeight();
+ me.setHeight(panelHeight);
+ }
+ },
+
+ initComponent: function() {
+ const me = this;
+
+ me.items = [
+ {
+ title: 'Drive',
+ xtype: 'panel',
+ layout: {
+ type: 'vbox',
+ },
+ defaults: {
+ width: '100%',
+ margin: '0 0 10 0',
+ },
+ items: [
+ {
+ xtype: 'pveQemuDiskData',
+ isCreate: me.isCreate,
+ confid: me.confid,
+ unused: me.unused,
+ insideWizard: me.insideWizard,
+ },
+ {
+ xtype: 'pveQemuDiskBasicOptions',
+ isCreate: me.isCreate,
+ confid: me.confid,
+ unused: me.unused,
+ insideWizard: me.insideWizard,
+ },
+ ],
+ },
+ {
+ title: 'Bandwidth Limits',
+ xtype: 'pveQemuDiskBandwidthOptions',
+ isCreate: me.isCreate,
+ confid: me.confid,
+ unused: me.unused,
+ insideWizard: me.insideWizard,
+ },
+ ];
+
+ me.callParent();
+
+ const updateConfid = () => {
+ const confid = me.down('pveQemuDiskData').getConfid();
+ me.down('pveQemuDiskBasicOptions').confid = confid;
+ me.down('pveQemuDiskBandwidthOptions').confid = confid;
+ };
+ const selector = me.down('pveQemuDiskData').down('pveControllerSelector');
+ if (selector) {
+ // confid (controller + deviceid) is the key for which panels belong together
+ // it is changed only in pveQemuDiskData => Always update confid in the other panels
+ // see mergeDiskValues
+ selector.query('field').forEach(f => f.on('change', updateConfid));
+ } else {
+ //no confid change possible, e.g. in edit window for a disk that is already attached
+ updateConfid();
+ }
+
+ me.setTabPosition(me.insideWizard ? 'bottom' : 'top');
+ },
+
+ setVMConfig: function(vmconfig) {
+ this.down('pveQemuDiskData').setVMConfig(vmconfig);
+ },
+
+ statics: {
+ // One tabpanel represents a whole drive/disk.
+ // Each panel in it has only some options.
+ // Values are collected by the wizard from inputpanels, ignoring tabpanels.
+ // But for disks (=> bus_match) we need values from all child inputpanels of the tabpanel.
+ // Each child panel prepares for this in onGetValues so that we can put it together here.
+ mergeDiskValues: function(values) {
+ for (const [key, value] of Object.entries(values)) {
+ if (key.match(PVE.Utils.bus_match) && Array.isArray(value)) {
+ const driveObj = value.reduce((acc, cur) => ({ ...acc, ...cur }));
+ values[key] = PVE.Parser.printQemuDrive(driveObj);
+ }
+ }
+ return values;
+ },
+ },
+});
+
+Ext.define('PVE.qemu.DiskWindow', {
+ extend: 'Proxmox.window.Edit',
+
+ isAdd: true,
+
+ backgroundDelay: 5,
+
+ bodyPadding: 0,
+
+ initComponent: function() {
+ const me = this;
+
+ const selnode = me.pveSelNode && me.pveSelNode.data && me.pveSelNode.data.node;
+ if (selnode && !me.nodename) {
+ me.nodename = selnode;
+ }
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ const unused = me.confid && me.confid.match(/^unused\d+$/);
+
+ me.isCreate = me.confid ? unused : true;
+
+ const ipanel = Ext.create('PVE.qemu.Disk', {
+ confid: me.confid,
+ unused: unused,
+ isCreate: me.isCreate,
+ });
+ ipanel.setNodename(me.nodename);
+
+ if (unused) {
+ me.subject = gettext('Unused Disk');
+ } else if (me.isCreate) {
+ me.subject = gettext('Hard Disk');
+ } else {
+ me.subject = gettext('Hard Disk') + ' (' + me.confid + ')';
+ }
+
+ me.items = [ipanel];
+
+ me.callParent();
+ /* 'data' is assigned an empty array in same file, and here we
+ * use it like an object
+ */
+ me.load({
+ success: function(response, options) {
+ ipanel.setVMConfig(response.result.data);
+ if (me.confid) {
+ const value = response.result.data[me.confid];
+ const drive = PVE.Parser.parseQemuDrive(me.confid, value);
+ if (!drive) {
+ Ext.Msg.alert(gettext('Error'), 'Unable to parse drive options');
+ me.close();
+ return;
+ }
+ ipanel.setDrive(drive);
+ me.isValid(); // trigger validation
+ }
+ },
+ });
+ },
+
+ getValues: function() {
+ return PVE.qemu.Disk.mergeDiskValues(this.callParent());
+ },
+});
diff --git a/www/manager6/qemu/disk/DiskBandwidthOptions.js b/www/manager6/qemu/disk/DiskBandwidthOptions.js
new file mode 100644
index 00000000..3834bb6a
--- /dev/null
+++ b/www/manager6/qemu/disk/DiskBandwidthOptions.js
@@ -0,0 +1,132 @@
+/* 'change' property is assigned a string and then a function */
+Ext.define('PVE.qemu.DiskBandwidthOptions', {
+ extend: 'Proxmox.panel.InputPanel',
+ alias: 'widget.pveQemuDiskBandwidthOptions',
+ mixins: ['Proxmox.Mixin.CBind'],
+
+ onlineHelp: 'qm_hard_disk',
+
+ insideWizard: false,
+
+ unused: false, // ADD usused disk imaged
+
+ vmconfig: {}, // used to select usused disks
+
+ cbindData: {
+ labelWidth: 140,
+ },
+
+ onGetValues: function(values) {
+ const me = this;
+ const result = {};
+
+ const names = ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr'];
+ Ext.Array.each(names, function(name) {
+ const burstName = name + '_max';
+ PVE.Utils.propertyStringSet(result, values[name], name);
+ PVE.Utils.propertyStringSet(result, values[burstName], burstName);
+ });
+
+ if (!me.confid) {
+ throw 'confid must be set by parent';
+ }
+ return { [me.confid]: result }; // see mergeDiskValues
+ },
+
+ setDrive: function(drive) {
+ this.setValues(drive); // non-existent values are ignored
+ },
+
+ column1: [
+ {
+ xtype: 'numberfield',
+ name: 'mbps_rd',
+ minValue: 1,
+ step: 1,
+ fieldLabel: gettext('Read limit') + ' (MB/s)',
+ emptyText: gettext('unlimited'),
+ cbind: {
+ labelWidth: '{labelWidth}',
+ },
+ },
+ {
+ xtype: 'numberfield',
+ name: 'mbps_wr',
+ minValue: 1,
+ step: 1,
+ fieldLabel: gettext('Write limit') + ' (MB/s)',
+ emptyText: gettext('unlimited'),
+ cbind: {
+ labelWidth: '{labelWidth}',
+ },
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'iops_rd',
+ minValue: 10,
+ step: 10,
+ fieldLabel: gettext('Read limit') + ' (ops/s)',
+ emptyText: gettext('unlimited'),
+ cbind: {
+ labelWidth: '{labelWidth}',
+ },
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'iops_wr',
+ minValue: 10,
+ step: 10,
+ fieldLabel: gettext('Write limit') + ' (ops/s)',
+ emptyText: gettext('unlimited'),
+ cbind: {
+ labelWidth: '{labelWidth}',
+ },
+ },
+ ],
+ column2: [
+ {
+ xtype: 'numberfield',
+ name: 'mbps_rd_max',
+ minValue: 1,
+ step: 1,
+ fieldLabel: gettext('Read max burst') + ' (MB)',
+ emptyText: gettext('default'),
+ cbind: {
+ labelWidth: '{labelWidth}',
+ },
+ },
+ {
+ xtype: 'numberfield',
+ name: 'mbps_wr_max',
+ minValue: 1,
+ step: 1,
+ fieldLabel: gettext('Write max burst') + ' (MB)',
+ emptyText: gettext('default'),
+ cbind: {
+ labelWidth: '{labelWidth}',
+ },
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'iops_rd_max',
+ minValue: 10,
+ step: 10,
+ fieldLabel: gettext('Read max burst') + ' (ops)',
+ emptyText: gettext('default'),
+ cbind: {
+ labelWidth: '{labelWidth}',
+ },
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ name: 'iops_wr_max',
+ minValue: 10,
+ step: 10,
+ fieldLabel: gettext('Write max burst') + ' (ops)',
+ emptyText: gettext('default'),
+ cbind: {
+ labelWidth: '{labelWidth}',
+ },
+ },
+ ],
+});
diff --git a/www/manager6/qemu/disk/DiskBasicOptions.js b/www/manager6/qemu/disk/DiskBasicOptions.js
new file mode 100644
index 00000000..cee4da8c
--- /dev/null
+++ b/www/manager6/qemu/disk/DiskBasicOptions.js
@@ -0,0 +1,102 @@
+/* 'change' property is assigned a string and then a function */
+Ext.define('PVE.qemu.DiskBasicOptions', {
+ extend: 'Proxmox.panel.InputPanel',
+ alias: 'widget.pveQemuDiskBasicOptions',
+ onlineHelp: 'qm_hard_disk',
+ mixins: ['Proxmox.Mixin.CBind'],
+
+ insideWizard: false,
+
+ unused: false, // ADD usused disk imaged
+
+ vmconfig: {}, // used to select usused disks
+
+ onGetValues: function(values) {
+ const me = this;
+ const result = {};
+
+ PVE.Utils.propertyStringSet(result, !values.backup, 'backup', '0');
+ PVE.Utils.propertyStringSet(result, values.noreplicate, 'replicate', 'no');
+ PVE.Utils.propertyStringSet(result, values.ssd, 'ssd', 'on');
+ PVE.Utils.propertyStringSet(result, values.iothread, 'iothread', 'on');
+ PVE.Utils.propertyStringSet(result, values.discard, 'discard', 'on');
+
+ if (!me.confid) {
+ throw 'confid must be set by parent';
+ }
+ return { [me.confid]: result }; // see mergeDiskValues
+ },
+
+ setDrive: function(drive) {
+ const me = this;
+
+ const values = {};
+ values.backup = PVE.Parser.parseBoolean(drive.backup, 1);
+ values.noreplicate = !PVE.Parser.parseBoolean(drive.replicate, 1);
+ values.ssd = PVE.Parser.parseBoolean(drive.ssd);
+ values.iothread = PVE.Parser.parseBoolean(drive.iothread);
+ values.discard = drive.discard === 'on';
+
+ me.setValues(values);
+ },
+
+ column1: [
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('Discard'),
+ reference: 'discard',
+ name: 'discard',
+ },
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('SSD emulation'),
+ name: 'ssd',
+ reference: 'ssd',
+ },
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: 'IO thread',
+ reference: 'iothread',
+ name: 'iothread',
+ listeners: {
+ change: function(field, value) {
+ if (field.up('pveQemuDiskBasicOptions').insideWizard) {
+ const vmScsiType = value ? 'virtio-scsi-single' : 'virtio-scsi-pci';
+ const disk = field.up('pveQemuDisk');
+ disk.down('field[name=scsiController]').setValue(vmScsiType);
+ }
+ },
+ },
+ },
+ ],
+ column2: [
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('Backup'),
+ autoEl: {
+ tag: 'div',
+ 'data-qtip': gettext('Include volume in backup job'),
+ },
+ name: 'backup',
+ },
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('Skip replication'),
+ name: 'noreplicate',
+ },
+ ],
+
+ listeners: {
+ beforerender: function() {
+ // Cannot query fields in initComponent => beforerender.
+ // Also those are all optional fields, so we don't need to run this right
+ // when the window opens
+ const me = this;
+ if (me.isCreate) {
+ me.down('field[name=backup]').setValue(true); // else set by setDrive
+ }
+ me.down('field[name=ssd]').setDisabled(me.confid && me.confid.match(/^virtio/));
+ me.down('field[name=iothread]').setDisabled(me.confid && !me.confid.match(/^(virtio|scsi)/));
+ },
+ },
+});
diff --git a/www/manager6/qemu/disk/DiskData.js b/www/manager6/qemu/disk/DiskData.js
new file mode 100644
index 00000000..d0fc0aa4
--- /dev/null
+++ b/www/manager6/qemu/disk/DiskData.js
@@ -0,0 +1,174 @@
+/* 'change' property is assigned a string and then a function */
+Ext.define('PVE.qemu.DiskData', {
+ extend: 'Proxmox.panel.InputPanel',
+ alias: 'widget.pveQemuDiskData',
+ onlineHelp: 'qm_hard_disk',
+
+ insideWizard: false,
+
+ unused: false,
+
+ vmconfig: {}, // used to select usused disks
+
+ getConfid() {
+ const me = this;
+ return me.isCreate ? this.down('pveControllerSelector').getConfid() : me.confid;
+ },
+
+ onGetValues: function(values) {
+ const me = this;
+ const result = {};
+
+ if (me.unused) {
+ result.file = me.vmconfig[values.unusedId];
+ // in this case we could also extract the confid from `values`
+ // but getConfid() works always
+ } else if (me.isCreate) {
+ if (values.hdimage) {
+ result.file = values.hdimage;
+ } else {
+ result.file = values.hdstorage + ":" + values.disksize;
+ }
+ result.format = values.diskformat;
+ } else {
+ // editing already attached disk
+ result.file = me.down('field[name=hdimage]').getValue();
+ }
+
+ PVE.Utils.propertyStringSet(result, values.cache, 'cache');
+
+ return { [me.getConfid()]: result }; // see mergeDiskValues
+ },
+
+ setVMConfig: function(vmconfig) {
+ const me = this;
+
+ me.vmconfig = vmconfig;
+
+ if (me.bussel) {
+ me.bussel.setVMConfig(vmconfig);
+ me.scsiController.setValue(vmconfig.scsihw);
+ }
+ if (me.unusedDisks) {
+ const disklist = [];
+ Ext.Object.each(vmconfig, function(key, value) {
+ if (key.match(/^unused\d+$/)) {
+ disklist.push([key, value]);
+ }
+ });
+ me.unusedDisks.store.loadData(disklist);
+ me.unusedDisks.setValue(me.confid);
+ }
+ },
+
+ setDrive: function(drive) {
+ const me = this;
+
+ const values = {};
+ const match = drive.file.match(/^([^:]+):/);
+ if (match) {
+ values.hdstorage = match[1];
+ }
+
+ values.hdimage = drive.file;
+ values.diskformat = drive.format || 'raw';
+ values.cache = drive.cache || '__default__';
+
+ me.setValues(values);
+ },
+
+ column2: [
+ {
+ xtype: 'CacheTypeSelector',
+ name: 'cache',
+ value: '__default__',
+ fieldLabel: gettext('Cache'),
+ },
+ ],
+
+ initComponent: function() {
+ const me = this;
+
+ me.column1 = [];
+ // scsiController & bussel must not be in every reference => work on copy
+ me.column2 = [...me.column2];
+
+ if (!me.confid || me.unused) {
+ // Create now => easily set visible from bussel listener
+ me.scsiController = Ext.create('Ext.form.field.Display', {
+ xtype: 'displayfield',
+ fieldLabel: gettext('SCSI Controller'),
+ reference: 'scsiController',
+ name: 'scsiController',
+ bind: me.insideWizard ? {
+ value: '{current.scsihw}',
+ } : undefined,
+ renderer: PVE.Utils.render_scsihw,
+ submitValue: false,
+ hidden: true,
+ });
+
+ // Create now => Children initialized => setVMConfig possible
+ me.bussel = Ext.create('PVE.form.ControllerSelector', {
+ xtype: 'pveControllerSelector',
+ itemId: 'bussel',
+ vmconfig: me.insideWizard ? { ide2: 'cdrom' } : {},
+ });
+
+ const changeFunction = (_, newValue) => {
+ const allowIOthread = newValue.match(/^(virtio|scsi)/);
+ const iothreadField = me.up('pveQemuDisk').down('field[name=iothread]');
+ iothreadField.setDisabled(!allowIOthread);
+ if (!allowIOthread) {
+ iothreadField.setValue(false);
+ }
+
+ const virtio = newValue.match(/^virtio/);
+ const ssdField = me.up('pveQemuDisk').down('field[name=ssd]');
+ ssdField.setDisabled(virtio);
+ if (virtio) {
+ ssdField.setValue(false);
+ }
+
+ me.scsiController.setVisible(newValue.match(/^scsi/));
+ };
+ me.bussel.down('field[name=controller]').addListener('change', changeFunction);
+
+ me.column2.unshift(me.bussel, me.scsiController);
+ }
+
+ if (me.unused) {
+ // Ext.create now => setVMConfig possible
+ me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', {
+ name: 'unusedId',
+ xtype: 'proxmoxKVComboBox',
+ fieldLabel: gettext('Disk image'),
+ matchFieldWidth: false,
+ listConfig: {
+ width: 350,
+ },
+ data: [],
+ allowBlank: false,
+ });
+ me.column1.push(me.unusedDisks);
+ } else if (me.isCreate) {
+ me.column1.push({
+ xtype: 'pveDiskStorageSelector',
+ storageContent: 'images',
+ storageLabel: gettext('Storage'),
+ name: 'disk',
+ autoSelect: me.insideWizard,
+ });
+ } else {
+ me.column1.push({
+ xtype: 'textfield',
+ disabled: true,
+ submitValue: false,
+ fieldLabel: gettext('Disk image'),
+ name: 'hdimage',
+ });
+ }
+
+ me.callParent();
+ },
+});
--
2.30.2
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2021-07-08 8:41 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-08 8:40 [pve-devel] [PATCH manager] ui: disk edit: Split out bandwidth limits Dominic Jäger
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal