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 474CA1FF17E for ; Thu, 30 Oct 2025 16:50:25 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 51F2C276DF; Thu, 30 Oct 2025 16:49:12 +0100 (CET) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Date: Thu, 30 Oct 2025 16:48:39 +0100 Message-ID: <20251030154851.540408-33-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20251030154851.540408-1-s.hanreich@proxmox.com> References: <20251030154851.540408-1-s.hanreich@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.183 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 KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Subject: [pve-devel] [PATCH pve-manager 5/8] ui: resource tree: add network resource 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" From: Gabriel Goller Add the newly introduced network resource to the resource tree, so the the status of fabrics can be displayed in the UI. For this matter, a new NetworkBrowser widget is added, which is responsible for showing the contents of a network resource. The NetworkBrowser widget also contains code for handling the zone type, which is currently still contained in the sdn resource type. This ensures a smooth transition when moving the zones into the new network resource type. Co-authored-by: Stefan Hanreich Signed-off-by: Gabriel Goller Signed-off-by: Stefan Hanreich --- www/manager6/Makefile | 2 + www/manager6/Utils.js | 11 ++ www/manager6/Workspace.js | 1 + www/manager6/sdn/FabricsContentView.js | 77 ++++++++++++ www/manager6/sdn/NetworkBrowser.js | 167 +++++++++++++++++++++++++ www/manager6/tree/ResourceTree.js | 6 + 6 files changed, 264 insertions(+) create mode 100644 www/manager6/sdn/FabricsContentView.js create mode 100644 www/manager6/sdn/NetworkBrowser.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index 85f9268d1..ba762578e 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -278,6 +278,7 @@ JSSRC= \ qemu/USBEdit.js \ qemu/VirtiofsEdit.js \ sdn/Browser.js \ + sdn/NetworkBrowser.js \ sdn/ControllerView.js \ sdn/Status.js \ sdn/StatusView.js \ @@ -313,6 +314,7 @@ JSSRC= \ sdn/zones/VlanEdit.js \ sdn/zones/VxlanEdit.js \ sdn/FabricsView.js \ + sdn/FabricsContentView.js \ sdn/fabrics/Common.js \ sdn/fabrics/InterfacePanel.js \ sdn/fabrics/NodeEdit.js \ diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js index c48ee0b25..2f812a442 100644 --- a/www/manager6/Utils.js +++ b/www/manager6/Utils.js @@ -1276,6 +1276,12 @@ Ext.define('PVE.Utils', { // templates objType = 'template'; status = type; + } else if (type === 'network') { + const network_type_mapping = { + fabric: 'fa fa-road', + }; + + return network_type_mapping[record.network_type] ?? ''; } else if (type === 'storage' && record.content === 'import') { return 'fa fa-cloud-download'; } else { @@ -1299,6 +1305,11 @@ Ext.define('PVE.Utils', { var cls = PVE.Utils.get_object_icon_class(value, record.data); var fa = ' '; + + if (value === 'network') { + return fa + record.data.network_type; + } + return fa + value; }, diff --git a/www/manager6/Workspace.js b/www/manager6/Workspace.js index a25746582..9f1e807d5 100644 --- a/www/manager6/Workspace.js +++ b/www/manager6/Workspace.js @@ -250,6 +250,7 @@ Ext.define('PVE.StdWorkspace', { lxc: 'pveLXCConfig', storage: 'PVE.storage.Browser', sdn: 'PVE.sdn.Browser', + network: 'PVE.network.Browser', pool: 'pvePoolConfig', tag: 'pveTagConfig', }; diff --git a/www/manager6/sdn/FabricsContentView.js b/www/manager6/sdn/FabricsContentView.js new file mode 100644 index 000000000..47e8bce7f --- /dev/null +++ b/www/manager6/sdn/FabricsContentView.js @@ -0,0 +1,77 @@ +Ext.define('PVE.sdn.FabricRoutesContentView', { + extend: 'Ext.grid.GridPanel', + alias: 'widget.pveSDNFabricRoutesContentView', + + columns: [ + { + header: gettext('Route'), + sortable: true, + dataIndex: 'route', + flex: 1, + }, + { + header: gettext('Via'), + sortable: true, + dataIndex: 'via', + renderer: (value) => { + if (Ext.isArray(value)) { + return value.join('
'); + } + return value || ''; + }, + flex: 1, + }, + ], +}); + +Ext.define('PVE.sdn.FabricNeighborsContentView', { + extend: 'Ext.grid.GridPanel', + alias: 'widget.pveSDNFabricNeighborsContentView', + + columns: [ + { + header: gettext('Neighbor'), + sortable: true, + dataIndex: 'neighbor', + flex: 1, + }, + { + header: gettext('Status'), + sortable: true, + dataIndex: 'status', + flex: 0.5, + }, + { + header: gettext('Uptime'), + sortable: true, + dataIndex: 'uptime', + flex: 0.5, + }, + ], +}); + +Ext.define('PVE.sdn.FabricInterfacesContentView', { + extend: 'Ext.grid.GridPanel', + alias: 'widget.pveSDNFabricInterfacesContentView', + + columns: [ + { + header: gettext('Name'), + sortable: true, + dataIndex: 'name', + flex: 1, + }, + { + header: gettext('Type'), + sortable: true, + dataIndex: 'type', + flex: 1, + }, + { + header: gettext('State'), + sortable: true, + dataIndex: 'state', + flex: 1, + }, + ], +}); diff --git a/www/manager6/sdn/NetworkBrowser.js b/www/manager6/sdn/NetworkBrowser.js new file mode 100644 index 000000000..ad024e6a1 --- /dev/null +++ b/www/manager6/sdn/NetworkBrowser.js @@ -0,0 +1,167 @@ +Ext.define('PVE.network.Browser', { + extend: 'PVE.panel.Config', + alias: 'widget.PVE.network.Browser', + + initComponent: function () { + let me = this; + let data = me.pveSelNode.data; + + let node = data.node; + if (!node) { + throw 'no node name specified'; + } + + let name = data.network; + if (!name) { + throw 'no name specified'; + } + + let networkType = data.network_type; + if (!name) { + throw 'no type specified'; + } + + me.items = []; + + if (networkType === 'fabric') { + me.onlineHelp = 'pvesdn_config_fabrics'; + + me.items.push({ + nodename: node, + fabricId: name, + protocol: me.pveSelNode.data.protocol, + xtype: 'pveSDNFabricRoutesContentView', + title: gettext('Routes'), + iconCls: 'fa fa-exchange', + itemId: 'routes', + width: '100%', + store: { + proxy: { + type: 'proxmox', + url: `/api2/json/nodes/${node}/sdn/fabrics/${name}/routes`, + reader: { + type: 'json', + rootProperty: 'data', + }, + }, + autoLoad: true, + }, + }); + + me.items.push({ + nodename: node, + fabricId: name, + protocol: me.pveSelNode.data.protocol, + xtype: 'pveSDNFabricNeighborsContentView', + title: gettext('Neighbors'), + iconCls: 'fa fa-handshake-o', + itemId: 'neighbors', + width: '100%', + store: { + proxy: { + type: 'proxmox', + url: `/api2/json/nodes/${node}/sdn/fabrics/${name}/neighbors`, + reader: { + type: 'json', + rootProperty: 'data', + }, + }, + autoLoad: true, + }, + }); + + me.items.push({ + nodename: node, + fabricId: name, + protocol: me.pveSelNode.data.protocol, + xtype: 'pveSDNFabricInterfacesContentView', + title: gettext('Interfaces'), + iconCls: 'fa fa-upload', + itemId: 'interfaces', + width: '100%', + store: { + proxy: { + type: 'proxmox', + url: `/api2/json/nodes/${node}/sdn/fabrics/${name}/interfaces`, + reader: { + type: 'json', + rootProperty: 'data', + }, + }, + autoLoad: true, + }, + }); + } else if (networkType === 'zone') { + const caps = Ext.state.Manager.get('GuiCap'); + + me.items.push({ + nodename: node, + zone: name, + xtype: 'pveSDNZoneContentPanel', + title: gettext('Content'), + iconCls: 'fa fa-th', + itemId: 'content', + }); + + if (caps.sdn['Permissions.Modify']) { + me.items.push({ + xtype: 'pveACLView', + title: gettext('Permissions'), + iconCls: 'fa fa-unlock', + itemId: 'permissions', + path: `/sdn/zones/${name}`, + }); + } + + me.items.push({ + nodename: node, + zone: name, + xtype: 'pveSDNZoneBridgePanel', + title: gettext('Bridges'), + iconCls: 'fa fa-network-wired x-fa-sdn-treelist', + itemId: 'bridges', + }); + + if (data.zone_type && data.zone_type === 'evpn') { + me.items.push({ + nodename: node, + zone: name, + xtype: 'pveSDNEvpnZoneIpVrfPanel', + title: gettext('IP-VRF'), + iconCls: 'fa fa-th-list', + itemId: 'ip-vrf', + }); + + me.items.push({ + nodename: node, + zone: name, + xtype: 'pveSDNEvpnZoneMacVrfPanel', + title: gettext('MAC-VRFs'), + iconCls: 'fa fa-th-list', + itemId: 'mac-vrfs', + }); + } + } else { + me.items.push({ + xtype: 'container', + title: gettext('Content'), + iconCls: 'fa fa-th', + itemId: 'content', + html: `unknown network type: ${networkType}`, + width: '100%', + }); + } + + Ext.apply(me, { + title: Ext.String.format( + gettext('{0} {1} on node {2}'), + `${networkType}`, + `'${name}'`, + `'${node}'`, + ), + hstateid: 'networktab', + }); + + me.callParent(); + }, +}); diff --git a/www/manager6/tree/ResourceTree.js b/www/manager6/tree/ResourceTree.js index e83ccfc85..b6ab9a1e9 100644 --- a/www/manager6/tree/ResourceTree.js +++ b/www/manager6/tree/ResourceTree.js @@ -25,6 +25,10 @@ Ext.define('PVE.tree.ResourceTree', { iconCls: 'fa fa-th', text: gettext('SDN'), }, + network: { + iconCls: 'fa fa-globe', + text: gettext('Network'), + }, qemu: { iconCls: 'fa fa-desktop', text: gettext('Virtual Machine'), @@ -55,6 +59,8 @@ Ext.define('PVE.tree.ResourceTree', { return 2; case 'sdn': return 3; + case 'network': + return 3.5; case 'storage': return 4; default: -- 2.47.3 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel