Make importing single disks easier. Required to import a whole VM via GUI. Signed-off-by: Dominic Jäger <d.jae...@proxmox.com> --- www/manager6/qemu/HDEdit.js | 134 ++++++++++++++++++++++++++---- www/manager6/qemu/HardwareView.js | 24 ++++++ 2 files changed, 141 insertions(+), 17 deletions(-)
diff --git a/www/manager6/qemu/HDEdit.js b/www/manager6/qemu/HDEdit.js index e2a5b914..5e0a3981 100644 --- a/www/manager6/qemu/HDEdit.js +++ b/www/manager6/qemu/HDEdit.js @@ -67,7 +67,8 @@ Ext.define('PVE.qemu.HDInputPanel', { if (me.unused) { me.drive.file = me.vmconfig[values.unusedId]; confid = values.controller + values.deviceid; - } else if (me.isCreate) { + } else if (me.isCreate && !me.isImport) { + // disk format & size should not be part of propertyString for import if (values.hdimage) { me.drive.file = values.hdimage; } else { @@ -83,16 +84,22 @@ Ext.define('PVE.qemu.HDInputPanel', { PVE.Utils.propertyStringSet(me.drive, values.iothread, 'iothread', 'on'); PVE.Utils.propertyStringSet(me.drive, values.cache, 'cache'); - var names = ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr']; - Ext.Array.each(names, function(name) { - var burst_name = name + '_max'; + var names = ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr']; + Ext.Array.each(names, function(name) { + var 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); - + }); + if (me.isImport) { + params.device_options = PVE.Parser.printPropertyString(me.drive); + params.source = values.sourceType === 'storage' + ? values.sourceVolid : values.sourcePath; + params.device = values.controller + values.deviceid; + params.storage = values.hdstorage; + if (values.diskformat) params.format = values.diskformat; + } else { + params[confid] = PVE.Parser.printQemuDrive(me.drive); + } return params; }, @@ -169,10 +176,14 @@ Ext.define('PVE.qemu.HDInputPanel', { me.advancedColumn2 = []; if (!me.confid || me.unused) { + let controllerColumn = me.isImport ? me.column2 : me.column1; me.bussel = Ext.create('PVE.form.ControllerSelector', { vmconfig: me.insideWizard ? {ide2: 'cdrom'} : {} }); - me.column1.push(me.bussel); + if (me.isImport) { + me.bussel.fieldLabel = 'Target Device'; + } + controllerColumn.push(me.bussel); me.scsiController = Ext.create('Ext.form.field.Display', { fieldLabel: gettext('SCSI Controller'), @@ -184,7 +195,7 @@ Ext.define('PVE.qemu.HDInputPanel', { submitValue: false, hidden: true }); - me.column1.push(me.scsiController); + controllerColumn.push(me.scsiController); } if (me.unused) { @@ -199,14 +210,21 @@ Ext.define('PVE.qemu.HDInputPanel', { allowBlank: false }); me.column1.push(me.unusedDisks); - } else if (me.isCreate) { - me.column1.push({ + } else if (me.isCreate || me.isImport) { + let selector = { xtype: 'pveDiskStorageSelector', storageContent: 'images', name: 'disk', nodename: me.nodename, - autoSelect: me.insideWizard - }); + hideSize: me.isImport, + autoSelect: me.insideWizard || me.isImport, + }; + if (me.isImport) { + selector.storageLabel = gettext('Target storage'); + me.column2.push(selector); + } else { + me.column1.push(selector); + } } else { me.column1.push({ xtype: 'textfield', @@ -217,6 +235,12 @@ Ext.define('PVE.qemu.HDInputPanel', { }); } + if (me.isImport) { + me.column2.push({ + xtype: 'box', + autoEl: { tag: 'hr' }, + }); + } me.column2.push( { xtype: 'CacheTypeSelector', @@ -231,6 +255,74 @@ Ext.define('PVE.qemu.HDInputPanel', { name: 'discard' } ); + if (me.isImport) { + let show = (element, value) => { + element.setHidden(!value); + element.setDisabled(!value); + }; + me.sourceRadioStorage = Ext.create('Ext.form.field.Radio', { + name: 'sourceType', + inputValue: 'storage', + boxLabel: gettext('Use a storage as source'), + checked: true, + hidden: Proxmox.UserName !== 'root@pam', + listeners: { + added: () => show(me.sourcePathTextfield, false), + change: (_, storageRadioChecked) => { + show(me.sourcePathTextfield, !storageRadioChecked); + let selectors = [ + me.sourceStorageSelector, + me.sourceFileSelector, + ]; + for (const selector of selectors) { + show(selector, storageRadioChecked); + } + }, + }, + }); + me.sourceStorageSelector = Ext.create('PVE.form.StorageSelector', { + name: 'inputImageStorage', + nodename: me.nodename, + fieldLabel: gettext('Source Storage'), + storageContent: 'images', + autoSelect: me.insideWizard, + listeners: { + change: function(_, selectedStorage) { + me.sourceFileSelector.setStorage(selectedStorage); + }, + }, + }); + me.sourceFileSelector = Ext.create('PVE.form.FileSelector', { + name: 'sourceVolid', + nodename: me.nodename, + storageContent: 'images', + fieldLabel: gettext('Source Image'), + }); + me.sourceRadioPath = Ext.create('Ext.form.field.Radio', { + name: 'sourceType', + inputValue: 'path', + boxLabel: gettext('Use an absolute path as source'), + hidden: Proxmox.UserName !== 'root@pam', + }); + me.sourcePathTextfield = Ext.create('Ext.form.field.Text', { + xtype: 'textfield', + fieldLabel: gettext('Source Path'), + name: 'sourcePath', + emptyText: '/home/user/disk.qcow2', + hidden: Proxmox.UserName !== 'root@pam', + validator: function(insertedText) { + return insertedText.startsWith('/') || + gettext('Must be an absolute path'); + }, + }); + me.column1.unshift( + me.sourceRadioStorage, + me.sourceStorageSelector, + me.sourceFileSelector, + me.sourceRadioPath, + me.sourcePathTextfield, + ); + } me.advancedColumn1.push( { @@ -372,14 +464,19 @@ Ext.define('PVE.qemu.HDEdit', { confid: me.confid, nodename: nodename, unused: unused, - isCreate: me.isCreate + isCreate: me.isCreate, + isImport: me.isImport, }); var subject; if (unused) { me.subject = gettext('Unused Disk'); + } else if (me.isImport) { + me.subject = gettext('Import Disk'); + me.submitText = 'Import'; + me.backgroundDelay = undefined; } else if (me.isCreate) { - me.subject = gettext('Hard Disk'); + me.subject = gettext('Hard Disk'); } else { me.subject = gettext('Hard Disk') + ' (' + me.confid + ')'; } @@ -404,6 +501,9 @@ Ext.define('PVE.qemu.HDEdit', { ipanel.setDrive(drive); me.isValid(); // trigger validation } + if (me.isImport) { + me.url = me.url.replace(/\/config$/, "/importdisk"); + } } }); } diff --git a/www/manager6/qemu/HardwareView.js b/www/manager6/qemu/HardwareView.js index 40b3fe86..dc5e217e 100644 --- a/www/manager6/qemu/HardwareView.js +++ b/www/manager6/qemu/HardwareView.js @@ -436,6 +436,29 @@ Ext.define('PVE.qemu.HardwareView', { handler: run_move }); + var import_btn = new Proxmox.button.Button({ + text: gettext('Import disk'), + hidden: !( + caps.storage['Datastore.Audit'] && + caps.storage['Datastore.Allocate'] && + caps.storage['Datastore.AllocateTemplate'] && + caps.storage['Datastore.AllocateSpace'] && + caps.vms['VM.Allocate'] && + caps.vms['VM.Config.Disk'] && + true + ), + handler: function() { + var win = Ext.create('PVE.qemu.HDEdit', { + method: 'POST', + url: `/api2/extjs/${baseurl}`, + pveSelNode: me.pveSelNode, + isImport: true, + }); + win.on('destroy', me.reload, me); + win.show(); + }, + }); + var remove_btn = new Proxmox.button.Button({ text: gettext('Remove'), defaultText: gettext('Remove'), @@ -752,6 +775,7 @@ Ext.define('PVE.qemu.HardwareView', { edit_btn, resize_btn, move_btn, + import_btn, revert_btn ], rows: rows, -- 2.20.1 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel