summaryGrid.getStore().setData(Object.entries(values).map(([key, value]) => ({ key, value })));
},
+ calculateAdditionalCDIdx: function() {
+ let me = this;
+
+ let maxIde = me.getMaxControllerId('ide');
+ let maxSata = me.getMaxControllerId('sata');
+ // only ide0 and ide2 can be used reliably for isos (e.g. for q35)
+ if (maxIde < 0) {
+ return 'ide0';
+ }
+ if (maxIde < 2) {
+ return 'ide2';
+ }
+ if (maxSata < PVE.Utils.diskControllerMaxIDs.sata - 1) {
+ return `sata${maxSata+1}`;
+ }
+
+ return '';
+ },
+
// assume assigned sata disks indices are continuous, so without holes
- getMaxSata: function() {
+ getMaxControllerId: function(controller) {
let me = this;
let view = me.getView();
- if (view.maxSata !== undefined) {
- return view.maxSata;
+ if (!controller) {
+ return -1;
+ }
+
+ let max = view[`max${controller}`];
+ if (max !== undefined) {
+ return max;
}
- view.maxSata = -1;
+ max = -1;
for (const key of Object.keys(me.getView().vmConfig)) {
- if (!key.toLowerCase().startsWith('sata')) {
+ if (!key.toLowerCase().startsWith(controller)) {
continue;
}
- let idx = parseInt(key.slice(4), 10);
- if (idx > view.maxSata) {
- view.maxSata = idx;
+ let idx = parseInt(key.slice(controller.length), 10);
+ if (idx > max) {
+ max = idx;
}
}
me.lookup('diskGrid').getStore().each(rec => {
- if (!rec.data.id.toLowerCase().startsWith('sata')) {
+ if (!rec.data.id.toLowerCase().startsWith(controller)) {
return;
}
- let idx = parseInt(rec.data.id.slice(4), 10);
- if (idx > view.maxSata) {
- view.maxSata = idx;
+ let idx = parseInt(rec.data.id.slice(controller.length), 10);
+ if (idx > max) {
+ max = idx;
}
});
me.lookup('cdGrid').getStore().each(rec => {
- if (!rec.data.id.toLowerCase().startsWith('sata')) {
+ if (!rec.data.id.toLowerCase().startsWith(controller) || rec.data.hidden) {
return;
}
- let idx = parseInt(rec.data.id.slice(4), 10);
- if (idx > view.maxSata) {
- view.maxSata = idx;
+ let idx = parseInt(rec.data.id.slice(controller.length), 10);
+ if (idx > max) {
+ max = idx;
}
});
- return view.maxSata;
+ view[`max${controller}`] = max;
+ return max;
},
mapDisk: function(value, metaData) {
return value;
}
let offset = parseInt(value.slice(4), 10);
- let newIdx = offset + me.getMaxSata() + 1;
- if (newIdx > PVE.Utils.diskControllerMaxIDs.sata) {
+ let newIdx = offset + me.getMaxControllerId('sata') + 1;
+ if (me.getViewModel().get('isWindows') && me.getView().additionalCdIdx?.startsWith('sata')) {
+ // additionalCdIdx takes the highest sata port
+ newIdx++;
+ }
+ if (newIdx >= PVE.Utils.diskControllerMaxIDs.sata) {
let prefix = '';
if (metaData !== undefined) {
// we're in the renderer so put a warning here
return `sata${newIdx}`;
},
- refreshDiskGrid: function() {
+ refreshGrids: function() {
this.lookup('diskGrid').reconfigure();
+ this.lookup('cdGrid').reconfigure();
+ },
+
+ onOSTypeChange: function(_cb, value) {
+ let me = this;
+ if (!value) {
+ return;
+ }
+ let store = me.lookup('cdGrid').getStore();
+ let collection = store.getData().getSource() ?? store.getData();
+ let rec = collection.find('autogenerated', true);
+
+ let isWindows = (value ?? '').startsWith('w');
+ if (rec) {
+ rec.set('hidden', !isWindows);
+ rec.commit();
+ }
+ let prepareVirtio = me.lookup('mapSata').getValue();
+ me.lookup('scsihw').setValue(prepareVirtio && isWindows ? 'virtio-scsi-single' : me.getView().vmConfig.scsihw);
+
+ me.refreshGrids();
+ },
+
+ onPrepareVirtioChange: function(_cb, value) {
+ let me = this;
+
+ let scsihw = me.lookup('scsihw');
+ scsihw.suspendEvents();
+ scsihw.setValue(value ? 'virtio-scsi-single' : me.getView().vmConfig.scsihw);
+ scsihw.resumeEvents();
+
+ me.refreshGrids();
+ },
+
+ onScsiHwChange: function(_field, value) {
+ let me = this;
+ me.getView().vmConfig.scsihw = value;
},
control: {
activate: 'calculateConfig',
},
'proxmoxcheckbox[reference=mapSata]': {
- change: 'refreshDiskGrid',
+ change: 'onPrepareVirtioChange',
},
'combobox[name=ostype]': {
- change: 'refreshDiskGrid',
+ change: 'onOSTypeChange',
+ },
+ 'pveScsiHwSelector': {
+ change: 'onScsiHwChange',
},
},
},
coreCount: 1,
socketCount: 1,
liveImport: false,
- os: '',
+ os: 'l26',
+ maxCdDrives: false,
warnings: [],
},
+ get('warnings').map(w => `<li>${w}</li>`).join('') + '</ul>',
liveImportNote: get => !get('liveImport') ? ''
: gettext('Note: If anything goes wrong during the live-import, new data written by the VM may be lost.'),
- isWindows: get => (get('os') ?? '').startsWith('win'),
+ isWindows: get => (get('os') ?? '').startsWith('w'),
},
},
reference: 'socketsField',
value: 1,
minValue: 1,
- maxValue: 4,
+ maxValue: 128,
allowBlank: true,
bind: {
value: '{socketCount}',
reference: 'coresField',
value: 1,
minValue: 1,
- maxValue: 128,
+ maxValue: 1024,
allowBlank: true,
bind: {
value: '{coreCount}',
},
{
xtype: 'pveMemoryField',
- fieldLabel: gettext('Memory'),
+ fieldLabel: gettext('Memory') + ' (MiB)',
name: 'memory',
reference: 'memoryField',
value: 512,
allowBlank: true,
},
- {
- //spacer
- xtype: 'displayfield',
- },
+ { xtype: 'displayfield' }, // spacer
+ { xtype: 'displayfield' }, // spacer
{
xtype: 'pveDiskStorageSelector',
reference: 'defaultStorage',
name: 'cpu',
reference: 'cputype',
value: 'x86-64-v2-AES',
- fieldLabel: gettext('Type'),
+ fieldLabel: gettext('CPU Type'),
},
{
xtype: 'displayfield',
data: PVE.Utils.kvm_ostypes.Linux,
},
},
+ { xtype: 'displayfield' }, // spacer
{
xtype: 'PVE.form.BridgeSelector',
reference: 'defaultBridge',
title: gettext('Advanced'),
xtype: 'inputpanel',
- column1: [
- {
- xtype: 'pveScsiHwSelector',
- reference: 'scsihw',
- name: 'scsihw',
- submitValue: false,
- fieldLabel: gettext('SCSI Controller'),
- },
- ],
-
- column2: [
- {
- xtype: 'proxmoxcheckbox',
- fieldLabel: gettext('Map SCSI to SATA'),
- labelWidth: 120,
- reference: 'mapSata',
- isFormField: false,
- hidden: true,
- disabled: true,
- bind: {
- hidden: '{!isWindows}',
- disabled: '{!isWindows}',
- },
- autoEl: {
- tag: 'div',
- 'data-qtip': gettext('Useful when wanting to use VirtIO-SCSI'),
- },
- },
- ],
+ // the first inputpanel handles all values, so prevent value leakage here
+ onGetValues: () => ({}),
- columnB: [
+ columnT: [
{
xtype: 'displayfield',
fieldLabel: gettext('Disks'),
},
],
},
+ ],
+
+ column1: [
+ {
+ xtype: 'proxmoxcheckbox',
+ fieldLabel: gettext('Prepare for VirtIO-SCSI'),
+ labelWidth: 200,
+ reference: 'mapSata',
+ isFormField: false,
+ disabled: true,
+ bind: {
+ disabled: '{!isWindows}',
+ },
+ autoEl: {
+ tag: 'div',
+ 'data-qtip': gettext('Maps SCSI disks to SATA and changes the SCSI Controller. Useful for a quicker switch to VirtIO-SCSI attached disks'),
+ },
+ },
+ ],
+
+ column2: [
+ {
+ xtype: 'pveScsiHwSelector',
+ reference: 'scsihw',
+ name: 'scsihw',
+ submitValue: false,
+ fieldLabel: gettext('SCSI Controller'),
+ },
+ ],
+
+ columnB: [
{
xtype: 'displayfield',
fieldLabel: gettext('CD/DVD Drives'),
labelWidth: 200,
- style: {
- paddingTop: '10px',
- },
},
{
xtype: 'grid',
sorters: [
'id',
],
+ filters: [
+ function(rec) {
+ return !rec.data.hidden;
+ },
+ ],
},
columns: [
{
}
cdroms.push({
enable: true,
+ hidden: false,
id,
});
delete me.vmConfig[id];
}
+
me.lookup('diskGrid').getStore().setData(disks);
me.lookup('netGrid').getStore().setData(nets);
me.lookup('cdGrid').getStore().setData(cdroms);
+ let additionalCdIdx = me.getController().calculateAdditionalCDIdx();
+ if (additionalCdIdx === '') {
+ me.getViewModel().set('maxCdDrives', true);
+ } else if (cdroms.length === 0) {
+ me.additionalCdIdx = additionalCdIdx;
+ me.lookup('cdGrid').getStore().add({
+ enable: true,
+ hidden: !(me.vmConfig.ostype ?? '').startsWith('w'),
+ id: additionalCdIdx,
+ autogenerated: true,
+ });
+ }
+
me.getViewModel().set('warnings', data.warnings.map(w => renderWarning(w)));
let osinfo = PVE.Utils.get_kvm_osinfo(me.vmConfig.ostype ?? '');