[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