From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 7126A1FF0E6 for ; Fri, 24 Jul 2026 16:34:22 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 274482158A; Fri, 24 Jul 2026 16:34:17 +0200 (CEST) From: David Riley To: pve-devel@lists.proxmox.com Subject: [PATCH pve-manager v3 11/12] fix #7294: ui: pool: add SDN VNets as pool members Date: Fri, 24 Jul 2026 16:25:27 +0200 Message-ID: <20260724142528.109453-12-d.riley@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260724142528.109453-1-d.riley@proxmox.com> References: <20260724142528.109453-1-d.riley@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1784903574982 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.084 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_LOW -0.7 Sender listed at https://www.dnswl.org/, low trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: KYSP6ACF7KARZHZWDCN23JNJKAN2BH6J X-Message-ID-Hash: KYSP6ACF7KARZHZWDCN23JNJKAN2BH6J X-MailFrom: d.riley@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Add user interface to manage SDN VNets inside resource pools. In the dialog window users can select an SDN zone, pick an associated VNet, and optionally specify a VLAN tag or enable propagation to grant full access to all tagged and untagged traffic. The VNet selector only shows VNets of the selected zone to prevent invalid configurations. Add opacity adjustments for the `fa-network-wired` icon used by pool networks. The light/base theme lowers the icons opacity to simulate a light grey color that matches other configuration elements. Signed-off-by: David Riley Link: https://bugzilla.proxmox.com/show_bug.cgi?id=7294 --- www/css/ext6-pve.css | 15 ++++ www/manager6/Utils.js | 1 + www/manager6/grid/PoolMembers.js | 145 +++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css index 5c37dd29..3c383dfa 100644 --- a/www/css/ext6-pve.css +++ b/www/css/ext6-pve.css @@ -464,6 +464,21 @@ div.right-aligned { content: " "; } +.x-fa-pool-net:before { + width: 14px; + height: 14px; + position: absolute; + left: 1px; + top: 1px; + opacity: 0.45; +} + +.x-fa-pool-net-grid:before { + left: 14px; + top: 6px; + opacity: 0.65; +} + .x-fa-treepanel:before { width: 16px; height: 24px; diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js index 040b5ae0..8f53dfb6 100644 --- a/www/manager6/Utils.js +++ b/www/manager6/Utils.js @@ -1322,6 +1322,7 @@ Ext.define('PVE.Utils', { const networkTypeMapping = { fabric: 'fa fa-road', zone: 'fa fa-th', + vnet: 'fa fa-network-wired x-fa-pool-net x-fa-pool-net-grid', }; return networkTypeMapping[record['network-type']] ?? ''; diff --git a/www/manager6/grid/PoolMembers.js b/www/manager6/grid/PoolMembers.js index 69f50e30..c3902983 100644 --- a/www/manager6/grid/PoolMembers.js +++ b/www/manager6/grid/PoolMembers.js @@ -158,6 +158,128 @@ Ext.define('PVE.pool.AddStorage', { }, }); +Ext.define('PVE.pool.AddVnet', { + extend: 'Proxmox.window.Edit', + + viewModel: { + data: { + zone: '', + }, + formulas: { + vnetDisabled: function (get) { + return !get('zone'); + }, + }, + }, + + initComponent: function () { + let me = this; + + if (!me.pool) { + throw 'no pool specified'; + } + + me.isCreate = true; + me.isAdd = true; + me.url = '/pools/'; + me.method = 'PUT'; + me.extraRequestParams.poolid = me.pool; + + Ext.apply(me, { + subject: gettext('VNet'), + width: 350, + items: [ + { + xtype: 'inputpanel', + onGetValues: function (values) { + let network = `vnet/${values.zone}/${values.vnet}`; + + if (values.propagate) { + network += '/*'; + } else if (values.tag) { + network += `/${values.tag}`; + } + + delete values.zone; + delete values.vnet; + delete values.propagate; + delete values.tag; + + values.network = network; + + return values; + }, + items: [ + { + xtype: 'pveSDNZoneSelector', + fieldLabel: gettext('Zone'), + name: 'zone', + allowBlank: false, + bind: { + value: '{zone}', + }, + listeners: { + change: function (_f, _value) { + let vnetField = me.lookupReference('vnetField'); + if (vnetField) { + vnetField.setValue(''); + } + }, + }, + }, + { + xtype: 'pveSDNVnetSelector', + fieldLabel: gettext('VNet'), + name: 'vnet', + reference: 'vnetField', + allowBlank: false, + bind: { + disabled: '{vnetDisabled}', + }, + listeners: { + beforequery: function (_queryPlan) { + let zone = me.getViewModel().get('zone'); + let store = this.getStore(); + store.clearFilter(); + store.filter('zone', zone); + return true; + }, + }, + }, + { + xtype: 'proxmoxintegerfield', + name: 'tag', + fieldLabel: gettext('VLAN Tag'), + minValue: 1, + maxValue: 4094, + allowBlank: true, + emptyText: gettext('All'), + bind: { + disabled: '{propagate}', + }, + }, + { + xtype: 'proxmoxcheckbox', + name: 'propagate', + checked: false, + uncheckedValue: 0, + fieldLabel: gettext('Propagate'), + boxLabel: gettext( + 'Grant full access to this VNet (tagged and untagged)', + ), + bind: { + value: '{propagate}', + }, + }, + ], + }, + ], + }); + + me.callParent(); + }, +}); + Ext.define('PVE.grid.PoolMembers', { extend: 'Ext.grid.GridPanel', alias: ['widget.pvePoolMembers'], @@ -223,6 +345,20 @@ Ext.define('PVE.grid.PoolMembers', { rec.data.type === 'openvz' ) { params.vms = rec.data.vmid; + } else if (rec.get('type') === 'network') { + if (rec.get('network-type') === 'vnet') { + let [_type, zone, vnet, tag] = rec.data.id.split('/'); + + let network = `vnet/${zone}/${vnet}`; + + if (tag) { + network += `/${tag}`; + } + + params.network = network; + } else { + console.warn('unknown network-type encountered'); + } } else { throw 'unknown resource type'; } @@ -268,6 +404,15 @@ Ext.define('PVE.grid.PoolMembers', { win.show(); }, }, + { + text: gettext('VNet'), + iconCls: 'fa fa-network-wired x-fa-pool-net', + handler: function () { + let win = Ext.create('PVE.pool.AddVnet', { pool: me.pool }); + win.on('destroy', reload); + win.show(); + }, + }, ], }), }, -- 2.47.3