widget.setNodename(view.nodename);
},
- storageChange: function(storageSelector, value) {
+ diskStorageChange: function(storageSelector, value) {
let me = this;
let grid = me.lookup('diskGrid');
let rec = storageSelector.getWidgetRecord();
- let validFormats = storageSelector.store.getById(value).data.format;
+ let validFormats = storageSelector.store.getById(value)?.data.format;
grid.query('pveDiskFormatSelector').some((selector) => {
if (selector.getWidgetRecord().data.id !== rec.data.id) {
return false;
});
},
+ isoStorageChange: function(storageSelector, value) {
+ let me = this;
+
+ let grid = me.lookup('cdGrid');
+ let rec = storageSelector.getWidgetRecord();
+ grid.query('pveFileSelector').some((selector) => {
+ if (selector.getWidgetRecord().data.id !== rec.data.id) {
+ return false;
+ }
+
+ selector.setStorage(value);
+ if (!value) {
+ selector.setValue('');
+ }
+
+ return true;
+ });
+ },
+
onOSBaseChange: function(_field, value) {
let me = this;
let ostype = me.lookup('ostype');
}
},
+ 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 })));
+ },
+
control: {
'grid field': {
// update records from widgetcolumns
rec.commit();
},
},
- 'pveStorageSelector': {
- change: 'storageChange',
+ 'grid[reference=diskGrid] pveStorageSelector': {
+ change: 'diskStorageChange',
+ },
+ 'grid[reference=cdGrid] pveStorageSelector': {
+ change: 'isoStorageChange',
},
'field[name=osbase]': {
change: 'onOSBaseChange',
},
+ 'panel[reference=summaryTab]': {
+ activate: 'calculateConfig',
+ },
},
},
},
},
+ 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);
- });
-
- 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,
- },
+ items: [
{
- xtype: 'proxmoxintegerfield',
- fieldLabel: gettext('Sockets'),
- name: 'sockets',
- reference: 'socketsField',
- value: 1,
- minValue: 1,
- maxValue: 4,
- allowBlank: true,
- bind: {
- value: '{socketCount}',
+ 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,');
+ }
+
+ grid.lookup('diskGrid').getStore().each((rec) => {
+ if (!rec.data.enable) {
+ return;
+ }
+ let id = rec.data.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);
+ });
+
+ 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);
+ });
+
+ if (grid.lookup('liveimport').getValue()) {
+ config['live-restore'] = 1;
+ }
+
+ return config;
},
- },
- {
- xtype: 'proxmoxintegerfield',
- fieldLabel: gettext('Cores'),
- name: 'cores',
- reference: 'coresField',
- value: 1,
- minValue: 1,
- maxValue: 128,
- allowBlank: true,
- bind: {
- value: '{coreCount}',
- },
- },
- {
- 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: [] },
- 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,
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ fieldLabel: gettext('Sockets'),
+ name: 'sockets',
+ reference: 'socketsField',
+ value: 1,
+ minValue: 1,
+ maxValue: 4,
+ allowBlank: true,
+ bind: {
+ value: '{socketCount}',
+ },
+ },
+ {
+ xtype: 'proxmoxintegerfield',
+ fieldLabel: gettext('Cores'),
+ name: 'cores',
+ reference: 'coresField',
+ value: 1,
+ minValue: 1,
+ maxValue: 128,
+ allowBlank: true,
+ bind: {
+ value: '{coreCount}',
},
},
{
- text: gettext('Disk'),
- dataIndex: 'id',
+ xtype: 'pveMemoryField',
+ fieldLabel: gettext('Memory'),
+ name: 'memory',
+ reference: 'memoryField',
+ value: 512,
+ allowBlank: true,
+ },
+ {
+ //spacer
+ xtype: 'displayfield',
+ },
+ {
+ xtype: 'pveDiskStorageSelector',
+ reference: 'defaultStorage',
+ storageLabel: gettext('Default Storage'),
+ storageContent: 'images',
+ autoSelect: true,
+ hideSize: true,
+ name: 'defaultStorage',
+ },
+ ],
+
+ 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'),
},
{
- text: gettext('Source'),
- dataIndex: 'import-from',
- flex: 1,
- renderer: function(value) {
- return value.replace(/^.*\//, '');
+ xtype: 'displayfield',
+ fieldLabel: gettext('Total cores'),
+ name: 'totalcores',
+ isFormField: false,
+ bind: {
+ value: '{totalCoreCount}',
},
},
{
- text: gettext('Storage'),
- dataIndex: 'file',
- xtype: 'widgetcolumn',
- width: 150,
- widget: {
- xtype: 'pveStorageSelector',
- isFormField: false,
- name: 'file',
- storageContent: 'images',
+ 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,
},
- onWidgetAttach: 'setNodename',
},
{
- text: gettext('Format'),
- dataIndex: 'format',
- xtype: 'widgetcolumn',
- width: 150,
- widget: {
- xtype: 'pveDiskFormatSelector',
- name: 'format',
- isFormField: false,
- matchFieldWidth: false,
+ 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,
+ boxLabel: gettext('Experimental'),
+ },
+ {
+ 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}',
},
},
],
},
{
- xtype: 'displayfield',
- fieldLabel: gettext('Network Interfaces'),
- labelWidth: 200,
- },
- {
- xtype: 'grid',
- maxHeight: 150,
- reference: 'netGrid',
- store: { data: [] },
- columns: [
+ title: gettext('Advanced'),
+ xtype: 'inputpanel',
+ items: [
{
- 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: 58,
+ 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',
+ },
+ {
+ 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,
+ },
+ },
+ ],
},
{
- text: gettext('ID'),
- dataIndex: 'id',
+ xtype: 'displayfield',
+ fieldLabel: gettext('CD/DVD Drives'),
+ labelWidth: 200,
},
{
- text: gettext('MAC address'),
- flex: 1,
- dataIndex: 'macaddr',
+ xtype: 'grid',
+ reference: 'cdGrid',
+ minHeight: 58,
+ 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('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('Model'),
- flex: 1,
- dataIndex: 'model',
+ xtype: 'displayfield',
+ fieldLabel: gettext('Network Interfaces'),
+ labelWidth: 200,
},
{
- text: gettext('Bridge'),
- dataIndex: 'bridge',
- xtype: 'widgetcolumn',
- widget: {
- xtype: 'PVE.form.BridgeSelector',
- name: 'bridge',
- isFormField: false,
- allowBlank: false,
+ xtype: 'grid',
+ minHeight: 58,
+ maxHeight: 150,
+ reference: 'netGrid',
+ store: {
+ data: [],
+ sorters: [
+ 'id',
+ ],
},
- onWidgetAttach: 'setNodename',
+ 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' },
+ ],
+ },
+ ],
},
],
},
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"),
let disks = [];
for (const [id, value] of Object.entries(data.disks ?? {})) {
+ let volid = Ext.htmlEncode('<none>');
+ let size = 'auto';
+ if (Ext.isObject(value)) {
+ volid = value.volid;
+ size = value.size;
+ }
disks.push({
id,
enable: true,
- 'import-from': id === 'efidisk0' ? Ext.htmlEncode('<none>') : value,
+ size,
+ 'import-from': volid,
format: 'raw',
});
}
parsed.enable = true;
nets.push(parsed);
}
+
+ let cdroms = [];
+ for (const [id, value] of Object.entries(me.vmConfig)) {
+ if (!Ext.isString(value) || !value.match(/media=cdrom/)) {
+ continue;
+ }
+ cdroms.push({
+ enable: true,
+ id,
+ });
+ delete me.vmConfig[id];
+ }
me.lookup('diskGrid').getStore().setData(disks);
me.lookup('netGrid').getStore().setData(nets);
+ me.lookup('cdGrid').getStore().setData(cdroms);
me.getViewModel().set('warnings', data.warnings.map(w => renderWarning(w)));