From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 0CED61FF187 for ; Mon, 8 Sep 2025 20:02:25 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 200B118D0E; Mon, 8 Sep 2025 20:01:55 +0200 (CEST) From: "Max R. Carrara" To: pve-devel@lists.proxmox.com Date: Mon, 8 Sep 2025 20:00:55 +0200 Message-ID: <20250908180058.530119-12-m.carrara@proxmox.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250908180058.530119-1-m.carrara@proxmox.com> References: <20250908180058.530119-1-m.carrara@proxmox.com> MIME-Version: 1.0 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1757354458633 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.088 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [RFC pve-manager master v1 11/12] ui: storage: support custom storage plugins in Datacenter > Storage X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" This commit implements support for custom form views for custom storage plugins. This is achieved by adapting the `createStorageEditWindow()` function to request the plugin's form view if it's a custom plugin. Whether a plugin is custom or not is first determined by fetching the metadata of all plugins. Furthermore, custom storage plugins now show up in the menu of the "Add" button. If a custom plugin defines a form view, the user can create a new storage config entry using the GUI. The 'short-name' of custom plugins is now also displayed in the storage config list as well as the dropdown selection list of the "Add" button, if present. Otherwise, we fall back to just using the plugin's type, as we were already doing. The items in the menu of the "Add" button are now added dynamically. This is because making requests is asynchronous. Anything else has led to various exceptions being thrown while testing this, due to race conditions. Signed-off-by: Max R. Carrara --- www/manager6/dc/StorageView.js | 134 +++++++++++++++++++++++++++------ 1 file changed, 113 insertions(+), 21 deletions(-) diff --git a/www/manager6/dc/StorageView.js b/www/manager6/dc/StorageView.js index e4c6f07d..f4515c94 100644 --- a/www/manager6/dc/StorageView.js +++ b/www/manager6/dc/StorageView.js @@ -11,6 +11,47 @@ Ext.define( stateId: 'grid-dc-storage', createStorageEditWindow: function (type, sid) { + let me = this; + + let metadataForPlugin = me.pluginMetadata[type]; + + if (!metadataForPlugin) { + Ext.Msg.alert(gettext('Error'), `Plugin '${type}' has no metadata`); + return; + } + + // NOTE: zfspool is only hardcoded for demonstration purposes + if (metadataForPlugin.kind === 'custom' || type === 'zfspool') { + Proxmox.Utils.API2Request({ + url: `/api2/extjs/plugins/storage/${type}/views/form`, + method: 'GET', + params: { + mode: sid ? 'update' : 'create', + }, + failure: function ({ htmlStatus }) { + Ext.Msg.alert(gettext('Error'), htmlStatus); + }, + success: function ({ result: { data } }) { + let formView = data; + + Ext.create('PVE.storage.CustomBaseEdit', { + paneltype: 'PVE.storage.CustomInputPanel', + type: type, + storageId: sid, + canDoBackups: metadataForPlugin.content.supported.includes('backup'), + formView: formView, + metadataForPlugin: metadataForPlugin, + autoShow: true, + listeners: { + destroy: me.reloadStore, + }, + }); + }, + }); + + return; + } + let schema = PVE.Utils.storageSchema[type]; if (!schema || !schema.ipanel) { throw 'no editor registered for storage type: ' + type; @@ -23,7 +64,7 @@ Ext.define( canDoBackups: schema.backups, autoShow: true, listeners: { - destroy: this.reloadStore, + destroy: me.reloadStore, }, }); }, @@ -45,6 +86,63 @@ Ext.define( let sm = Ext.create('Ext.selection.RowModel', {}); + me.pluginMetadata = {}; + + let menuButtonAdd = new Ext.menu.Menu({ + items: [], + }); + + let pushBuiltinPluginsToMenu = function () { + for (const [type, storage] of Object.entries(PVE.Utils.storageSchema)) { + console.log(`Adding builtin plugin '${type}' to Add Button`); + if (storage.hideAdd) { + continue; + } + + menuButtonAdd.add({ + text: PVE.Utils.format_storage_type(type), + iconCls: 'fa fa-fw fa-' + storage.faIcon, + handler: () => me.createStorageEditWindow(type), + }); + } + }; + + let pushCustomPluginsToMenu = function () { + for (const type in me.pluginMetadata) { + if (!Object.hasOwn(me.pluginMetadata, type)) { + continue; + } + + const metadata = me.pluginMetadata[type]; + + if (metadata.kind !== 'custom') { + continue; + } + + menuButtonAdd.add({ + text: metadata['short-name'] || PVE.Utils.format_storage_type(type), + iconCls: 'fa fa-fw fa-folder', + handler: () => me.createStorageEditWindow(type), + }); + } + }; + + Proxmox.Utils.API2Request({ + url: `/api2/extjs/plugins/storage`, + method: 'GET', + success: function ({ result: { data } }) { + data.forEach((metadata) => { + me.pluginMetadata[metadata.type] = metadata; + }); + + pushBuiltinPluginsToMenu(); + pushCustomPluginsToMenu(); + }, + failure: function ({ htmlStatus }) { + Ext.Msg.alert('Error', htmlStatus); + }, + }); + let run_editor = function () { let rec = sm.getSelection()[0]; if (!rec) { @@ -66,23 +164,19 @@ Ext.define( callback: () => store.load(), }); - // else we cannot dynamically generate the add menu handlers - let addHandleGenerator = function (type) { - return function () { - me.createStorageEditWindow(type); - }; - }; - let addMenuItems = []; - for (const [type, storage] of Object.entries(PVE.Utils.storageSchema)) { - if (storage.hideAdd) { - continue; + // value is plugin type here + let columnRendererType = function (value, meta, record) { + let metadataForPlugin = me.pluginMetadata[value]; + + if (metadataForPlugin && metadataForPlugin.kind === 'custom') { + return ( + metadataForPlugin['short-name'] || + PVE.Utils.format_storage_type(value, meta, record) + ); } - addMenuItems.push({ - text: PVE.Utils.format_storage_type(type), - iconCls: 'fa fa-fw fa-' + storage.faIcon, - handler: addHandleGenerator(type), - }); - } + + return PVE.Utils.format_storage_type(value, meta, record); + }; Ext.apply(me, { store: store, @@ -94,9 +188,7 @@ Ext.define( tbar: [ { text: gettext('Add'), - menu: new Ext.menu.Menu({ - items: addMenuItems, - }), + menu: menuButtonAdd, }, remove_btn, edit_btn, @@ -113,7 +205,7 @@ Ext.define( flex: 1, sortable: true, dataIndex: 'type', - renderer: PVE.Utils.format_storage_type, + renderer: columnRendererType, }, { header: gettext('Content'), -- 2.47.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel