[pve-devel] [RFC pve-manager 1/2] multi disk edit: add copy button

Max Carrara m.carrara at proxmox.com
Wed Dec 6 11:49:53 CET 2023


On 12/6/23 10:25, Dominik Csapak wrote:
> high level, i think i'd find it better to put the button next to the
> remove button (as you suggested in the cover letter)
> 
> my rationale is that the place where the add button is, is a 'neutral' place without connection to an existing disk, but the copy is an
> action related to a specific disk so it makes sense to add it there

I agree! I'll put it there in the v2. I just want to make sure that
I can disable the button as well, since we temporarily disable the
"add" button while constructing the item.

> 
> alternatively you could maybe put it in the disk panel itself, so there
> would still be only a single copy button visible (when selecting the disk) but that might
> 1. look weird (would have to test)
> 2. be hard to place (not sure if there would be space at all)
> 3. make the code uglier (you'd have to call something in the parent class)
> 
> the idea is nice, otherwise see some comments inline
> 
> On 12/5/23 16:44, Max Carrara wrote:
>> Add a copy button that copies the configuration of one disk to a new
>> one.
>>
>> Signed-off-by: Max Carrara <m.carrara at proxmox.com>
>> ---
>>   www/manager6/panel/MultiDiskEdit.js | 78 +++++++++++++++++++++++++----
>>   1 file changed, 68 insertions(+), 10 deletions(-)
>>
>> diff --git a/www/manager6/panel/MultiDiskEdit.js b/www/manager6/panel/MultiDiskEdit.js
>> index ea1f974d..dffbb4c5 100644
>> --- a/www/manager6/panel/MultiDiskEdit.js
>> +++ b/www/manager6/panel/MultiDiskEdit.js
>> @@ -13,14 +13,37 @@ Ext.define('PVE.panel.MultiDiskPanel', {
>>       controller: {
>>       xclass: 'Ext.app.ViewController',
>>   +    disableableButtons: ['addButton', 'copyButton'],
>> +
>>       vmconfig: {},
>>         onAdd: function() {
>>           let me = this;
>> -        me.lookup('addButton').setDisabled(true);
>> +        me.disableButtons();
>>           me.addDisk();
>> -        let count = me.lookup('grid').getStore().getCount() + 1; // +1 is from ide2
>> -        me.lookup('addButton').setDisabled(count >= me.maxCount);
>> +        me.enableButtons();
>> +    },
>> +
>> +    onCopy: function(button, event) {
>> +        let me = this;
>> +        me.disableButtons();
>> +        me.addDisk(true);
>> +        me.enableButtons();
>> +    },
>> +
>> +    disableButtons: function() {
>> +        let me = this;
>> +        for (let buttonName of me.disableableButtons) {
>> +        me.lookup(buttonName).setDisabled(true);
>> +        }
>> +    },
>> +
>> +    enableButtons: function() {
>> +        let me = this;
>> +        let count = me.lookup('grid').getStore().getCount() + 1;
> 
> you lost the ide2 comment here ;)

Woops, my bad!

> 
> 
>> +        for (let buttonName of me.disableableButtons) {
>> +        me.lookup(buttonName).setDisabled(count >= me.maxCount);
>> +        }
>>       },
>>         getNextFreeDisk: function(vmconfig) {
>> @@ -34,7 +57,7 @@ Ext.define('PVE.panel.MultiDiskPanel', {
>>       // define in subclass
>>       diskSorter: undefined,
>>   -    addDisk: function() {
>> +    addDisk: function(copy = false) {
>>           let me = this;
>>           let grid = me.lookup('grid');
>>           let store = grid.getStore();
>> @@ -54,6 +77,21 @@ Ext.define('PVE.panel.MultiDiskPanel', {
>>           })[0];
>>             let panel = me.addPanel(itemId, vmconfig, nextFreeDisk);
>> +
>> +        if (copy) {
>> +        let selection = grid.getSelection()[0];
>> +        let selectedItemId = selection.get('itemId');
>> +        let panelFrom = me.getView().getComponent(selectedItemId);
>> +
>> +        if (!panelFrom) {
>> +            throw "unexpected missing panel";
>> +        }
>> +
>> +        let values = panelFrom.getValues(false, true);
>> +        values.deviceid = nextFreeDisk.id;
> 
> what happens here when the nextfree controller type is not the same?
> the options of the panel differ between them (e.g. ide is not
> capable of setting read only or iothread)

`setValues()` uses `Ext.query()` under the hood - `query()` will return
an empty array if it cannot find anything, so if a field cannot be found,
it will just be silently ignored.

> 
> maybe we should disable that button if there is no free id for
> the same controller type? we do want to copy the config, but
> if we can't then we can't ;)

This approach seems reasonable, though one thing I have noticed when
testing this, is that if you end up running out of IDs, it will simply
add another device with ID 0 and then (rightfully) complain that the
device is already in use. It's not possible to proceed in this case;
either the disk needs to be removed or the bus type needs to be changed.

imo that's acceptable - it does what the user wants but it's up to them
what to do if the IDs run out.

In that case we also don't need to handle changing to a different controller

The add button behaves a little differently though and will automatically
switch to a new bus, so maybe the copy button could to the same, but
I'm not quite sure yet, to be honest.

> 
>> +        panel.setValues(values);
>> +        }
>> +
>>           panel.updateVMConfig(vmconfig);
>>             // we need to setup a validitychange handler, so that we can show
>> @@ -170,7 +208,7 @@ Ext.define('PVE.panel.MultiDiskPanel', {
>>             store.remove(record);
>>           me.getView().remove(record.get('itemId'));
>> -        me.lookup('addButton').setDisabled(false);
>> +        me.enableButtons();
>>           me.updateVMConfig();
>>           me.checkValidity();
>>       },
>> @@ -251,11 +289,31 @@ Ext.define('PVE.panel.MultiDiskPanel', {
>>               ],
>>           },
>>           {
>> -            xtype: 'button',
>> -            reference: 'addButton',
>> -            text: gettext('Add'),
>> -            iconCls: 'fa fa-plus-circle',
>> -            handler: 'onAdd',
>> +            layout: 'hbox',
>> +            border: false,
>> +            defaults: {
>> +            border: false,
>> +            layout: 'anchor',
>> +            flex: 1,
>> +            },
>> +            items: [
>> +            {
>> +                xtype: 'button',
>> +                reference: 'addButton',
>> +                text: gettext('Add'),
>> +                iconCls: 'fa fa-plus-circle',
>> +                handler: 'onAdd',
>> +                margin: '0 2.5 0 0',
>> +            },
>> +            {
>> +                xtype: 'button',
>> +                reference: 'copyButton',
>> +                text: gettext('Copy'),
>> +                iconCls: 'fa fa-files-o',
>> +                handler: 'onCopy',
>> +                margin: '0 0 0 2.5',
>> +            },
>> +            ],
>>           },
>>           {
>>               // dummy field to control wizard validation





More information about the pve-devel mailing list