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 C965A1FF146 for ; Tue, 12 May 2026 16:14:11 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 70C8416AAA; Tue, 12 May 2026 16:13:49 +0200 (CEST) From: Hannes Laimer To: pve-devel@lists.proxmox.com Subject: [PATCH pve-manager v4 6/7] ui: sdn: add BGP fabric support Date: Tue, 12 May 2026 16:13:04 +0200 Message-ID: <20260512141305.199664-7-h.laimer@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260512141305.199664-1-h.laimer@proxmox.com> References: <20260512141305.199664-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: 1778595079297 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: BQDOOVJB6P44FNBMOPNCMF6V4NBEPZVH X-Message-ID-Hash: BQDOOVJB6P44FNBMOPNCMF6V4NBEPZVH 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 | 67 +++++++++++++++++++ .../sdn/fabrics/bgp/InterfacePanel.js | 15 +++++ www/manager6/sdn/fabrics/bgp/NodeEdit.js | 23 +++++++ 7 files changed, 130 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 1ad77f3d..90d73ba1 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -347,6 +347,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 0362df01..6901b96f 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) { @@ -199,6 +200,10 @@ Ext.define('PVE.sdn.Fabric.View', { text: 'WireGuard', handler: 'addWireGuard', }, + { + text: 'BGP', + handler: 'addBgp', + }, ], }, addNodeButton, @@ -278,6 +283,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]; @@ -288,6 +294,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]; @@ -308,6 +315,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 4b3967a1..8004ff1b 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..c4a94a52 --- /dev/null +++ b/www/manager6/sdn/fabrics/bgp/FabricEdit.js @@ -0,0 +1,67 @@ +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'), + emptyText: gettext('IP Prefixes'), + deleteEmpty: true, + skipEmptyText: true, + }, + ], + + additionalAdvancedItems: [ + { + xtype: 'pveSDNRouteMapSelector', + name: 'route_map_in', + fieldLabel: gettext('Incoming Route Map'), + emptyText: gettext('Route Map'), + deleteEmpty: true, + skipEmptyText: true, + }, + { + xtype: 'pveSDNRouteMapSelector', + name: 'route_map_out', + fieldLabel: gettext('Outgoing Route Map'), + 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