From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id D7B3862252 for ; Wed, 16 Sep 2020 09:43:43 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id C471520879 for ; Wed, 16 Sep 2020 09:43:13 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 48F772086F for ; Wed, 16 Sep 2020 09:43:12 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 12CF4453D9 for ; Wed, 16 Sep 2020 09:43:12 +0200 (CEST) Date: Wed, 16 Sep 2020 09:43:12 +0200 From: Dominic =?iso-8859-1?Q?J=E4ger?= To: pve-devel@lists.proxmox.com Message-ID: <20200916074311.GA1146873@mala.proxmox.com> References: <20200915113324.313395-1-d.jaeger@proxmox.com> <20200915113324.313395-3-d.jaeger@proxmox.com> MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <20200915113324.313395-3-d.jaeger@proxmox.com> User-Agent: Mutt/1.10.1 (2018-07-13) X-SPAM-LEVEL: Spam detection results: 0 AWL 1.534 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [proxmox.com, params.storage, caps.storage] Subject: Re: [pve-devel] [PATCH manager v4 2/2] Hardware View: Add GUI for importdisk X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 16 Sep 2020 07:43:43 -0000 If you only want to take a quick lock, I put a screenshot in Bugzilla https://bugzilla.proxmox.com/show_bug.cgi?id=2886#c2 On Tue, Sep 15, 2020 at 01:33:24PM +0200, Dominic Jäger wrote: > Make importing single disks easier. > Required to import a whole VM via GUI. > > Signed-off-by: Dominic Jäger > --- > v3->v4: > * Reuse propertyStringSet instead of building it myself > * More detailed permissions > * Reorder GUI elements such that source is first > * Assemble importdisk URL here instead of widget-toolkit & use regex for > correct replacement > * Allow selecting images from PVE storages (Normal users + root) or all paths > (root) > > 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