From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 2FD861FF13A for ; Wed, 13 May 2026 20:42:43 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id B23F519C23; Wed, 13 May 2026 20:42:27 +0200 (CEST) From: Hannes Laimer To: pve-devel@lists.proxmox.com Subject: [PATCH pve-manager v5 7/8] ui: sdn: add BGP fabric support Date: Wed, 13 May 2026 20:42:12 +0200 Message-ID: <20260513184213.506775-8-h.laimer@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260513184213.506775-1-h.laimer@proxmox.com> References: <20260513184213.506775-1-h.laimer@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1778697732797 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.081 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 Message-ID-Hash: MCKYJ56IVY3KTTQQCKJP6THPATYRTN6T X-Message-ID-Hash: MCKYJ56IVY3KTTQQCKJP6THPATYRTN6T X-MailFrom: h.laimer@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: Register BGP as a fabric protocol in the UI. The fabric editor omits the ASN field since each node must specify its own unique ASN for eBGP unnumbered peering. The interface panel excludes the IP column since BGP unnumbered interfaces have no IP addresses. Signed-off-by: Hannes Laimer --- www/manager6/Makefile | 3 + www/manager6/sdn/FabricsView.js | 12 ++++ www/manager6/sdn/fabrics/FabricEdit.js | 12 +++- www/manager6/sdn/fabrics/NodeEdit.js | 1 + www/manager6/sdn/fabrics/bgp/FabricEdit.js | 70 +++++++++++++++++++ .../sdn/fabrics/bgp/InterfacePanel.js | 15 ++++ www/manager6/sdn/fabrics/bgp/NodeEdit.js | 23 ++++++ 7 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 www/manager6/sdn/fabrics/bgp/FabricEdit.js create mode 100644 www/manager6/sdn/fabrics/bgp/InterfacePanel.js create mode 100644 www/manager6/sdn/fabrics/bgp/NodeEdit.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index b4ae1b3b..27f123d8 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -348,6 +348,9 @@ JSSRC= \ sdn/fabrics/wireguard/InterfacePanel.js \ sdn/fabrics/wireguard/NodeEdit.js \ sdn/fabrics/wireguard/FabricEdit.js \ + sdn/fabrics/bgp/InterfacePanel.js \ + sdn/fabrics/bgp/NodeEdit.js \ + sdn/fabrics/bgp/FabricEdit.js \ storage/ContentView.js \ storage/BackupView.js \ storage/Base.js \ diff --git a/www/manager6/sdn/FabricsView.js b/www/manager6/sdn/FabricsView.js index 7da1990d..9a802985 100644 --- a/www/manager6/sdn/FabricsView.js +++ b/www/manager6/sdn/FabricsView.js @@ -34,6 +34,7 @@ Ext.define('PVE.sdn.Fabric.View', { openfabric: 'OpenFabric', ospf: 'OSPF', wireguard: 'WireGuard', + bgp: 'BGP', }; const displayValue = PROTOCOL_DISPLAY_NAMES[value]; if (rec.data.state === undefined || rec.data.state === null) { @@ -203,6 +204,10 @@ Ext.define('PVE.sdn.Fabric.View', { text: 'WireGuard', handler: 'addWireGuard', }, + { + text: 'BGP', + handler: 'addBgp', + }, ], }, addNodeButton, @@ -282,6 +287,7 @@ Ext.define('PVE.sdn.Fabric.View', { openfabric: 'PVE.sdn.Fabric.OpenFabric.Fabric.Edit', ospf: 'PVE.sdn.Fabric.Ospf.Fabric.Edit', wireguard: 'PVE.sdn.Fabric.WireGuard.Fabric.Edit', + bgp: 'PVE.sdn.Fabric.Bgp.Fabric.Edit', }; return FABRIC_PANELS[protocol]; @@ -292,6 +298,7 @@ Ext.define('PVE.sdn.Fabric.View', { openfabric: 'PVE.sdn.Fabric.OpenFabric.Node.Edit', ospf: 'PVE.sdn.Fabric.Ospf.Node.Edit', wireguard: 'PVE.sdn.Fabric.WireGuard.Node.Edit', + bgp: 'PVE.sdn.Fabric.Bgp.Node.Edit', }; return NODE_PANELS[protocol]; @@ -312,6 +319,11 @@ Ext.define('PVE.sdn.Fabric.View', { me.openFabricAddWindow('ospf'); }, + addBgp: function () { + let me = this; + me.openFabricAddWindow('bgp'); + }, + openFabricAddWindow: function (protocol) { let me = this; diff --git a/www/manager6/sdn/fabrics/FabricEdit.js b/www/manager6/sdn/fabrics/FabricEdit.js index 43a402a9..322a62c0 100644 --- a/www/manager6/sdn/fabrics/FabricEdit.js +++ b/www/manager6/sdn/fabrics/FabricEdit.js @@ -37,6 +37,7 @@ Ext.define('PVE.sdn.Fabric.Fabric.Edit', { ], additionalItems: [], + additionalAdvancedItems: [], additionalTabs: [], initComponent: function () { @@ -101,10 +102,15 @@ Ext.define('PVE.sdn.Fabric.Fabric.Edit', { if (me.additionalTabs.length > 0) { let items = [...me.items, ...me.additionalItems]; - let iPanel = Ext.create('Proxmox.panel.InputPanel', { + let panelConfig = { title: gettext('Fabric'), items, - }); + }; + if (me.additionalAdvancedItems.length > 0) { + panelConfig.advancedItems = me.additionalAdvancedItems; + } + + let iPanel = Ext.create('Proxmox.panel.InputPanel', panelConfig); me.bodyPadding = 0; @@ -116,7 +122,7 @@ Ext.define('PVE.sdn.Fabric.Fabric.Edit', { }, ]; } else { - me.items.push(...me.additionalItems); + me.items.push(...me.additionalItems, ...me.additionalAdvancedItems); } me.callParent(); diff --git a/www/manager6/sdn/fabrics/NodeEdit.js b/www/manager6/sdn/fabrics/NodeEdit.js index f2a32c7b..272915aa 100644 --- a/www/manager6/sdn/fabrics/NodeEdit.js +++ b/www/manager6/sdn/fabrics/NodeEdit.js @@ -260,6 +260,7 @@ Ext.define('PVE.sdn.Fabric.Node.Edit', { openfabric: 'PVE.sdn.Fabric.OpenFabric.InterfacePanel', ospf: 'PVE.sdn.Fabric.Ospf.InterfacePanel', wireguard: 'PVE.sdn.Fabric.WireGuard.InterfacePanel', + bgp: 'PVE.sdn.Fabric.Bgp.InterfacePanel', }; return INTERFACE_PANELS[protocol]; diff --git a/www/manager6/sdn/fabrics/bgp/FabricEdit.js b/www/manager6/sdn/fabrics/bgp/FabricEdit.js new file mode 100644 index 00000000..40941b5a --- /dev/null +++ b/www/manager6/sdn/fabrics/bgp/FabricEdit.js @@ -0,0 +1,70 @@ +Ext.define('PVE.sdn.Fabric.Bgp.Fabric.Edit', { + extend: 'PVE.sdn.Fabric.Fabric.Edit', + + subject: 'BGP', + onlineHelp: 'pvesdn_bgp_fabric', + + extraRequestParams: { + protocol: 'bgp', + }, + + additionalItems: [ + { + xtype: 'proxmoxcheckbox', + fieldLabel: gettext('BFD'), + labelWidth: 120, + name: 'bfd', + uncheckedValue: 0, + defaultValue: 0, + }, + { + xtype: 'pveSDNPrefixListSelector', + name: 'route_filter', + fieldLabel: gettext('Route Filter'), + labelWidth: 120, + emptyText: gettext('IP Prefixes'), + deleteEmpty: true, + skipEmptyText: true, + }, + ], + + additionalAdvancedItems: [ + { + xtype: 'pveSDNRouteMapSelector', + name: 'route_map_in', + fieldLabel: gettext('Incoming Route Map'), + labelWidth: 120, + emptyText: gettext('Route Map'), + deleteEmpty: true, + skipEmptyText: true, + }, + { + xtype: 'pveSDNRouteMapSelector', + name: 'route_map_out', + fieldLabel: gettext('Outgoing Route Map'), + labelWidth: 120, + emptyText: gettext('Route Map'), + deleteEmpty: true, + skipEmptyText: true, + }, + ], + + additionalTabs: [ + { + xtype: 'inputpanel', + title: gettext('Route Redistribution'), + items: [ + { + xtype: 'pveSDNRedistributionGrid', + name: 'redistribute', + sources: [ + ['ospf', gettext('OSPF')], + ['connected', gettext('Connected')], + ['static', gettext('Static')], + ['kernel', gettext('Kernel')], + ], + }, + ], + } + ], +}); diff --git a/www/manager6/sdn/fabrics/bgp/InterfacePanel.js b/www/manager6/sdn/fabrics/bgp/InterfacePanel.js new file mode 100644 index 00000000..c7ac7627 --- /dev/null +++ b/www/manager6/sdn/fabrics/bgp/InterfacePanel.js @@ -0,0 +1,15 @@ +Ext.define('PVE.sdn.Fabric.Bgp.InterfacePanel', { + extend: 'PVE.sdn.Fabric.InterfacePanel', + + hasIpv6Support: false, + + // BGP unnumbered interfaces have no IP - override commonColumns to + // exclude the IP column that the base class defines. + initComponent: function () { + let me = this; + + me.commonColumns = me.commonColumns.filter((col) => col.dataIndex !== 'ip'); + + me.callParent(); + }, +}); diff --git a/www/manager6/sdn/fabrics/bgp/NodeEdit.js b/www/manager6/sdn/fabrics/bgp/NodeEdit.js new file mode 100644 index 00000000..1ad586df --- /dev/null +++ b/www/manager6/sdn/fabrics/bgp/NodeEdit.js @@ -0,0 +1,23 @@ +Ext.define('PVE.sdn.Fabric.Bgp.Node.Edit', { + extend: 'PVE.sdn.Fabric.Node.Edit', + protocol: 'bgp', + + extraRequestParams: { + protocol: 'bgp', + role: 'internal', + }, + + includeWireguardInterfaces: true, + + additionalItems: [ + { + xtype: 'proxmoxintegerfield', + fieldLabel: gettext('ASN'), + labelWidth: 120, + name: 'asn', + minValue: 1, + maxValue: 4294967295, + allowBlank: false, + }, + ], +}); -- 2.47.3