This adds a dropdown box for iSCSI, LVM, LVMThin & ZFS storage options where a cluster node needs to be chosen. As default the current node is selected. It restricts the the storage to be only availabe on the selected node.
Signed-off-by: Stefan Hrdlicka <s.hrdli...@proxmox.com> --- www/manager6/storage/Base.js | 44 ++++++++++++++++++++++- www/manager6/storage/IScsiEdit.js | 54 ++++++++++++++++++++++------- www/manager6/storage/LVMEdit.js | 21 +++++++++-- www/manager6/storage/LvmThinEdit.js | 35 ++++++++++++++----- www/manager6/storage/ZFSPoolEdit.js | 24 ++++++++++--- 5 files changed, 148 insertions(+), 30 deletions(-) diff --git a/www/manager6/storage/Base.js b/www/manager6/storage/Base.js index 7f6d7a09..bb497a5f 100644 --- a/www/manager6/storage/Base.js +++ b/www/manager6/storage/Base.js @@ -34,8 +34,9 @@ Ext.define('PVE.panel.StorageBase', { me.column2 = me.column2 || []; me.column2.unshift( { - xtype: 'pveNodeSelector', + xtype: 'pveScanNodeSelector', name: 'nodes', + reference: 'storageNodeRestriction', disabled: me.storageId === 'local', fieldLabel: gettext('Nodes'), emptyText: gettext('All') + ' (' + gettext('No restrictions') +')', @@ -74,6 +75,47 @@ Ext.define('PVE.panel.StorageBase', { me.callParent(); }, + getPveScanNodeSelector: function() { + return { + xtype: 'pveScanNodeSelector', + name: 'node', + itemId: 'pveScanNodeSelector', + fieldLabel: gettext('Scan node'), + allowBlank: false, + disallowedNodes: undefined, + onlineValidator: true, + preferredValue: Proxmox.NodeName, + submitValue: false, + autoEl: { + tag: 'div', + 'data-qtip': gettext('Look for availabe storage options from selected node.'), + }, + }; + }, +}); + +Ext.define('PVE.storage.ComboBoxSetStoreNode', { + extend: 'Ext.form.field.ComboBox', + config: { + apiBaseUrl: '/api2/json/nodes/', + apiStoragePath: '', + }, + + setNodeName: function(value, storeLoad = true) { + let me = this; + if (value === null || value === '') { + value = Proxmox.NodeName; + } + + let store = me.getStore(); + let proxy = store.getProxy(); + proxy.setUrl(me.apiBaseUrl + value + me.apiStoragePath); + this.clearValue(); + if (storeLoad) { + store.load(); + } + }, + }); Ext.define('PVE.storage.BaseEdit', { diff --git a/www/manager6/storage/IScsiEdit.js b/www/manager6/storage/IScsiEdit.js index 2f35f882..c121273b 100644 --- a/www/manager6/storage/IScsiEdit.js +++ b/www/manager6/storage/IScsiEdit.js @@ -1,5 +1,5 @@ Ext.define('PVE.storage.IScsiScan', { - extend: 'Ext.form.field.ComboBox', + extend: 'PVE.storage.ComboBoxSetStoreNode', alias: 'widget.pveIScsiScan', queryParam: 'portal', @@ -10,6 +10,9 @@ Ext.define('PVE.storage.IScsiScan', { loadingText: gettext('Scanning...'), width: 350, }, + config: { + apiStoragePath: '/scan/iscsi', + }, doRawQuery: function() { // do nothing }, @@ -42,7 +45,7 @@ Ext.define('PVE.storage.IScsiScan', { fields: ['target', 'portal'], proxy: { type: 'proxmox', - url: `/api2/json/nodes/${me.nodename}/scan/iscsi`, + url: me.apiBaseUrl + me.nodename + me.apiStoragePath, }, }); store.sort('target', 'ASC'); @@ -77,8 +80,40 @@ Ext.define('PVE.storage.IScsiInputPanel', { initComponent: function() { var me = this; - me.column1 = [ - { + me.column1 = []; + let target = null; + if (me.isCreate) { + target = Ext.createWidget('pveIScsiScan', { + readOnly: !me.isCreate, + name: 'target', + value: '', + fieldLabel: 'Target', + allowBlank: false, + }); + + let pveScanNodeSelector = me.getPveScanNodeSelector(); + pveScanNodeSelector.listeners = { + change: { + fn: function(field, value) { + target.setNodeName(value, false); + me.lookupReference('storageNodeRestriction').setValue(value); + }, + }, + }; + pveScanNodeSelector.preferredValue = ''; + pveScanNodeSelector.allowBlank = true; + pveScanNodeSelector.autoSelect = false; + + me.column1.push(pveScanNodeSelector); + } else { + target = Ext.createWidget('displayfield', { + name: 'target', + value: '', + fieldLabel: gettext('Target'), + allowBlank: false, + }); + } + me.column1.push({ xtype: me.isCreate ? 'textfield' : 'displayfield', name: 'portal', value: '', @@ -94,15 +129,8 @@ Ext.define('PVE.storage.IScsiInputPanel', { }, }, }, - { - readOnly: !me.isCreate, - xtype: me.isCreate ? 'pveIScsiScan' : 'displayfield', - name: 'target', - value: '', - fieldLabel: 'Target', - allowBlank: false, - }, - ]; + ); + me.column1.push(target); me.column2 = [ { diff --git a/www/manager6/storage/LVMEdit.js b/www/manager6/storage/LVMEdit.js index 2a9cd283..a58982e9 100644 --- a/www/manager6/storage/LVMEdit.js +++ b/www/manager6/storage/LVMEdit.js @@ -1,10 +1,13 @@ Ext.define('PVE.storage.VgSelector', { - extend: 'Ext.form.field.ComboBox', + extend: 'PVE.storage.ComboBoxSetStoreNode', alias: 'widget.pveVgSelector', valueField: 'vg', displayField: 'vg', queryMode: 'local', editable: false, + config: { + apiStoragePath: '/scan/lvm', + }, initComponent: function() { var me = this; @@ -17,7 +20,7 @@ Ext.define('PVE.storage.VgSelector', { fields: ['vg', 'size', 'free'], proxy: { type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm', + url: me.apiBaseUrl + me.nodename + me.apiStoragePath, }, }); @@ -103,11 +106,23 @@ Ext.define('PVE.storage.LVMInputPanel', { }); if (me.isCreate) { - var vgField = Ext.create('PVE.storage.VgSelector', { + let vgField = Ext.create('PVE.storage.VgSelector', { name: 'vgname', + reference: 'pveLVMVGSelector', fieldLabel: gettext('Volume group'), allowBlank: false, }); + let pveScanNodeSelector = me.getPveScanNodeSelector(); + pveScanNodeSelector.listeners = { + change: { + fn: function(field, value) { + vgField.setNodeName(value); + me.lookupReference('storageNodeRestriction').setValue(value); + }, + }, + }; + + me.column1.push(pveScanNodeSelector); var baseField = Ext.createWidget('pveFileSelector', { name: 'base', diff --git a/www/manager6/storage/LvmThinEdit.js b/www/manager6/storage/LvmThinEdit.js index 4eab7740..da72dac7 100644 --- a/www/manager6/storage/LvmThinEdit.js +++ b/www/manager6/storage/LvmThinEdit.js @@ -1,5 +1,5 @@ Ext.define('PVE.storage.TPoolSelector', { - extend: 'Ext.form.field.ComboBox', + extend: 'PVE.storage.ComboBoxSetStoreNode', alias: 'widget.pveTPSelector', queryParam: 'vg', @@ -7,6 +7,10 @@ Ext.define('PVE.storage.TPoolSelector', { displayField: 'lv', editable: false, + config: { + apiStoragePath: '/scan/lvmthin', + }, + doRawQuery: function() { // nothing }, @@ -40,7 +44,7 @@ Ext.define('PVE.storage.TPoolSelector', { fields: ['lv'], proxy: { type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvmthin', + url: me.apiBaseUrl + me.nodename + me.apiStoragePath, }, }); @@ -58,13 +62,16 @@ Ext.define('PVE.storage.TPoolSelector', { }); Ext.define('PVE.storage.BaseVGSelector', { - extend: 'Ext.form.field.ComboBox', + extend: 'PVE.storage.ComboBoxSetStoreNode', alias: 'widget.pveBaseVGSelector', valueField: 'vg', displayField: 'vg', queryMode: 'local', editable: false, + config: { + apiStoragePath: '/scan/lvm', + }, initComponent: function() { var me = this; @@ -77,7 +84,7 @@ Ext.define('PVE.storage.BaseVGSelector', { fields: ['vg', 'size', 'free'], proxy: { type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/lvm', + url: me.apiBaseUrl + me.nodename + me.apiStoragePath, }, }); @@ -121,14 +128,12 @@ Ext.define('PVE.storage.LvmThinInputPanel', { }); if (me.isCreate) { - var vgField = Ext.create('PVE.storage.TPoolSelector', { + let vgField = Ext.create('PVE.storage.TPoolSelector', { name: 'thinpool', fieldLabel: gettext('Thin Pool'), allowBlank: false, }); - - me.column1.push({ - xtype: 'pveBaseVGSelector', + let vgSelector = Ext.create('PVE.storage.BaseVGSelector', { name: 'vgname', fieldLabel: gettext('Volume group'), listeners: { @@ -140,6 +145,20 @@ Ext.define('PVE.storage.LvmThinInputPanel', { }, }, }); + let pveScanNodeSelector = me.getPveScanNodeSelector(); + pveScanNodeSelector.listeners = { + change: { + fn: function(field, value) { + // don't reload the store, it requires a VG selected + vgField.setNodeName(value, false); + vgSelector.setNodeName(value); + me.lookupReference('storageNodeRestriction').setValue(value); + }, + }, + }; + me.column1.push(pveScanNodeSelector); + + me.column1.push(vgSelector); me.column1.push(vgField); } diff --git a/www/manager6/storage/ZFSPoolEdit.js b/www/manager6/storage/ZFSPoolEdit.js index 8e689f0c..c274d84a 100644 --- a/www/manager6/storage/ZFSPoolEdit.js +++ b/www/manager6/storage/ZFSPoolEdit.js @@ -1,5 +1,5 @@ Ext.define('PVE.storage.ZFSPoolSelector', { - extend: 'Ext.form.field.ComboBox', + extend: 'PVE.storage.ComboBoxSetStoreNode', alias: 'widget.pveZFSPoolSelector', valueField: 'pool', displayField: 'pool', @@ -8,6 +8,9 @@ Ext.define('PVE.storage.ZFSPoolSelector', { listConfig: { loadingText: gettext('Scanning...'), }, + config: { + apiStoragePath: '/scan/zfs', + }, initComponent: function() { var me = this; @@ -20,10 +23,9 @@ Ext.define('PVE.storage.ZFSPoolSelector', { fields: ['pool', 'size', 'free'], proxy: { type: 'proxmox', - url: '/api2/json/nodes/' + me.nodename + '/scan/zfs', + url: me.apiBaseUrl + me.nodename + me.apiStoragePath, }, }); - store.sort('pool', 'ASC'); Ext.apply(me, { @@ -45,11 +47,23 @@ Ext.define('PVE.storage.ZFSPoolInputPanel', { me.column1 = []; if (me.isCreate) { - me.column1.push(Ext.create('PVE.storage.ZFSPoolSelector', { + let zfsPoolSelector = Ext.create('PVE.storage.ZFSPoolSelector', { name: 'pool', fieldLabel: gettext('ZFS Pool'), allowBlank: false, - })); + }); + let pveScanNodeSelector = me.getPveScanNodeSelector(); + pveScanNodeSelector.listeners = { + change: { + fn: function(field, value) { + zfsPoolSelector.setNodeName(value); + me.lookupReference('storageNodeRestriction').setValue(value); + }, + }, + }; + + me.column1.push(pveScanNodeSelector); + me.column1.push(zfsPoolSelector); } else { me.column1.push(Ext.createWidget('displayfield', { name: 'pool', -- 2.30.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel