[pve-devel] [PATCH v5 13/15] ui: allow configuring pci and usb mapping

Dominik Csapak d.csapak at proxmox.com
Tue Jun 6 15:52:20 CEST 2023


uses the new ResourceMapTree to add the CRUD interfaces for the
mappings.

We add both of them into a single panel, since the datacenter menu
already has many entries, and without a proper summary for the group, we
cannot really put them in a category

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
 www/manager6/Makefile         |   2 +
 www/manager6/StateProvider.js |   1 +
 www/manager6/dc/Config.js     |  45 ++++++++++++++-
 www/manager6/dc/PCIMapView.js | 106 ++++++++++++++++++++++++++++++++++
 www/manager6/dc/USBMapView.js |  98 +++++++++++++++++++++++++++++++
 5 files changed, 250 insertions(+), 2 deletions(-)
 create mode 100644 www/manager6/dc/PCIMapView.js
 create mode 100644 www/manager6/dc/USBMapView.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 938ec9f1..c78a42a7 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -173,6 +173,8 @@ JSSRC= 							\
 	dc/MetricServerView.js				\
 	dc/UserTagAccessEdit.js				\
 	dc/RegisteredTagsEdit.js			\
+	dc/PCIMapView.js				\
+	dc/USBMapView.js				\
 	lxc/CmdMenu.js					\
 	lxc/Config.js					\
 	lxc/CreateWizard.js				\
diff --git a/www/manager6/StateProvider.js b/www/manager6/StateProvider.js
index fafbb112..d62d33b2 100644
--- a/www/manager6/StateProvider.js
+++ b/www/manager6/StateProvider.js
@@ -47,6 +47,7 @@ Ext.define('PVE.StateProvider', {
     hprefix: 'v1',
 
     compDict: {
+	'resource-map': 55,
         tfa: 54,
 	sdn: 53,
 	cloudinit: 52,
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 13ded12e..40c7bf1b 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -255,8 +255,49 @@ Ext.define('PVE.dc.Config', {
 		iconCls: 'fa fa-bar-chart',
 		itemId: 'metricservers',
 		onlineHelp: 'external_metric_server',
-	    },
-	    {
+	    });
+	}
+
+	if (caps.mapping['Mapping.Audit'] ||
+	    caps.mapping['Mapping.Use'] ||
+	    caps.mapping['Mapping.Modify']) {
+	    me.items.push(
+		{
+		    xtype: 'container',
+		    title: gettext('Resource Mappings'),
+		    itemId: 'resources',
+		    iconCls: 'fa fa-folder-o',
+		    layout: {
+			type: 'vbox',
+			align: 'stretch',
+			multi: true,
+		    },
+		    scrollable: true,
+		    defaults: {
+			collapsible: true,
+			animCollapse: false,
+			margin: '7 10 3 10',
+		    },
+		    items: [
+			{
+			    collapsible: true,
+			    xtype: 'pveDcPCIMapView',
+			    title: gettext('PCI Devices'),
+			    flex: 1,
+			},
+			{
+			    collapsible: true,
+			    xtype: 'pveDcUSBMapView',
+			    title: gettext('USB Devices'),
+			    flex: 1,
+			},
+		    ],
+		},
+	    );
+	}
+
+	if (caps.dc['Sys.Audit']) {
+	    me.items.push({
 		xtype: 'pveDcSupport',
 		title: gettext('Support'),
 		itemId: 'support',
diff --git a/www/manager6/dc/PCIMapView.js b/www/manager6/dc/PCIMapView.js
new file mode 100644
index 00000000..3efa19d8
--- /dev/null
+++ b/www/manager6/dc/PCIMapView.js
@@ -0,0 +1,106 @@
+Ext.define('pve-resource-pci-tree', {
+    extend: 'Ext.data.Model',
+    idProperty: 'internalId',
+    fields: ['type', 'text', 'path', 'id', 'subsystem-id', 'iommugroup', 'description', 'digest'],
+});
+
+Ext.define('PVE.dc.PCIMapView', {
+    extend: 'PVE.tree.ResourceMapTree',
+    alias: 'widget.pveDcPCIMapView',
+
+    editWindowClass: 'PVE.window.PCIMapEditWindow',
+    baseUrl: '/cluster/mapping/pci',
+    mapIconCls: 'pve-itype-icon-pci',
+    getStatusCheckUrl: (node) => `/nodes/${node}/hardware/pci?pci-class-blacklist=`,
+    entryIdProperty: 'path',
+
+    checkValidity: function(data, node) {
+	let me = this;
+	let ids = {};
+	data.forEach((entry) => {
+	    ids[entry.id] = entry;
+	});
+	me.getRootNode()?.cascade(function(rec) {
+	    if (rec.data.node !== node || rec.data.type !== 'map') {
+		return;
+	    }
+
+	    let id = rec.data.path;
+	    if (!id.match(/\.\d$/)) {
+		id += '.0';
+	    }
+	    let device = ids[id];
+	    if (!device) {
+		rec.set('valid', 0);
+		rec.set('errmsg', Ext.String.format(gettext("Cannot find PCI id {0}"), id));
+		rec.commit();
+		return;
+	    }
+
+
+	    let deviceId = `${device.vendor}:${device.device}`.replace(/0x/g, '');
+	    let subId = `${device.subsystem_vendor}:${device.subsystem_device}`.replace(/0x/g, '');
+
+	    let toCheck = {
+		id: deviceId,
+		'subsystem-id': subId,
+		iommugroup: device.iommugroup !== -1 ? device.iommugroup : undefined,
+	    };
+
+	    let valid = 1;
+	    let errors = [];
+	    let errText = gettext("Configuration for {0} not correct ('{1}' != '{2}')");
+	    for (const [key, validValue] of Object.entries(toCheck)) {
+		if (`${rec.data[key]}` !== `${validValue}`) {
+		    errors.push(Ext.String.format(errText, key, rec.data[key] ?? '', validValue));
+		    valid = 0;
+		}
+	    }
+
+	    rec.set('valid', valid);
+	    rec.set('errmsg', errors.join('<br>'));
+	    rec.commit();
+	});
+    },
+
+    store: {
+	sorters: 'text',
+	model: 'pve-resource-pci-tree',
+	data: {},
+    },
+
+    columns: [
+	{
+	    xtype: 'treecolumn',
+	    text: gettext('ID/Node/Path'),
+	    dataIndex: 'text',
+	    width: 200,
+	},
+	{
+	    text: gettext('Vendor/Device'),
+	    dataIndex: 'id',
+	},
+	{
+	    text: gettext('Subsystem Vendor/Device'),
+	    dataIndex: 'subsystem-id',
+	},
+	{
+	    text: gettext('IOMMU group'),
+	    dataIndex: 'iommugroup',
+	},
+	{
+	    header: gettext('Status'),
+	    dataIndex: 'valid',
+	    flex: 1,
+	    renderer: 'renderStatus',
+	},
+	{
+	    header: gettext('Comment'),
+	    dataIndex: 'description',
+	    renderer: function(value, _meta, record) {
+		return value ?? record.data.comment;
+	    },
+	    flex: 1,
+	},
+    ],
+});
diff --git a/www/manager6/dc/USBMapView.js b/www/manager6/dc/USBMapView.js
new file mode 100644
index 00000000..953e2425
--- /dev/null
+++ b/www/manager6/dc/USBMapView.js
@@ -0,0 +1,98 @@
+Ext.define('pve-resource-usb-tree', {
+    extend: 'Ext.data.Model',
+    idProperty: 'internalId',
+    fields: ['type', 'text', 'path', 'id', 'description', 'digest'],
+});
+
+Ext.define('PVE.dc.USBMapView', {
+    extend: 'PVE.tree.ResourceMapTree',
+    alias: 'widget.pveDcUSBMapView',
+
+    editWindowClass: 'PVE.window.USBMapEditWindow',
+    baseUrl: '/cluster/mapping/usb',
+    mapIconCls: 'fa fa-usb',
+    getStatusCheckUrl: (node) => `/nodes/${node}/hardware/usb`,
+    entryIdProperty: 'id',
+
+    checkValidity: function(data, node) {
+	let me = this;
+	let ids = {};
+	let paths = {};
+	data.forEach((entry) => {
+	    ids[`${entry.vendid}:${entry.prodid}`] = entry;
+	    paths[`${entry.busnum}-${entry.usbpath}`] = entry;
+	});
+	me.getRootNode()?.cascade(function(rec) {
+	    if (rec.data.node !== node || rec.data.type !== 'map') {
+		return;
+	    }
+
+	    let device;
+	    if (rec.data.path) {
+		device = paths[rec.data.path];
+	    }
+	    device ??= ids[rec.data.id];
+
+	    if (!device) {
+		rec.set('valid', 0);
+		rec.set('errmsg', Ext.String.format(gettext("Cannot find USB device {0}"), rec.data.id));
+		rec.commit();
+		return;
+	    }
+
+
+	    let deviceId = `${device.vendid}:${device.prodid}`.replace(/0x/g, '');
+
+	    let toCheck = {
+		id: deviceId,
+	    };
+
+	    let valid = 1;
+	    let errors = [];
+	    let errText = gettext("Configuration for {0} not correct ('{1}' != '{2}')");
+	    for (const [key, validValue] of Object.entries(toCheck)) {
+		if (rec.data[key] !== validValue) {
+		    errors.push(Ext.String.format(errText, key, rec.data[key] ?? '', validValue));
+		    valid = 0;
+		}
+	    }
+
+	    rec.set('valid', valid);
+	    rec.set('errmsg', errors.join('<br>'));
+	    rec.commit();
+	});
+    },
+
+    store: {
+	sorters: 'text',
+	model: 'pve-resource-usb-tree',
+	data: {},
+    },
+
+    columns: [
+	{
+	    xtype: 'treecolumn',
+	    text: gettext('ID/Node/Vendor&Device'),
+	    dataIndex: 'text',
+	    width: 200,
+	},
+	{
+	    text: gettext('Path'),
+	    dataIndex: 'path',
+	},
+	{
+	    header: gettext('Status'),
+	    dataIndex: 'valid',
+	    flex: 1,
+	    renderer: 'renderStatus',
+	},
+	{
+	    header: gettext('Comment'),
+	    dataIndex: 'description',
+	    renderer: function(value, _meta, record) {
+		return value ?? record.data.comment;
+	    },
+	    flex: 1,
+	},
+    ],
+});
-- 
2.30.2






More information about the pve-devel mailing list