From d420b29ae31e4ff2ffd9cd707c5b1468dd0e6f3a Mon Sep 17 00:00:00 2001 From: Dominik Csapak Date: Wed, 18 Jan 2023 14:12:59 +0100 Subject: [PATCH] ui: storage: move node scan selector inside combobox by converting the relevant selection boxes to combogrids. This is done to reduce confusion for how/why to select a node, and doing it this way it is moved closer to the selection of the actual value we want. It still restricts the nodes when selecting a specific one. Show it only when there is more than one node according to the in-memory PVE.data.ResourceStore info, as for single-node setups there isn't any other node one could scan anyway. Suggested-by: Thomas Lamprecht Signed-off-by: Dominik Csapak Signed-off-by: Thomas Lamprecht --- www/manager6/form/ComboBoxSetStoreNode.js | 45 +++++++++++++- www/manager6/storage/IScsiEdit.js | 53 +++++++++-------- www/manager6/storage/LVMEdit.js | 71 +++++++++++++++++------ www/manager6/storage/LvmThinEdit.js | 71 +++++++++++++---------- www/manager6/storage/ZFSPoolEdit.js | 29 +++++---- 5 files changed, 180 insertions(+), 89 deletions(-) diff --git a/www/manager6/form/ComboBoxSetStoreNode.js b/www/manager6/form/ComboBoxSetStoreNode.js index 3490ddd7..a654636b 100644 --- a/www/manager6/form/ComboBoxSetStoreNode.js +++ b/www/manager6/form/ComboBoxSetStoreNode.js @@ -1,16 +1,57 @@ Ext.define('PVE.form.ComboBoxSetStoreNode', { - extend: 'Ext.form.field.ComboBox', + extend: 'Proxmox.form.ComboGrid', config: { apiBaseUrl: '/api2/json/nodes/', apiSuffix: '', }, + showNodeSelector: false, + setNodeName: function(value) { let me = this; value ||= Proxmox.NodeName; me.getStore().getProxy().setUrl(`${me.apiBaseUrl}${value}${me.apiSuffix}`); - this.clearValue(); + me.clearValue(); + }, + + nodeChange: function(_field, value) { + let me = this; + // disable autoSelect if there is already a selection or we have the picker open + if (me.getValue() || me.isExpanded) { + let autoSelect = me.autoSelect; + me.autoSelect = false; + me.store.on('afterload', function() { + me.autoSelect = autoSelect; + }, { single: true }); + } + me.setNodeName(value); + me.fireEvent('nodechanged', value); }, + initComponent: function() { + let me = this; + + if (me.showNodeSelector && PVE.data.ResourceStore.getNodes().length > 1) { + me.errorHeight = 140; + Ext.apply(me.listConfig ?? {}, { + tbar: { + xtype: 'toolbar', + items: [ + { + xtype: "pveStorageScanNodeSelector", + autoSelect: false, + fieldLabel: gettext('Node to scan'), + listeners: { + change: (field, value) => me.nodeChange(field, value), + }, + }, + ], + }, + emptyText: me.listConfig?.emptyText ?? gettext('Nothing found'), + }); + } + + me.callParent(); + }, }); diff --git a/www/manager6/storage/IScsiEdit.js b/www/manager6/storage/IScsiEdit.js index 96c1aa28..6a1e4aeb 100644 --- a/www/manager6/storage/IScsiEdit.js +++ b/www/manager6/storage/IScsiEdit.js @@ -6,32 +6,43 @@ Ext.define('PVE.storage.IScsiScan', { valueField: 'target', displayField: 'target', matchFieldWidth: false, + allowBlank: false, + listConfig: { - loadingText: gettext('Scanning...'), width: 350, + columns: [ + { + dataIndex: 'target', + flex: 1, + }, + ], + emptyText: gettext('No iSCSI target found'), }, + config: { apiSuffix: '/scan/iscsi', }, - doRawQuery: function() { - // do nothing - }, - onTriggerClick: function() { - let me = this; + showNodeSelector: true, - if (!me.queryCaching || me.lastQuery !== me.portal) { - me.store.removeAll(); + reload: function() { + let me = this; + if (!me.isDisabled()) { + me.getStore().load(); } - - me.allQuery = me.portal; - - me.callParent(); }, setPortal: function(portal) { let me = this; me.portal = portal; + me.getStore().getProxy().setExtraParams({ portal }); + me.reload(); + }, + + setNodeName: function(value) { + let me = this; + me.callParent([value]); + me.reload(); }, initComponent: function() { @@ -81,19 +92,6 @@ Ext.define('PVE.storage.IScsiInputPanel', { let me = this; me.column1 = [ - { - xtype: 'pveStorageScanNodeSelector', - disabled: !me.isCreate, - hidden: !me.isCreate, - listeners: { - change: { - fn: function(field, value) { - me.lookup('iScsiTargetScan').setNodeName(value); - me.lookup('storageNodeRestriction').setValue(value); - }, - }, - }, - }, { xtype: me.isCreate ? 'textfield' : 'displayfield', name: 'portal', @@ -117,6 +115,11 @@ Ext.define('PVE.storage.IScsiInputPanel', { fieldLabel: gettext('Target'), allowBlank: false, reference: 'iScsiTargetScan', + listeners: { + nodechanged: function(value) { + me.lookup('storageNodeRestriction').setValue(value); + }, + }, }), ]; diff --git a/www/manager6/storage/LVMEdit.js b/www/manager6/storage/LVMEdit.js index b323a529..84ee198d 100644 --- a/www/manager6/storage/LVMEdit.js +++ b/www/manager6/storage/LVMEdit.js @@ -5,10 +5,23 @@ Ext.define('PVE.storage.VgSelector', { displayField: 'vg', queryMode: 'local', editable: false, + + listConfig: { + columns: [ + { + dataIndex: 'vg', + flex: 1, + }, + ], + emptyText: gettext('No volume groups found'), + }, + config: { apiSuffix: '/scan/lvm', }, + showNodeSelector: true, + setNodeName: function(value) { let me = this; me.callParent([value]); @@ -35,9 +48,6 @@ Ext.define('PVE.storage.VgSelector', { Ext.apply(me, { store: store, - listConfig: { - loadingText: gettext('Scanning...'), - }, }); me.callParent(); @@ -93,6 +103,42 @@ Ext.define('PVE.storage.BaseStorageSelector', { }, }); +Ext.define('PVE.storage.LunSelector', { + extend: 'PVE.form.FileSelector', + alias: 'widget.pveStorageLunSelector', + + nodename: 'localhost', + storageContent: 'images', + allowBlank: false, + + initComponent: function() { + let me = this; + + if (PVE.data.ResourceStore.getNodes().length > 1) { + me.errorHeight = 140; + Ext.apply(me.listConfig ?? {}, { + tbar: { + xtype: 'toolbar', + items: [ + { + xtype: "pveStorageScanNodeSelector", + autoSelect: false, + fieldLabel: gettext('Node to scan'), + listeners: { + change: (_field, value) => me.setNodename(value), + }, + }, + ], + }, + emptyText: me.listConfig?.emptyText ?? gettext('Nothing found'), + }); + } + + me.callParent(); + }, + +}); + Ext.define('PVE.storage.LVMInputPanel', { extend: 'PVE.panel.StorageBase', @@ -118,27 +164,16 @@ Ext.define('PVE.storage.LVMInputPanel', { fieldLabel: gettext('Volume group'), reference: 'volumeGroupSelector', allowBlank: false, - }); - me.column1.push({ - xtype: 'pveStorageScanNodeSelector', - listeners: { - change: { - fn: function(field, value) { - me.lookup('volumeGroupSelector').setNodeName(value); - me.lookup('storageNodeRestriction').setValue(value); - }, - }, - }, + listeners: { + nodechanged: (value) => me.lookup('storageNodeRestriction').setValue(value), + } }); - let baseField = Ext.createWidget('pveFileSelector', { + let baseField = Ext.createWidget('pveStorageLunSelector', { name: 'base', hidden: true, disabled: true, - nodename: 'localhost', - storageContent: 'images', fieldLabel: gettext('Base volume'), - allowBlank: false, }); me.column1.push({ diff --git a/www/manager6/storage/LvmThinEdit.js b/www/manager6/storage/LvmThinEdit.js index 92e6c296..0c288bb0 100644 --- a/www/manager6/storage/LvmThinEdit.js +++ b/www/manager6/storage/LvmThinEdit.js @@ -6,31 +6,40 @@ Ext.define('PVE.storage.TPoolSelector', { valueField: 'lv', displayField: 'lv', editable: false, + allowBlank: false, + + listConfig: { + emptyText: gettext("No thinpool found"), + columns: [ + { + dataIndex: 'lv', + flex: 1, + }, + ], + }, config: { apiSuffix: '/scan/lvmthin', }, - doRawQuery: function() { - // nothing - }, - - onTriggerClick: function() { + reload: function() { let me = this; - - if (!me.queryCaching || me.lastQuery !== me.vg) { - me.store.removeAll(); + if (!me.isDisabled()) { + me.getStore().load(); } - - me.allQuery = me.vg; - - me.callParent(); }, setVG: function(myvg) { let me = this; - me.vg = myvg; + me.getStore().getProxy().setExtraParams({ vg: myvg }); + me.reload(); + }, + + setNodeName: function(value) { + let me = this; + me.callParent([value]); + me.reload(); }, initComponent: function() { @@ -52,9 +61,6 @@ Ext.define('PVE.storage.TPoolSelector', { Ext.apply(me, { store: store, - listConfig: { - loadingText: gettext('Scanning...'), - }, }); me.callParent(); @@ -69,6 +75,19 @@ Ext.define('PVE.storage.BaseVGSelector', { displayField: 'vg', queryMode: 'local', editable: false, + allowBlank: false, + + listConfig: { + columns: [ + { + dataIndex: 'vg', + flex: 1, + }, + ], + }, + + showNodeSelector: true, + config: { apiSuffix: '/scan/lvm', }, @@ -97,9 +116,6 @@ Ext.define('PVE.storage.BaseVGSelector', { Ext.apply(me, { store: store, - listConfig: { - loadingText: gettext('Scanning...'), - }, }); me.callParent(); @@ -135,24 +151,15 @@ Ext.define('PVE.storage.LvmThinInputPanel', { }); if (me.isCreate) { - me.column1.push({ - xtype: 'pveStorageScanNodeSelector', - listeners: { - change: { - fn: function(field, value) { - me.lookup('thinPoolSelector').setNodeName(value); - me.lookup('volumeGroupSelector').setNodeName(value); - me.lookup('storageNodeRestriction').setValue(value); - }, - }, - }, - }); - me.column1.push(Ext.create('PVE.storage.BaseVGSelector', { name: 'vgname', fieldLabel: gettext('Volume group'), reference: 'volumeGroupSelector', listeners: { + nodechanged: function(value) { + me.lookup('thinPoolSelector').setNodeName(value); + me.lookup('storageNodeRestriction').setValue(value); + }, change: function(f, value) { if (me.isCreate) { let vgField = me.lookup('thinPoolSelector'); diff --git a/www/manager6/storage/ZFSPoolEdit.js b/www/manager6/storage/ZFSPoolEdit.js index 65e152be..df12fbbc 100644 --- a/www/manager6/storage/ZFSPoolEdit.js +++ b/www/manager6/storage/ZFSPoolEdit.js @@ -5,13 +5,24 @@ Ext.define('PVE.storage.ZFSPoolSelector', { displayField: 'pool', queryMode: 'local', editable: false, + allowBlank: false, + listConfig: { - loadingText: gettext('Scanning...'), + columns: [ + { + dataIndex: 'pool', + flex: 1, + }, + ], + emptyText: gettext('No ZFS Pools found'), }, + config: { apiSuffix: '/scan/zfs', }, + showNodeSelector: true, + setNodeName: function(value) { let me = this; me.callParent([value]); @@ -54,22 +65,16 @@ Ext.define('PVE.storage.ZFSPoolInputPanel', { me.column1 = []; if (me.isCreate) { - me.column1.push({ - xtype: 'pveStorageScanNodeSelector', - listeners: { - change: { - fn: function(field, value) { - me.lookup('zfsPoolSelector').setNodeName(value); - me.lookup('storageNodeRestriction').setValue(value); - }, - }, - }, - }); me.column1.push(Ext.create('PVE.storage.ZFSPoolSelector', { name: 'pool', fieldLabel: gettext('ZFS Pool'), reference: 'zfsPoolSelector', allowBlank: false, + listeners: { + nodechanged: function(value) { + me.lookup('storageNodeRestriction').setValue(value); + }, + }, })); } else { me.column1.push(Ext.createWidget('displayfield', { -- 2.39.5