X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=www%2Fmanager6%2Fwindow%2FGuestImport.js;h=b846f00eb09cfad4b536aa9bbe8f9f23ef3dd8e8;hb=2d0bf566efd23e263fc94bd36df376bbe7d5671d;hp=f899b164e5481513e36c072aee4d4d426151ebe0;hpb=27afd69862756d587f87a35f5ffd4a8ebabacd05;p=pve-manager.git diff --git a/www/manager6/window/GuestImport.js b/www/manager6/window/GuestImport.js index f899b164..b846f00e 100644 --- a/www/manager6/window/GuestImport.js +++ b/www/manager6/window/GuestImport.js @@ -84,6 +84,147 @@ Ext.define('PVE.window.GuestImport', { } }, + calculateConfig: function() { + let me = this; + let inputPanel = me.lookup('mainInputPanel'); + let summaryGrid = me.lookup('summaryGrid'); + let values = inputPanel.getValues(); + 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 + getMaxControllerId: function(controller) { + let me = this; + let view = me.getView(); + if (!controller) { + return -1; + } + + let max = view[`max${controller}`]; + if (max !== undefined) { + return max; + } + + max = -1; + for (const key of Object.keys(me.getView().vmConfig)) { + if (!key.toLowerCase().startsWith(controller)) { + continue; + } + 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(controller)) { + return; + } + 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(controller) || rec.data.hidden) { + return; + } + let idx = parseInt(rec.data.id.slice(controller.length), 10); + if (idx > max) { + max = idx; + } + }); + + view[`max${controller}`] = max; + return max; + }, + + 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.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 + let warning = gettext('Too many disks, could not map to SATA.'); + prefix = ` `; + } + return `${prefix}${value}`; + } + return `sata${newIdx}`; + }, + + 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('win'); + 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: { 'grid field': { // update records from widgetcolumns @@ -102,6 +243,18 @@ Ext.define('PVE.window.GuestImport', { 'field[name=osbase]': { change: 'onOSBaseChange', }, + 'panel[reference=summaryTab]': { + activate: 'calculateConfig', + }, + 'proxmoxcheckbox[reference=mapSata]': { + change: 'onPrepareVirtioChange', + }, + 'combobox[name=ostype]': { + change: 'onOSTypeChange', + }, + 'pveScsiHwSelector': { + change: 'onScsiHwChange', + }, }, }, @@ -109,6 +262,9 @@ Ext.define('PVE.window.GuestImport', { data: { coreCount: 1, socketCount: 1, + liveImport: false, + os: '', + maxCdDrives: false, warnings: [], }, @@ -117,367 +273,567 @@ 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'), }, }, + width: 700, + bodyPadding: 0, + items: [ { - xtype: 'inputpanel', - onGetValues: function(values) { - let me = this; - let grid = me.up('pveGuestImportWindow'); - - let config = Ext.apply(grid.vmConfig, values); - - if (config.scsi0) { - config.scsi0 = config.scsi0.replace('local:0,', 'local:0,format=qcow2,'); - } - - grid.lookup('diskGrid').getStore().each((rec) => { - if (!rec.data.enable) { - return; - } - let id = rec.data.id; - delete rec.data.enable; - delete rec.data.id; - rec.data.file += ':0'; // for our special api format - if (id === 'efidisk0') { - delete rec.data['import-from']; - } - config[id] = PVE.Parser.printQemuDrive(rec.data); - }); - - grid.lookup('netGrid').getStore().each((rec) => { - if (!rec.data.enable) { - return; - } - let id = rec.data.id; - delete rec.data.enable; - delete rec.data.id; - config[id] = PVE.Parser.printQemuNetwork(rec.data); - }); - - grid.lookup('cdGrid').getStore().each((rec) => { - if (!rec.data.enable) { - return; - } - let id = rec.data.id; - delete rec.data.enable; - delete rec.data.id; - let cd = { - media: 'cdrom', - file: rec.data.file ? rec.data.file : 'none', - }; - config[id] = PVE.Parser.printPropertyString(cd); - }); - - if (grid.lookup('liveimport').getValue()) { - config['live-restore'] = 1; - } - - return config; + xtype: 'tabpanel', + defaults: { + bodyPadding: 10, }, - - column1: [ - { - xtype: 'pveGuestIDSelector', - name: 'vmid', - fieldLabel: 'VM', - guestType: 'qemu', - loadNextFreeID: true, - }, - { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Sockets'), - name: 'sockets', - reference: 'socketsField', - value: 1, - minValue: 1, - maxValue: 4, - allowBlank: true, - bind: { - value: '{socketCount}', - }, - }, + items: [ { - xtype: 'proxmoxintegerfield', - fieldLabel: gettext('Cores'), - name: 'cores', - reference: 'coresField', - value: 1, - minValue: 1, - maxValue: 128, - allowBlank: true, - bind: { - value: '{coreCount}', + title: gettext('General'), + xtype: 'inputpanel', + reference: 'mainInputPanel', + onGetValues: function(values) { + let me = this; + let grid = me.up('pveGuestImportWindow'); + + // from pveDiskStorageSelector + let defaultStorage = values.hdstorage; + let defaultFormat = values.diskformat; + delete values.hdstorage; + delete values.diskformat; + + let defaultBridge = values.defaultBridge; + delete values.defaultBridge; + + let config = Ext.apply(grid.vmConfig, values); + + if (config.scsi0) { + 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 = 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, + }; + delete data.enable; + delete data.id; + delete data.size; + if (!data.file) { + data.file = defaultStorage; + data.format = defaultFormat; + } + data.file += ':0'; // for our special api format + if (id === 'efidisk0') { + delete data['import-from']; + } + 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; + } + let id = rec.data.id; + let data = { + ...rec.data, + }; + delete data.enable; + delete data.id; + if (!data.bridge) { + data.bridge = defaultBridge; + } + config[id] = PVE.Parser.printQemuNetwork(data); + }); + + grid.lookup('cdGrid').getStore().each((rec) => { + if (!rec.data.enable) { + return; + } + let id = rec.data.id; + let cd = { + media: 'cdrom', + file: rec.data.file ? rec.data.file : 'none', + }; + config[id] = PVE.Parser.printPropertyString(cd); + }); + + config.scsihw = grid.lookup('scsihw').getValue(); + + if (grid.lookup('liveimport').getValue()) { + config['live-restore'] = 1; + } + + // remove __default__ values + for (const [key, value] of Object.entries(config)) { + if (value === '__default__') { + delete config[key]; + } + } + + return config; }, - }, - { - xtype: 'pveMemoryField', - fieldLabel: gettext('Memory'), - name: 'memory', - reference: 'memoryField', - value: 512, - allowBlank: true, - }, - ], - column2: [ - { - xtype: 'textfield', - fieldLabel: gettext('Name'), - name: 'name', - vtype: 'DnsName', - reference: 'nameField', - allowBlank: true, - }, - { - xtype: 'CPUModelSelector', - name: 'cpu', - reference: 'cputype', - value: 'x86-64-v2-AES', - fieldLabel: gettext('Type'), - }, - { - xtype: 'displayfield', - fieldLabel: gettext('Total cores'), - name: 'totalcores', - isFormField: false, - bind: { - value: '{totalCoreCount}', - }, - }, - { - xtype: 'combobox', - submitValue: false, - name: 'osbase', - fieldLabel: gettext('OS Type'), - editable: false, - queryMode: 'local', - value: 'Linux', - store: Object.keys(PVE.Utils.kvm_ostypes), - }, - { - xtype: 'combobox', - name: 'ostype', - reference: 'ostype', - fieldLabel: gettext('Version'), - value: 'l26', - allowBlank: false, - editable: false, - queryMode: 'local', - valueField: 'val', - displayField: 'desc', - store: { - fields: ['desc', 'val'], - data: PVE.Utils.kvm_ostypes.Linux, - }, - }, - ], - columnB: [ - { - xtype: 'displayfield', - fieldLabel: gettext('Disks'), - labelWidth: 200, - }, - { - xtype: 'grid', - reference: 'diskGrid', - maxHeight: 150, - store: { - data: [], - sorters: [ - 'id', - ], - }, - columns: [ + column1: [ { - xtype: 'checkcolumn', - header: gettext('Use'), - width: 50, - dataIndex: 'enable', - listeners: { - checkchange: function(_column, _rowIndex, _checked, record) { - record.commit(); - }, - }, + xtype: 'pveGuestIDSelector', + name: 'vmid', + fieldLabel: 'VM', + guestType: 'qemu', + loadNextFreeID: true, }, { - text: gettext('Disk'), - dataIndex: 'id', + xtype: 'proxmoxintegerfield', + fieldLabel: gettext('Sockets'), + name: 'sockets', + reference: 'socketsField', + value: 1, + minValue: 1, + maxValue: 4, + allowBlank: true, + bind: { + value: '{socketCount}', + }, }, { - text: gettext('Source'), - dataIndex: 'import-from', - flex: 1, - renderer: function(value) { - return value.replace(/^.*\//, ''); + xtype: 'proxmoxintegerfield', + fieldLabel: gettext('Cores'), + name: 'cores', + reference: 'coresField', + value: 1, + minValue: 1, + maxValue: 128, + allowBlank: true, + bind: { + value: '{coreCount}', }, }, { - text: gettext('Storage'), - dataIndex: 'file', - xtype: 'widgetcolumn', - width: 150, - widget: { - xtype: 'pveStorageSelector', - isFormField: false, - name: 'file', - storageContent: 'images', - }, - onWidgetAttach: 'setNodename', + xtype: 'pveMemoryField', + fieldLabel: gettext('Memory'), + name: 'memory', + reference: 'memoryField', + value: 512, + allowBlank: true, }, { - text: gettext('Format'), - dataIndex: 'format', - xtype: 'widgetcolumn', - width: 150, - widget: { - xtype: 'pveDiskFormatSelector', - name: 'format', - isFormField: false, - matchFieldWidth: false, - }, + //spacer + xtype: 'displayfield', + }, + { + xtype: 'pveDiskStorageSelector', + reference: 'defaultStorage', + storageLabel: gettext('Default Storage'), + storageContent: 'images', + autoSelect: true, + hideSize: true, + name: 'defaultStorage', }, ], - }, - { - xtype: 'displayfield', - fieldLabel: gettext('CD/DVD Drives'), - labelWidth: 200, - }, - { - xtype: 'grid', - reference: 'cdGrid', - maxHeight: 150, - store: { - data: [], - sorters: [ - 'id', - ], - }, - columns: [ + + column2: [ { - xtype: 'checkcolumn', - header: gettext('Use'), - width: 50, - dataIndex: 'enable', - listeners: { - checkchange: function(_column, _rowIndex, _checked, record) { - record.commit(); - }, + xtype: 'textfield', + fieldLabel: gettext('Name'), + name: 'name', + vtype: 'DnsName', + reference: 'nameField', + allowBlank: true, + }, + { + xtype: 'CPUModelSelector', + name: 'cpu', + reference: 'cputype', + value: 'x86-64-v2-AES', + fieldLabel: gettext('Type'), + }, + { + xtype: 'displayfield', + fieldLabel: gettext('Total cores'), + name: 'totalcores', + isFormField: false, + bind: { + value: '{totalCoreCount}', }, }, { - text: gettext('Slot'), - dataIndex: 'id', - sorted: true, + xtype: 'combobox', + submitValue: false, + name: 'osbase', + fieldLabel: gettext('OS Type'), + editable: false, + queryMode: 'local', + value: 'Linux', + store: Object.keys(PVE.Utils.kvm_ostypes), + }, + { + xtype: 'combobox', + name: 'ostype', + reference: 'ostype', + fieldLabel: gettext('Version'), + value: 'l26', + allowBlank: false, + editable: false, + queryMode: 'local', + valueField: 'val', + displayField: 'desc', + bind: { + value: '{os}', + }, + store: { + fields: ['desc', 'val'], + data: PVE.Utils.kvm_ostypes.Linux, + }, + }, + { + xtype: 'PVE.form.BridgeSelector', + reference: 'defaultBridge', + name: 'defaultBridge', + allowBlank: false, + fieldLabel: gettext('Default Bridge'), + }, + ], + + columnB: [ + { + xtype: 'proxmoxcheckbox', + fieldLabel: gettext('Live Import'), + reference: 'liveimport', + isFormField: false, + boxLabelCls: 'pmx-hint black x-form-cb-label', + bind: { + value: '{liveImport}', + boxLabel: '{liveImportNote}', + }, }, { - text: gettext('Storage'), - xtype: 'widgetcolumn', - width: 150, - widget: { - xtype: 'pveStorageSelector', - isFormField: false, - autoSelect: false, - allowBlank: true, - emptyText: Proxmox.Utils.noneText, - storageContent: 'iso', + xtype: 'displayfield', + fieldLabel: gettext('Warnings'), + labelWidth: 200, + hidden: true, + bind: { + hidden: '{hideWarnings}', }, - onWidgetAttach: 'setNodename', }, { - text: gettext('ISO'), - dataIndex: 'file', - xtype: 'widgetcolumn', - flex: 1, - widget: { - xtype: 'pveFileSelector', - name: 'file', - isFormField: false, - allowBlank: true, - emptyText: Proxmox.Utils.noneText, - storageContent: 'iso', + xtype: 'displayfield', + reference: 'warningText', + userCls: 'pmx-hint', + hidden: true, + bind: { + hidden: '{hideWarnings}', + value: '{warningsText}', }, - onWidgetAttach: 'setNodename', }, ], }, { - xtype: 'displayfield', - fieldLabel: gettext('Network Interfaces'), - labelWidth: 200, - }, - { - xtype: 'grid', - maxHeight: 150, - reference: 'netGrid', - store: { - data: [], - sorters: [ - 'id', - ], - }, - columns: [ + title: gettext('Advanced'), + xtype: 'inputpanel', + + // the first inputpanel handles all values, so prevent value leakage here + onGetValues: () => ({}), + + columnT: [ { - xtype: 'checkcolumn', - header: gettext('Use'), - width: 50, - dataIndex: 'enable', - listeners: { - checkchange: function(_column, _rowIndex, _checked, record) { - record.commit(); + xtype: 'displayfield', + fieldLabel: gettext('Disks'), + labelWidth: 200, + }, + { + xtype: 'grid', + reference: 'diskGrid', + minHeight: 60, + maxHeight: 150, + store: { + data: [], + sorters: [ + 'id', + ], + }, + columns: [ + { + xtype: 'checkcolumn', + header: gettext('Use'), + width: 50, + dataIndex: 'enable', + listeners: { + checkchange: function(_column, _rowIndex, _checked, record) { + record.commit(); + }, + }, }, + { + text: gettext('Disk'), + dataIndex: 'id', + renderer: 'mapDisk', + }, + { + text: gettext('Source'), + dataIndex: 'import-from', + flex: 1, + renderer: function(value) { + return value.replace(/^.*\//, ''); + }, + }, + { + text: gettext('Size'), + dataIndex: 'size', + renderer: (value) => { + if (Ext.isNumeric(value)) { + return Proxmox.Utils.render_size(value); + } + return value ?? Proxmox.Utils.unknownText; + }, + }, + { + text: gettext('Storage'), + dataIndex: 'file', + xtype: 'widgetcolumn', + width: 150, + widget: { + xtype: 'pveStorageSelector', + isFormField: false, + autoSelect: false, + allowBlank: true, + emptyText: gettext('From Default'), + name: 'file', + storageContent: 'images', + }, + onWidgetAttach: 'setNodename', + }, + { + text: gettext('Format'), + dataIndex: 'format', + xtype: 'widgetcolumn', + width: 150, + widget: { + xtype: 'pveDiskFormatSelector', + name: 'format', + disabled: true, + isFormField: false, + matchFieldWidth: false, + }, + }, + ], + }, + ], + + 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: [ { - text: gettext('ID'), - dataIndex: 'id', + xtype: 'pveScsiHwSelector', + reference: 'scsihw', + name: 'scsihw', + submitValue: false, + fieldLabel: gettext('SCSI Controller'), }, + ], + + columnB: [ { - text: gettext('MAC address'), - flex: 1, - dataIndex: 'macaddr', - renderer: value => value ?? 'auto', + xtype: 'displayfield', + fieldLabel: gettext('CD/DVD Drives'), + labelWidth: 200, + style: { + paddingTop: '10px', + }, }, { - text: gettext('Model'), - flex: 1, - dataIndex: 'model', + xtype: 'grid', + reference: 'cdGrid', + minHeight: 60, + maxHeight: 150, + store: { + data: [], + sorters: [ + 'id', + ], + filters: [ + function(rec) { + return !rec.data.hidden; + }, + ], + }, + columns: [ + { + xtype: 'checkcolumn', + header: gettext('Use'), + width: 50, + dataIndex: 'enable', + listeners: { + checkchange: function(_column, _rowIndex, _checked, record) { + record.commit(); + }, + }, + }, + { + text: gettext('Slot'), + dataIndex: 'id', + sorted: true, + }, + { + text: gettext('Storage'), + xtype: 'widgetcolumn', + width: 150, + widget: { + xtype: 'pveStorageSelector', + isFormField: false, + autoSelect: false, + allowBlank: true, + emptyText: Proxmox.Utils.noneText, + storageContent: 'iso', + }, + onWidgetAttach: 'setNodename', + }, + { + text: gettext('ISO'), + dataIndex: 'file', + xtype: 'widgetcolumn', + flex: 1, + widget: { + xtype: 'pveFileSelector', + name: 'file', + isFormField: false, + allowBlank: true, + emptyText: Proxmox.Utils.noneText, + storageContent: 'iso', + }, + onWidgetAttach: 'setNodename', + }, + ], }, { - text: gettext('Bridge'), - dataIndex: 'bridge', - xtype: 'widgetcolumn', - widget: { - xtype: 'PVE.form.BridgeSelector', - name: 'bridge', - isFormField: false, - allowBlank: false, + xtype: 'displayfield', + fieldLabel: gettext('Network Interfaces'), + labelWidth: 200, + style: { + paddingTop: '10px', }, - onWidgetAttach: 'setNodename', + }, + { + xtype: 'grid', + minHeight: 58, + maxHeight: 150, + reference: 'netGrid', + store: { + data: [], + sorters: [ + 'id', + ], + }, + columns: [ + { + xtype: 'checkcolumn', + header: gettext('Use'), + width: 50, + dataIndex: 'enable', + listeners: { + checkchange: function(_column, _rowIndex, _checked, record) { + record.commit(); + }, + }, + }, + { + text: gettext('ID'), + dataIndex: 'id', + }, + { + text: gettext('MAC address'), + flex: 1, + dataIndex: 'macaddr', + renderer: value => value ?? 'auto', + }, + { + text: gettext('Model'), + flex: 1, + dataIndex: 'model', + xtype: 'widgetcolumn', + widget: { + xtype: 'pveNetworkCardSelector', + name: 'model', + isFormField: false, + allowBlank: false, + }, + }, + { + text: gettext('Bridge'), + dataIndex: 'bridge', + xtype: 'widgetcolumn', + flex: 1, + widget: { + xtype: 'PVE.form.BridgeSelector', + name: 'bridge', + isFormField: false, + autoSelect: false, + allowBlank: true, + emptyText: gettext('From Default'), + }, + onWidgetAttach: 'setNodename', + }, + ], }, ], }, { - xtype: 'displayfield', - fieldLabel: gettext('Warnings'), - labelWidth: 200, - hidden: true, - bind: { - hidden: '{hideWarnings}', - }, - }, - { - xtype: 'displayfield', - reference: 'warningText', - userCls: 'pmx-hint', - hidden: true, - bind: { - hidden: '{hideWarnings}', - value: '{warningsText}', - }, + title: gettext('Resulting Config'), + reference: 'summaryTab', + items: [ + { + xtype: 'grid', + reference: 'summaryGrid', + maxHeight: 400, + scrollable: true, + store: { + model: 'KeyValue', + sorters: [{ + property: 'key', + direction: 'ASC', + }], + }, + columns: [ + { header: 'Key', width: 150, dataIndex: 'key' }, + { header: 'Value', flex: 1, dataIndex: 'value' }, + ], + }, + ], }, ], }, @@ -500,21 +856,18 @@ Ext.define('PVE.window.GuestImport', { me.callParent(); - me.query('toolbar')?.[0]?.insert(0, { - xtype: 'proxmoxcheckbox', - reference: 'liveimport', - boxLabelAlign: 'before', - boxLabel: gettext('Live Import'), - }); - me.setTitle(Ext.String.format(gettext('Import Guest - {0}'), `${me.storage}:${me.volumeName}`)); + me.lookup('defaultStorage').setNodename(me.nodename); + me.lookup('defaultBridge').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) { @@ -530,10 +883,17 @@ Ext.define('PVE.window.GuestImport', { let disks = []; for (const [id, value] of Object.entries(data.disks ?? {})) { + let volid = Ext.htmlEncode(''); + let size = 'auto'; + if (Ext.isObject(value)) { + volid = value.volid; + size = value.size; + } disks.push({ id, enable: true, - 'import-from': id === 'efidisk0' ? Ext.htmlEncode('') : value, + size, + 'import-from': volid, format: 'raw', }); } @@ -552,14 +912,29 @@ Ext.define('PVE.window.GuestImport', { } 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('win'), + id: additionalCdIdx, + autogenerated: true, + }); + } + me.getViewModel().set('warnings', data.warnings.map(w => renderWarning(w))); let osinfo = PVE.Utils.get_kvm_osinfo(me.vmConfig.ostype ?? '');