From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 6A0599E239 for ; Tue, 6 Jun 2023 16:01:06 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 521DB36B64 for ; Tue, 6 Jun 2023 16:01:06 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Tue, 6 Jun 2023 16:01:05 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 29B7948CF9 for ; Tue, 6 Jun 2023 15:52:28 +0200 (CEST) From: Dominik Csapak To: pve-devel@lists.proxmox.com Date: Tue, 6 Jun 2023 15:52:14 +0200 Message-Id: <20230606135222.984747-16-d.csapak@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20230606135222.984747-1-d.csapak@proxmox.com> References: <20230606135222.984747-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.015 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 SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pve-devel] [PATCH v5 07/15] ui: qemu/PCIEdit: rework panel to add a mapped configuration 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: , X-List-Received-Date: Tue, 06 Jun 2023 14:01:06 -0000 reworks the panel to use a controller, so that we can easily add the selector for mapped pci devices shows now a selection between 'raw' and 'mapped' devices, where 'raw' ones work like before, and 'mapped' ones take the values form the hardware map config Signed-off-by: Dominik Csapak --- www/manager6/qemu/PCIEdit.js | 323 ++++++++++++++++++++++++----------- 1 file changed, 219 insertions(+), 104 deletions(-) diff --git a/www/manager6/qemu/PCIEdit.js b/www/manager6/qemu/PCIEdit.js index 2f67aece..d98f410d 100644 --- a/www/manager6/qemu/PCIEdit.js +++ b/www/manager6/qemu/PCIEdit.js @@ -3,71 +3,164 @@ Ext.define('PVE.qemu.PCIInputPanel', { onlineHelp: 'qm_pci_passthrough_vm_config', - setVMConfig: function(vmconfig) { - var me = this; - me.vmconfig = vmconfig; + controller: { + xclass: 'Ext.app.ViewController', + + setVMConfig: function(vmconfig) { + let me = this; + let view = me.getView(); + me.vmconfig = vmconfig; - var hostpci = me.vmconfig[me.confid] || ''; + let hostpci = me.vmconfig[view.confid] || ''; - var values = PVE.Parser.parsePropertyString(hostpci, 'host'); - if (values.host) { - if (!values.host.match(/^[0-9a-f]{4}:/i)) { // add optional domain - values.host = "0000:" + values.host; + let values = PVE.Parser.parsePropertyString(hostpci, 'host'); + if (values.host) { + if (values.host.includes(':')) { + values.type = 'raw'; + if (!values.host.match(/^[0-9a-f]{4}:/i)) { // add optional domain + values.host = "0000:" + values.host; + } + if (values.host.length < 11) { // 0000:00:00 format not 0000:00:00.0 + values.host += ".0"; + values.multifunction = true; + } + } else { + values.hostmapped = values.host; + delete values.host; + values.type = 'mapped'; + } } - if (values.host.length < 11) { // 0000:00:00 format not 0000:00:00.0 - values.host += ".0"; - values.multifunction = true; + + values['x-vga'] = PVE.Parser.parseBoolean(values['x-vga'], 0); + values.pcie = PVE.Parser.parseBoolean(values.pcie, 0); + values.rombar = PVE.Parser.parseBoolean(values.rombar, 1); + + view.setValues(values); + if (!me.vmconfig.machine || me.vmconfig.machine.indexOf('q35') === -1) { + // machine is not set to some variant of q35, so we disable pcie + let pcie = me.lookup('pcie'); + pcie.setDisabled(true); + pcie.setBoxLabel(gettext('Q35 only')); } - } - values['x-vga'] = PVE.Parser.parseBoolean(values['x-vga'], 0); - values.pcie = PVE.Parser.parseBoolean(values.pcie, 0); - values.rombar = PVE.Parser.parseBoolean(values.rombar, 1); + if (values.romfile) { + me.lookup('romfile').setVisible(true); + } + }, - me.setValues(values); - if (!me.vmconfig.machine || me.vmconfig.machine.indexOf('q35') === -1) { - // machine is not set to some variant of q35, so we disable pcie - var pcie = me.down('field[name=pcie]'); - pcie.setDisabled(true); - pcie.setBoxLabel(gettext('Q35 only')); - } + selectorEnable: function(selector) { + let me = this; + me.pciDevChange(selector, selector.getValue()); + }, - if (values.romfile) { - me.down('field[name=romfile]').setVisible(true); - } - }, + pciDevChange: function(pcisel, value) { + let me = this; + let mdevfield = me.lookup('mdev'); + if (!value) { + if (!pcisel.isDisabled()) { + mdevfield.setDisabled(true); + } + return; + } + let pciDev = pcisel.getStore().getById(value); - onGetValues: function(values) { - let me = this; - if (!me.confid) { - for (let i = 0; i < PVE.Utils.hardware_counts.hostpci; i++) { - if (!me.vmconfig['hostpci' + i.toString()]) { - me.confid = 'hostpci' + i.toString(); - break; + mdevfield.setDisabled(!pciDev || !pciDev.data.mdev); + if (!pciDev) { + return; + } + + let path = value; + if (pciDev.data.map) { + // find local mapping + for (const entry of pciDev.data.map) { + let mapping = PVE.Parser.parsePropertyString(entry); + if (mapping.node === pcisel.up('inputpanel').nodename) { + path = mapping.path.split(';')[0]; + break; + } + } + if (path.indexOf('.') === -1) { + path += '.0'; } } - // FIXME: what if no confid was found?? - } - values.host.replace(/^0000:/, ''); // remove optional '0000' domain - if (values.multifunction) { - values.host = values.host.substring(0, values.host.indexOf('.')); // skip the '.X' - delete values.multifunction; - } + if (pciDev.data.mdev) { + mdevfield.setPciID(path); + } + if (pcisel.reference === 'selector') { + let iommu = pciDev.data.iommugroup; + if (iommu === -1) { + return; + } + // try to find out if there are more devices in that iommu group + let id = path.substring(0, 5); // 00:00 + let count = 0; + pcisel.getStore().each(({ data }) => { + if (data.iommugroup === iommu && data.id.substring(0, 5) !== id) { + count++; + return false; + } + return true; + }); + me.lookup('group_warning').setVisible(count > 0); + } + }, - if (values.rombar) { - delete values.rombar; - } else { - values.rombar = 0; - } + onGetValues: function(values) { + let me = this; + let view = me.getView(); + if (!view.confid) { + for (let i = 0; i < PVE.Utils.hardware_counts.hostpci; i++) { + if (!me.vmconfig['hostpci' + i.toString()]) { + view.confid = 'hostpci' + i.toString(); + break; + } + } + // FIXME: what if no confid was found?? + } - if (!values.romfile) { - delete values.romfile; - } + if (values.hostmapped) { + values.host = values.hostmapped; + delete values.hostmapped; + } else { + values.host.replace(/^0000:/, ''); // remove optional '0000' domain + + if (values.multifunction) { + values.host = values.host.substring(0, values.host.indexOf('.')); // skip the '.X' + delete values.multifunction; + } + } + + if (values.rombar) { + delete values.rombar; + } else { + values.rombar = 0; + } + + if (!values.romfile) { + delete values.romfile; + } + + delete values.type; + + let ret = {}; + ret[view.confid] = PVE.Parser.printPropertyString(values, 'host'); + return ret; + }, + }, - let ret = {}; - ret[me.confid] = PVE.Parser.printPropertyString(values, 'host'); - return ret; + viewModel: { + data: { + isMapped: true, + }, + }, + + setVMConfig: function(vmconfig) { + return this.getController().setVMConfig(vmconfig); + }, + + onGetValues: function(values) { + return this.getController().onGetValues(values); }, initComponent: function() { @@ -78,78 +171,97 @@ Ext.define('PVE.qemu.PCIInputPanel', { throw "no node name specified"; } + me.columnT = [ + { + xtype: 'displayfield', + reference: 'iommu_warning', + hidden: true, + columnWidth: 1, + padding: '0 0 10 0', + value: 'No IOMMU detected, please activate it.' + + 'See Documentation for further information.', + userCls: 'pmx-hint', + }, + { + xtype: 'displayfield', + reference: 'group_warning', + hidden: true, + columnWidth: 1, + padding: '0 0 10 0', + itemId: 'iommuwarning', + value: 'The selected Device is not in a seperate IOMMU group, make sure this is intended.', + userCls: 'pmx-hint', + }, + ]; + me.column1 = [ + { + xtype: 'radiofield', + name: 'type', + inputValue: 'mapped', + boxLabel: gettext('Mapped Device'), + bind: { + value: '{isMapped}', + }, + }, + { + xtype: 'pvePCIMapSelector', + fieldLabel: gettext('Device'), + reference: 'mapped_selector', + name: 'hostmapped', + labelAlign: 'right', + nodename: me.nodename, + allowBlank: false, + bind: { + disabled: '{!isMapped}', + }, + listeners: { + change: 'pciDevChange', + enable: 'selectorEnable', + }, + }, + { + xtype: 'radiofield', + name: 'type', + inputValue: 'raw', + checked: true, + boxLabel: gettext('Raw Device'), + }, { xtype: 'pvePCISelector', fieldLabel: gettext('Device'), name: 'host', + reference: 'selector', nodename: me.nodename, + labelAlign: 'right', allowBlank: false, + disabled: true, + bind: { + disabled: '{isMapped}', + }, onLoadCallBack: function(store, records, success) { if (!success || !records.length) { return; } - if (records.every((val) => val.data.iommugroup === -1)) { // no IOMMU groups - let warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - value: 'No IOMMU detected, please activate it.' + - 'See Documentation for further information.', - userCls: 'pmx-hint', - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } + me.lookup('iommu_warning').setVisible( + records.every((val) => val.data.iommugroup === -1), + ); }, listeners: { - change: function(pcisel, value) { - if (!value) { - return; - } - let pciDev = pcisel.getStore().getById(value); - let mdevfield = me.down('field[name=mdev]'); - mdevfield.setDisabled(!pciDev || !pciDev.data.mdev); - if (!pciDev) { - return; - } - if (pciDev.data.mdev) { - mdevfield.setPciID(value); - } - let iommu = pciDev.data.iommugroup; - if (iommu === -1) { - return; - } - // try to find out if there are more devices in that iommu group - let id = pciDev.data.id.substring(0, 5); // 00:00 - let count = 0; - pcisel.getStore().each(({ data }) => { - if (data.iommugroup === iommu && data.id.substring(0, 5) !== id) { - count++; - return false; - } - return true; - }); - let warning = me.down('#iommuwarning'); - if (count && !warning) { - warning = Ext.create('Ext.form.field.Display', { - columnWidth: 1, - padding: '0 0 10 0', - itemId: 'iommuwarning', - value: 'The selected Device is not in a seperate IOMMU group, make sure this is intended.', - userCls: 'pmx-hint', - }); - me.items.insert(0, warning); - me.updateLayout(); // insert does not trigger that - } else if (!count && warning) { - me.remove(warning); - } - }, + change: 'pciDevChange', + enable: 'selectorEnable', }, }, { xtype: 'proxmoxcheckbox', fieldLabel: gettext('All Functions'), + reference: 'all_functions', + disabled: true, + labelAlign: 'right', name: 'multifunction', + bind: { + disabled: '{isMapped}', + }, }, ]; @@ -157,6 +269,7 @@ Ext.define('PVE.qemu.PCIInputPanel', { { xtype: 'pveMDevSelector', name: 'mdev', + reference: 'mdev', disabled: true, fieldLabel: gettext('MDev Type'), nodename: me.nodename, @@ -188,6 +301,7 @@ Ext.define('PVE.qemu.PCIInputPanel', { submitValue: true, hidden: true, fieldLabel: 'ROM-File', + reference: 'romfile', name: 'romfile', }, { @@ -214,6 +328,7 @@ Ext.define('PVE.qemu.PCIInputPanel', { { xtype: 'proxmoxcheckbox', fieldLabel: 'PCI-Express', + reference: 'pcie', name: 'pcie', }, { -- 2.30.2