[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