-/*jslint confusion: true */
/* 'change' property is assigned a string and then a function */
Ext.define('PVE.qemu.HDInputPanel', {
- extend: 'PVE.panel.InputPanel',
+ extend: 'Proxmox.panel.InputPanel',
alias: 'widget.pveQemuHDInputPanel',
onlineHelp: 'qm_hard_disk',
vmconfig: {}, // used to select usused disks
- controller: {
+ viewModel: {
+ data: {
+ isSCSI: false,
+ isVirtIO: false,
+ isSCSISingle: false,
+ },
+ },
+ controller: {
xclass: 'Ext.app.ViewController',
onControllerChange: function(field) {
- var value = field.getValue();
+ let me = this;
+ let vm = this.getViewModel();
- var allowIOthread = value.match(/^(virtio|scsi)/);
- this.lookup('iothread').setDisabled(!allowIOthread);
- if (!allowIOthread) {
- this.lookup('iothread').setValue(false);
- }
+ let value = field.getValue();
+ vm.set('isSCSI', value.match(/^scsi/));
+ vm.set('isVirtIO', value.match(/^virtio/));
- var allowDiscard = value.match(/^scsi/);
- this.lookup('discard').setDisabled(!allowDiscard);
- if (!allowDiscard) {
- this.lookup('discard').setValue(false);
- }
+ me.fireIdChange();
+ },
+
+ fireIdChange: function() {
+ let view = this.getView();
+ view.fireEvent('diskidchange', view, view.bussel.getConfId());
},
control: {
'field[name=controller]': {
change: 'onControllerChange',
- afterrender: 'onControllerChange'
+ afterrender: 'onControllerChange',
+ },
+ 'field[name=deviceid]': {
+ change: 'fireIdChange',
},
- 'field[name=hdstorage]': {
+ 'field[name=scsiController]': {
change: function(f, value) {
- if (!value) { // initial store loading fires an unwanted 'change'
- return;
- }
- var me = this.getView();
- var rec = f.store.getById(value);
- if (rec.data.type === 'iscsi') {
- me.hdfilesel.setStorage(value);
- me.hdfilesel.setDisabled(false);
- me.formatsel.setValue('raw');
- me.formatsel.setDisabled(true);
- me.hdfilesel.setVisible(true);
- me.hdsizesel.setDisabled(true);
- me.hdsizesel.setVisible(false);
- } else if (rec.data.type === 'lvm' ||
- rec.data.type === 'lvmthin' ||
- rec.data.type === 'drbd' ||
- rec.data.type === 'rbd' ||
- rec.data.type === 'sheepdog' ||
- rec.data.type === 'zfs' ||
- rec.data.type === 'zfspool') {
- me.hdfilesel.setDisabled(true);
- me.hdfilesel.setVisible(false);
- me.formatsel.setValue('raw');
- me.formatsel.setDisabled(true);
- me.hdsizesel.setDisabled(false);
- me.hdsizesel.setVisible(true);
- } else {
- me.hdfilesel.setDisabled(true);
- me.hdfilesel.setVisible(false);
- me.formatsel.setValue('qcow2');
- me.formatsel.setDisabled(false);
- me.hdsizesel.setDisabled(false);
- me.hdsizesel.setVisible(true);
- }
- }
+ let vm = this.getViewModel();
+ vm.set('isSCSISingle', value === 'virtio-scsi-single');
+ },
+ },
+ },
+
+ init: function(view) {
+ var vm = this.getViewModel();
+ if (view.isCreate) {
+ vm.set('isIncludedInBackup', true);
}
- }
+ if (view.confid) {
+ vm.set('isSCSI', view.confid.match(/^scsi/));
+ vm.set('isVirtIO', view.confid.match(/^virtio/));
+ }
+ },
},
onGetValues: function(values) {
var me = this;
var params = {};
- var confid = me.confid || (values.controller + values.deviceid);
+ var confid = me.confid || values.controller + values.deviceid;
if (me.unused) {
me.drive.file = me.vmconfig[values.unusedId];
me.drive.format = values.diskformat;
}
- if (values.nobackup) {
- me.drive.backup = 'no';
- } else {
- delete me.drive.backup;
- }
-
- if (values.noreplicate) {
- me.drive.replicate = 'no';
- } else {
- delete me.drive.replicate;
- }
-
- if (values.discard) {
- me.drive.discard = 'on';
- } else {
- delete me.drive.discard;
- }
-
- if (values.iothread) {
- me.drive.iothread = 'on';
- // do not silently change a VM-wide option after creating it
- if (me.insideWizard) {
- params.scsihw = 'virtio-scsi-single';
- }
- } else {
- delete me.drive.iothread;
- }
-
- if (values.cache) {
- me.drive.cache = values.cache;
- } else {
- delete me.drive.cache;
- }
+ 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.readOnly, 'ro', 'on');
+ PVE.Utils.propertyStringSet(me.drive, values.cache, 'cache');
+ PVE.Utils.propertyStringSet(me.drive, values.aio, 'aio');
+
+ ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr'].forEach(name => {
+ let 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;
},
+ updateVMConfig: function(vmconfig) {
+ var me = this;
+ me.vmconfig = vmconfig;
+ me.bussel?.updateVMConfig(vmconfig);
+ },
+
setVMConfig: function(vmconfig) {
var me = this;
if (me.bussel) {
me.bussel.setVMConfig(vmconfig);
+ me.scsiController.setValue(vmconfig.scsihw);
}
if (me.unusedDisks) {
var disklist = [];
}
values.hdimage = drive.file;
- values.nobackup = !PVE.Parser.parseBoolean(drive.backup, 1);
+ 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.discard = drive.discard === 'on';
+ values.ssd = PVE.Parser.parseBoolean(drive.ssd);
values.iothread = PVE.Parser.parseBoolean(drive.iothread);
+ values.readOnly = PVE.Parser.parseBoolean(drive.ro);
+ values.aio = drive.aio || '__default__';
+
+ 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.hdstoragesel.setNodename(nodename);
- me.hdfilesel.setStorage(undefined, nodename);
+ me.down('#hdstorage').setNodename(nodename);
+ me.down('#hdimage').setStorage(undefined, nodename);
},
- initComponent : function() {
+ hasAdvanced: true,
+
+ initComponent: function() {
var me = this;
me.drive = {};
- me.column1 = [];
- me.column2 = [];
+ let column1 = [];
+ let column2 = [];
+
+ let advancedColumn1 = [];
+ let advancedColumn2 = [];
if (!me.confid || me.unused) {
me.bussel = Ext.create('PVE.form.ControllerSelector', {
- vmconfig: me.insideWizard ? {ide2: 'cdrom'} : {}
+ vmconfig: me.vmconfig,
+ selectFree: true,
});
- me.column1.push(me.bussel);
+ column1.push(me.bussel);
+
+ me.scsiController = Ext.create('Ext.form.field.Display', {
+ fieldLabel: gettext('SCSI Controller'),
+ reference: 'scsiController',
+ name: 'scsiController',
+ bind: me.insideWizard ? {
+ value: '{current.scsihw}',
+ visible: '{isSCSI}',
+ } : {
+ visible: '{isSCSI}',
+ },
+ renderer: PVE.Utils.render_scsihw,
+ submitValue: false,
+ hidden: true,
+ });
+ column1.push(me.scsiController);
}
if (me.unused) {
- me.unusedDisks = Ext.create('PVE.form.KVComboBox', {
+ me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', {
name: 'unusedId',
fieldLabel: gettext('Disk image'),
matchFieldWidth: false,
listConfig: {
- width: 350
+ width: 350,
},
data: [],
- allowBlank: false
+ allowBlank: false,
});
- me.column1.push(me.unusedDisks);
+ column1.push(me.unusedDisks);
} else if (me.isCreate) {
- me.formatsel = Ext.create('PVE.form.DiskFormatSelector', {
- name: 'diskformat',
- fieldLabel: gettext('Format'),
- value: 'qcow2',
- allowBlank: false
- });
-
- me.hdfilesel = Ext.create('PVE.form.FileSelector', {
- name: 'hdimage',
- nodename: me.nodename,
+ column1.push({
+ xtype: 'pveDiskStorageSelector',
storageContent: 'images',
- fieldLabel: gettext('Disk image'),
- disabled: true,
- hidden: true,
- allowBlank: false
- });
-
- me.hdsizesel = Ext.createWidget('numberfield', {
- name: 'disksize',
- minValue: 0.001,
- maxValue: 128*1024,
- decimalPrecision: 3,
- value: '32',
- fieldLabel: gettext('Disk size') + ' (GB)',
- allowBlank: false
- });
-
- me.hdstoragesel = Ext.create('PVE.form.StorageSelector', {
- name: 'hdstorage',
+ name: 'disk',
nodename: me.nodename,
- fieldLabel: gettext('Storage'),
- storageContent: 'images',
autoSelect: me.insideWizard,
- allowBlank: false
});
- me.column1.push(me.hdstoragesel);
- me.column1.push(me.hdfilesel);
- me.column1.push(me.hdsizesel);
- me.column1.push(me.formatsel);
-
} else {
- me.column1.push({
+ column1.push({
xtype: 'textfield',
disabled: true,
submitValue: false,
fieldLabel: gettext('Disk image'),
- name: 'hdimage'
+ name: 'hdimage',
});
}
- me.column2.push({
- xtype: 'CacheTypeSelector',
- name: 'cache',
- value: '__default__',
- fieldLabel: gettext('Cache')
- },
- {
- xtype: 'pvecheckbox',
- fieldLabel: gettext('No backup'),
- name: 'nobackup'
- },
- {
- xtype: 'pvecheckbox',
- hidden: me.insideWizard,
- fieldLabel: gettext('Skip replication'),
- name: 'noreplicate'
- },
- {
- xtype: 'pvecheckbox',
- fieldLabel: gettext('Discard'),
- disabled: me.confid && !me.confid.match(/^scsi/),
- reference: 'discard',
- name: 'discard'
- },
- {
- xtype: 'pvecheckbox',
- disabled: me.confid && !me.confid.match(/^(virtio|scsi)/),
- fieldLabel: gettext('IO thread'),
- reference: 'iothread',
- name: 'iothread'
- });
+ column2.push(
+ {
+ xtype: 'CacheTypeSelector',
+ name: 'cache',
+ value: '__default__',
+ fieldLabel: gettext('Cache'),
+ },
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('Discard'),
+ reference: 'discard',
+ name: 'discard',
+ },
+ {
+ xtype: 'proxmoxcheckbox',
+ name: 'iothread',
+ fieldLabel: 'IO thread',
+ clearOnDisable: true,
+ bind: {
+ disabled: '{!isVirtIO && !isSCSI}',
+ // Checkbox.setValue handles Arrays in a different way, therefore cast to bool
+ value: '{!!isVirtIO || (isSCSI && isSCSISingle)}',
+ },
+ },
+ );
+
+ advancedColumn1.push(
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('SSD emulation'),
+ name: 'ssd',
+ clearOnDisable: true,
+ bind: {
+ disabled: '{isVirtIO}',
+ },
+ },
+ {
+ xtype: 'proxmoxcheckbox',
+ name: 'readOnly', // `ro` in the config, we map in get/set values
+ defaultValue: 0,
+ fieldLabel: gettext('Read-only'),
+ clearOnDisable: true,
+ bind: {
+ disabled: '{!isVirtIO && !isSCSI}',
+ },
+ },
+ );
+
+ advancedColumn2.push(
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('Backup'),
+ autoEl: {
+ tag: 'div',
+ 'data-qtip': gettext('Include volume in backup job'),
+ },
+ name: 'backup',
+ bind: {
+ value: '{isIncludedInBackup}',
+ },
+ },
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('Skip replication'),
+ name: 'noreplicate',
+ },
+ {
+ xtype: 'proxmoxKVComboBox',
+ name: 'aio',
+ fieldLabel: gettext('Async IO'),
+ allowBlank: false,
+ value: '__default__',
+ comboItems: [
+ ['__default__', Proxmox.Utils.defaultText + ' (io_uring)'],
+ ['io_uring', 'io_uring'],
+ ['native', 'native'],
+ ['threads', 'threads'],
+ ],
+ },
+ );
+
+ let labelWidth = 140;
+
+ let bwColumn1 = [
+ {
+ 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'),
+ },
+ ];
+
+ let bwColumn2 = [
+ {
+ 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.items = [
+ {
+ xtype: 'tabpanel',
+ plain: true,
+ bodyPadding: 10,
+ border: 0,
+ items: [
+ {
+ title: gettext('Disk'),
+ xtype: 'inputpanel',
+ reference: 'diskpanel',
+ column1,
+ column2,
+ advancedColumn1,
+ advancedColumn2,
+ showAdvanced: me.showAdvanced,
+ getValues: () => ({}),
+ },
+ {
+ title: gettext('Bandwidth'),
+ xtype: 'inputpanel',
+ reference: 'bwpanel',
+ column1: bwColumn1,
+ column2: bwColumn2,
+ showAdvanced: me.showAdvanced,
+ getValues: () => ({}),
+ },
+ ],
+ },
+ ];
me.callParent();
- }
+ },
+
+ setAdvancedVisible: function(visible) {
+ this.lookup('diskpanel').setAdvancedVisible(visible);
+ this.lookup('bwpanel').setAdvancedVisible(visible);
+ },
});
-/*jslint confusion: false */
Ext.define('PVE.qemu.HDEdit', {
- extend: 'PVE.window.Edit',
+ extend: 'Proxmox.window.Edit',
isAdd: true,
- initComponent : function() {
+ backgroundDelay: 5,
+
+ width: 600,
+ bodyPadding: 0,
+
+ initComponent: function() {
var me = this;
var nodename = me.pveSelNode.data.node;
confid: me.confid,
nodename: nodename,
unused: unused,
- isCreate: me.isCreate
+ isCreate: me.isCreate,
});
- var subject;
if (unused) {
me.subject = gettext('Unused Disk');
} else if (me.isCreate) {
me.subject = gettext('Hard Disk') + ' (' + me.confid + ')';
}
- me.items = [ ipanel ];
+ me.items = [ipanel];
me.callParent();
- /*jslint confusion: true*/
/* 'data' is assigned an empty array in same file, and here we
* use it like an object
*/
ipanel.setDrive(drive);
me.isValid(); // trigger validation
}
- }
+ },
});
- /*jslint confusion: false*/
- }
+ },
});