[pve-devel] [PATCH manager v4 2/2] Hardware View: Add GUI for importdisk
Dominic Jäger
d.jaeger at proxmox.com
Wed Sep 16 09:43:12 CEST 2020
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 <d.jaeger at proxmox.com>
> ---
> 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 at 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 at 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 at 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 at lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
More information about the pve-devel
mailing list