[pve-devel] [PATCH pve-manager 2/2] fabrics: add resource view for fabrics

Gabriel Goller g.goller at proxmox.com
Wed Aug 13 15:30:14 CEST 2025


When clicking on the fabric resources a new content view is available.
It shows the routes and the neighbors of the fabric on that specific
node.

Signed-off-by: Gabriel Goller <g.goller at proxmox.com>
---
 www/manager6/Makefile                  |   1 +
 www/manager6/sdn/Browser.js            | 120 ++++++++++++++++++++-----
 www/manager6/sdn/FabricsContentView.js |  91 +++++++++++++++++++
 www/manager6/sdn/StatusView.js         |   2 +-
 4 files changed, 191 insertions(+), 23 deletions(-)
 create mode 100644 www/manager6/sdn/FabricsContentView.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 07401f21520b..8b4d672f5145 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -313,6 +313,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/sdn/Browser.js b/www/manager6/sdn/Browser.js
index f7694ae91864..af82bd6390d3 100644
--- a/www/manager6/sdn/Browser.js
+++ b/www/manager6/sdn/Browser.js
@@ -15,39 +15,115 @@ Ext.define('PVE.sdn.Browser', {
         if (!sdnId) {
             throw 'no sdn ID specified';
         }
+        let sdnType = me.pveSelNode.data.sdn_type;
+        if (!sdnType) {
+            throw 'no sdn object type specified';
+        }
 
         me.items = [];
 
+        const caps = Ext.state.Manager.get('GuiCap');
+
+        switch (sdnType) {
+            case 'zone':
+                me.items.push({
+                    nodename: nodename,
+                    zone: sdnId,
+                    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/${sdnId}`,
+                    });
+                }
+                break;
+            case 'fabric':
+                {
+                    let neighborStore = new Ext.data.Store({
+                        model: 'Neighbor',
+                        proxy: {
+                            type: 'proxmox',
+                            url: '/api2/json/cluster/sdn/fabrics/neighbors',
+                            reader: {
+                                type: 'json',
+                                rootProperty: 'data',
+                            },
+                            extraParams: {
+                                node: nodename,
+                            },
+                        },
+                        autoLoad: true,
+                    });
+
+                    let routeStore = new Ext.data.Store({
+                        model: 'Route',
+                        proxy: {
+                            type: 'proxmox',
+                            url: '/api2/json/cluster/sdn/fabrics/routes',
+                            reader: {
+                                type: 'json',
+                                rootProperty: 'data',
+                            },
+                            extraParams: {
+                                node: nodename,
+                            },
+                        },
+                        autoLoad: true,
+                    });
+
+                    me.items.push({
+                        nodename: nodename,
+                        routeStore: routeStore,
+                        fabricId: sdnId,
+                        protocol: me.pveSelNode.data.protocol,
+                        xtype: 'pveSDNFabricRoutesContentView',
+                        title: gettext('Routes'),
+                        iconCls: 'fa fa-th',
+                        itemId: 'routes',
+                        width: '100%',
+                    });
+                    me.items.push({
+                        nodename: nodename,
+                        neighborStore: neighborStore,
+                        fabricId: sdnId,
+                        protocol: me.pveSelNode.data.protocol,
+                        xtype: 'pveSDNFabricNeighborsContentView',
+                        title: gettext('Neighbors'),
+                        iconCls: 'fa fa-th',
+                        itemId: 'neighbors',
+                        width: '100%',
+                    });
+                }
+                break;
+        }
+
         Ext.apply(me, {
             title: Ext.String.format(
-                gettext('Zone {0} on node {1}'),
+                gettext('{0} {1} on node {2}'),
+                `${sdnType}`,
                 `'${sdnId}'`,
                 `'${nodename}'`,
             ),
             hstateid: 'sdntab',
         });
 
-        const caps = Ext.state.Manager.get('GuiCap');
-
-        me.items.push({
-            nodename: nodename,
-            zone: sdnId,
-            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/${sdnId}`,
-            });
-        }
-
         me.callParent();
     },
 });
+
+Ext.define('Route', {
+    extend: 'Ext.data.Model',
+    fields: ['route', 'via', 'fabric_id', 'protocol'],
+});
+Ext.define('Neighbor', {
+    extend: 'Ext.data.Model',
+    fields: ['neighbor', 'status', 'fabric_id', 'protocol'],
+});
diff --git a/www/manager6/sdn/FabricsContentView.js b/www/manager6/sdn/FabricsContentView.js
new file mode 100644
index 000000000000..f1e5ec146b8b
--- /dev/null
+++ b/www/manager6/sdn/FabricsContentView.js
@@ -0,0 +1,91 @@
+Ext.define('PVE.sdn.FabricRoutesContentView', {
+    extend: 'Ext.grid.GridPanel',
+    alias: 'widget.pveSDNFabricRoutesContentView',
+
+    initComponent: function () {
+        let me = this;
+        let sm = Ext.create('Ext.selection.RowModel', {});
+
+        me.routeStore.addFilter([
+            {
+                property: 'fabric_id',
+                value: me.fabricId,
+            },
+            {
+                property: 'protocol',
+                value: me.protocol,
+            },
+        ]);
+        me.routeStore.sort('route', 'ASC');
+
+        Ext.apply(me, {
+            store: me.routeStore,
+            selModel: sm,
+            columns: [
+                {
+                    header: 'Route',
+                    sortable: true,
+                    dataIndex: 'route',
+                    flex: 1,
+                },
+                {
+                    header: 'Via',
+                    sortable: true,
+                    dataIndex: 'via',
+                    renderer: (value) => {
+                        if (Ext.isArray(value)) {
+                            return value.join('<br>');
+                        }
+                        return value || '';
+                    },
+                    flex: 1,
+                },
+            ],
+        });
+
+        me.callParent();
+    },
+});
+
+Ext.define('PVE.sdn.FabricNeighborsContentView', {
+    extend: 'Ext.grid.GridPanel',
+    alias: 'widget.pveSDNFabricNeighborsContentView',
+
+    initComponent: function () {
+        let me = this;
+        let sm = Ext.create('Ext.selection.RowModel', {});
+
+        me.neighborStore.addFilter([
+            {
+                property: 'fabric_id',
+                value: me.fabricId,
+            },
+            {
+                property: 'protocol',
+                value: me.protocol,
+            },
+        ]);
+        me.neighborStore.sort('neighbor', 'ASC');
+
+        Ext.apply(me, {
+            store: me.neighborStore,
+            selModel: sm,
+            columns: [
+                {
+                    header: 'Neighbor',
+                    sortable: true,
+                    dataIndex: 'neighbor',
+                    flex: 1,
+                },
+                {
+                    header: 'Status',
+                    sortable: true,
+                    dataIndex: 'status',
+                    flex: 0.5,
+                },
+            ],
+        });
+
+        me.callParent();
+    },
+});
diff --git a/www/manager6/sdn/StatusView.js b/www/manager6/sdn/StatusView.js
index dd05c73fdfcf..e66e3f624354 100644
--- a/www/manager6/sdn/StatusView.js
+++ b/www/manager6/sdn/StatusView.js
@@ -102,7 +102,7 @@ Ext.define(
     function () {
         Ext.define('pve-sdn-status', {
             extend: 'Ext.data.Model',
-            fields: ['id', 'type', 'node', 'status', 'sdn'],
+            fields: ['id', 'type', 'node', 'status', 'sdn', 'sdn_type'],
             idProperty: 'id',
         });
     },
-- 
2.47.2





More information about the pve-devel mailing list