X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=www%2Fmanager6%2Fwindow%2FGuestImport.js;h=f4473c3068442f4cf58b7d03c447cef94980c65e;hb=b4f879fdfd71beca5aa074ca3c9ba469c083e93e;hp=872738e63bdcccf2869fafaec3a2cf37b84fb8ee;hpb=a5e56799e7e1ac26f2332bbfb703d70cf79d70d5;p=pve-manager.git diff --git a/www/manager6/window/GuestImport.js b/www/manager6/window/GuestImport.js index 872738e6..f4473c30 100644 --- a/www/manager6/window/GuestImport.js +++ b/www/manager6/window/GuestImport.js @@ -92,6 +92,79 @@ Ext.define('PVE.window.GuestImport', { summaryGrid.getStore().setData(Object.entries(values).map(([key, value]) => ({ key, value }))); }, + // assume assigned sata disks indices are continuous, so without holes + getMaxSata: function() { + let me = this; + let view = me.getView(); + if (view.maxSata !== undefined) { + return view.maxSata; + } + + view.maxSata = -1; + for (const key of Object.keys(me.getView().vmConfig)) { + if (!key.toLowerCase().startsWith('sata')) { + continue; + } + let idx = parseInt(key.slice(4), 10); + if (idx > view.maxSata) { + view.maxSata = idx; + } + } + me.lookup('diskGrid').getStore().each(rec => { + if (!rec.data.id.toLowerCase().startsWith('sata')) { + return; + } + let idx = parseInt(rec.data.id.slice(4), 10); + if (idx > view.maxSata) { + view.maxSata = idx; + } + }); + me.lookup('cdGrid').getStore().each(rec => { + if (!rec.data.id.toLowerCase().startsWith('sata')) { + return; + } + let idx = parseInt(rec.data.id.slice(4), 10); + if (idx > view.maxSata) { + view.maxSata = idx; + } + }); + + return view.maxSata; + }, + + mapDisk: function(value, metaData) { + let me = this; + let mapSata = me.lookup('mapSata'); + if (mapSata.isDisabled() || !mapSata.getValue()) { + return value; + } + if (!value.toLowerCase().startsWith('scsi')) { + return value; + } + let offset = parseInt(value.slice(4), 10); + let newIdx = offset + me.getMaxSata() + 1; + if (newIdx > PVE.Utils.diskControllerMaxIDs.sata) { + let prefix = ''; + if (metaData !== undefined) { + // we're in the renderer so put a warning here + let warning = gettext('Too many disks, could not map to SATA.'); + prefix = ` `; + } + return `${prefix}${value}`; + } + return `sata${newIdx}`; + }, + + refreshDiskGrid: function() { + this.lookup('diskGrid').reconfigure(); + }, + + toggleIsoSelector: function(_cb, value) { + let me = this; + me.lookup('isoSelector').setDisabled(!value); + me.lookup('isoSelector').setHidden(!value); + }, + control: { 'grid field': { // update records from widgetcolumns @@ -113,6 +186,15 @@ Ext.define('PVE.window.GuestImport', { 'panel[reference=summaryTab]': { activate: 'calculateConfig', }, + 'proxmoxcheckbox[reference=mapSata]': { + change: 'refreshDiskGrid', + }, + 'combobox[name=ostype]': { + change: 'refreshDiskGrid', + }, + 'proxmoxcheckbox[reference=enableSecondCD]': { + change: 'toggleIsoSelector', + }, }, }, @@ -120,6 +202,8 @@ Ext.define('PVE.window.GuestImport', { data: { coreCount: 1, socketCount: 1, + liveImport: false, + os: '', warnings: [], }, @@ -128,6 +212,9 @@ Ext.define('PVE.window.GuestImport', { hideWarnings: get => get('warnings').length === 0, warningsText: get => '', + 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'), }, }, @@ -164,11 +251,22 @@ Ext.define('PVE.window.GuestImport', { config.scsi0 = config.scsi0.replace('local:0,', 'local:0,format=qcow2,'); } + let parsedBoot = PVE.Parser.parsePropertyString(config.boot ?? ''); + if (parsedBoot.order) { + parsedBoot.order = parsedBoot.order.split(';'); + } + grid.lookup('diskGrid').getStore().each((rec) => { if (!rec.data.enable) { return; } - let id = rec.data.id; + let id = grid.getController().mapDisk(rec.data.id); + if (id !== rec.data.id && parsedBoot?.order) { + let idx = parsedBoot.order.indexOf(rec.data.id); + if (idx !== -1) { + parsedBoot.order[idx] = id; + } + } let data = { ...rec.data, }; @@ -186,6 +284,11 @@ Ext.define('PVE.window.GuestImport', { config[id] = PVE.Parser.printQemuDrive(data); }); + if (parsedBoot.order) { + parsedBoot.order = parsedBoot.order.join(';'); + } + config.boot = PVE.Parser.printPropertyString(parsedBoot); + grid.lookup('netGrid').getStore().each((rec) => { if (!rec.data.enable) { return; @@ -214,10 +317,40 @@ Ext.define('PVE.window.GuestImport', { config[id] = PVE.Parser.printPropertyString(cd); }); + config.scsihw = grid.lookup('scsihw').getValue(); + if (grid.lookup('liveimport').getValue()) { config['live-restore'] = 1; } + if (grid.lookup('enableSecondCD')) { + let idsToTry = ['ide0', 'ide2']; + for (let i = 0; i <=PVE.Utils.diskControllerMaxIDs.sata; i++) { + idsToTry.push(`sata{$i}`); + } + let found = false; + for (const id of idsToTry) { + if (!config[id]) { + config[id] = PVE.Parser.printQemuDrive({ + media: 'cdrom', + file: grid.lookup('isoSelector').getValue(), + }); + found = true; + break; + } + } + if (!found) { + console.warn('could not insert cd drive for virtio'); + } + } + + // remove __default__ values + for (const [key, value] of Object.entries(config)) { + if (value === '__default__') { + delete config[key]; + } + } + return config; }, @@ -324,6 +457,9 @@ Ext.define('PVE.window.GuestImport', { queryMode: 'local', valueField: 'val', displayField: 'desc', + bind: { + value: '{os}', + }, store: { fields: ['desc', 'val'], data: PVE.Utils.kvm_ostypes.Linux, @@ -344,7 +480,11 @@ Ext.define('PVE.window.GuestImport', { fieldLabel: gettext('Live Import'), reference: 'liveimport', isFormField: false, - boxLabel: gettext('Experimental'), + boxLabelCls: 'pmx-hint black x-form-cb-label', + bind: { + value: '{liveImport}', + boxLabel: '{liveImportNote}', + }, }, { xtype: 'displayfield', @@ -370,7 +510,63 @@ Ext.define('PVE.window.GuestImport', { { title: gettext('Advanced'), xtype: 'inputpanel', - items: [ + + // the first inputpanel handles the values, prevent + // accidental values from this inputpanel here + onGetValues: () => ({}), + column1: [ + { + xtype: 'pveScsiHwSelector', + reference: 'scsihw', + name: 'scsihw', + submitValue: false, + fieldLabel: gettext('SCSI Controller'), + }, + { + xtype: 'proxmoxcheckbox', + reference: 'enableSecondCD', + isFormField: false, + hidden: true, + checked: false, + boxLabel: gettext('Add additional drive for VirtIO drivers'), + bind: { + hidden: '{!isWindows}', + disabled: '{!isWindows}', + }, + }, + ], + + 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'), + }, + }, + { + xtype: 'pveIsoSelector', + reference: 'isoSelector', + submitValue: false, + labelWidth: 120, + labelAlign: 'left', + insideWizard: true, + hidden: true, + disabled: true, + }, + ], + + columnB: [ { xtype: 'displayfield', fieldLabel: gettext('Disks'), @@ -379,7 +575,7 @@ Ext.define('PVE.window.GuestImport', { { xtype: 'grid', reference: 'diskGrid', - minHeight: 58, + minHeight: 60, maxHeight: 150, store: { data: [], @@ -402,6 +598,7 @@ Ext.define('PVE.window.GuestImport', { { text: gettext('Disk'), dataIndex: 'id', + renderer: 'mapDisk', }, { text: gettext('Source'), @@ -456,11 +653,14 @@ Ext.define('PVE.window.GuestImport', { xtype: 'displayfield', fieldLabel: gettext('CD/DVD Drives'), labelWidth: 200, + style: { + paddingTop: '10px', + }, }, { xtype: 'grid', reference: 'cdGrid', - minHeight: 58, + minHeight: 60, maxHeight: 150, store: { data: [], @@ -520,6 +720,9 @@ Ext.define('PVE.window.GuestImport', { xtype: 'displayfield', fieldLabel: gettext('Network Interfaces'), labelWidth: 200, + style: { + paddingTop: '10px', + }, }, { xtype: 'grid', @@ -633,13 +836,15 @@ Ext.define('PVE.window.GuestImport', { me.lookup('defaultStorage').setNodename(me.nodename); me.lookup('defaultBridge').setNodename(me.nodename); + me.lookup('isoSelector').setNodename(me.nodename); let renderWarning = w => { const warningsCatalogue = { - 'cdrom-image-ignored': gettext("CD-ROM images cannot get imported, please reconfigure the '{0}' drive after the import"), + 'cdrom-image-ignored': gettext("CD-ROM images cannot get imported, if required you can reconfigure the '{0}' drive in the 'Advanced' tab."), 'nvme-unsupported': gettext("NVMe disks are currently not supported, '{0}' will get attaced as SCSI"), 'ovmf-with-lsi-unsupported': gettext("OVMF is built without LSI drivers, scsi hardware was set to '{1}'"), 'serial-port-socket-only': gettext("Serial socket '{0}' will be mapped to a socket"), + 'guest-is-running': gettext('Virtual guest seems to be running on source host. Import might fail or have inconsistent state!'), }; let message = warningsCatalogue[w.type]; if (!w.type || !message) {