[pve-devel] [PATCH V2 manager 1/3] fix #2822: add iscsi, lvm, lvmthin & zfs storage for all cluster nodes

Stefan Hrdlicka s.hrdlicka at proxmox.com
Wed Jun 22 16:39:27 CEST 2022


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.hrdlicka at 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






More information about the pve-devel mailing list