pve-manager/www/manager6/qemu/PCIEdit.js
Dominik Csapak b1d42186d1 ui: qemu/PCIEdit: rework panel to add a mapped configuration
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 <d.csapak@proxmox.com>
2023-06-16 16:25:42 +02:00

378 lines
8.1 KiB
JavaScript

Ext.define('PVE.qemu.PCIInputPanel', {
extend: 'Proxmox.panel.InputPanel',
onlineHelp: 'qm_pci_passthrough_vm_config',
controller: {
xclass: 'Ext.app.ViewController',
setVMConfig: function(vmconfig) {
let me = this;
let view = me.getView();
me.vmconfig = vmconfig;
let hostpci = me.vmconfig[view.confid] || '';
let 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;
}
if (values.host.length < 11) { // 0000:00:00 format not 0000:00:00.0
values.host += ".0";
values.multifunction = true;
}
values.type = 'raw';
} else if (values.mapping) {
values.type = 'mapped';
}
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'));
}
if (values.romfile) {
me.lookup('romfile').setVisible(true);
}
},
selectorEnable: function(selector) {
let me = this;
me.pciDevChange(selector, selector.getValue());
},
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);
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';
}
}
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);
}
},
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??
}
values.host?.replace(/^0000:/, ''); // remove optional '0000' domain
if (values.multifunction && values.host) {
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;
},
},
viewModel: {
data: {
isMapped: true,
},
},
setVMConfig: function(vmconfig) {
return this.getController().setVMConfig(vmconfig);
},
onGetValues: function(values) {
return this.getController().onGetValues(values);
},
initComponent: function() {
let me = this;
me.nodename = me.pveSelNode.data.node;
if (!me.nodename) {
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: 'mapping',
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;
}
me.lookup('iommu_warning').setVisible(
records.every((val) => val.data.iommugroup === -1),
);
},
listeners: {
change: 'pciDevChange',
enable: 'selectorEnable',
},
},
{
xtype: 'proxmoxcheckbox',
fieldLabel: gettext('All Functions'),
reference: 'all_functions',
disabled: true,
labelAlign: 'right',
name: 'multifunction',
bind: {
disabled: '{isMapped}',
},
},
];
me.column2 = [
{
xtype: 'pveMDevSelector',
name: 'mdev',
reference: 'mdev',
disabled: true,
fieldLabel: gettext('MDev Type'),
nodename: me.nodename,
listeners: {
change: function(field, value) {
let multiFunction = me.down('field[name=multifunction]');
if (value) {
multiFunction.setValue(false);
}
multiFunction.setDisabled(!!value);
},
},
},
{
xtype: 'proxmoxcheckbox',
fieldLabel: gettext('Primary GPU'),
name: 'x-vga',
},
];
me.advancedColumn1 = [
{
xtype: 'proxmoxcheckbox',
fieldLabel: 'ROM-Bar',
name: 'rombar',
},
{
xtype: 'displayfield',
submitValue: true,
hidden: true,
fieldLabel: 'ROM-File',
reference: 'romfile',
name: 'romfile',
},
{
xtype: 'textfield',
name: 'vendor-id',
fieldLabel: Ext.String.format(gettext('{0} ID'), gettext('Vendor')),
emptyText: gettext('From Device'),
vtype: 'PciId',
allowBlank: true,
submitEmpty: false,
},
{
xtype: 'textfield',
name: 'device-id',
fieldLabel: Ext.String.format(gettext('{0} ID'), gettext('Device')),
emptyText: gettext('From Device'),
vtype: 'PciId',
allowBlank: true,
submitEmpty: false,
},
];
me.advancedColumn2 = [
{
xtype: 'proxmoxcheckbox',
fieldLabel: 'PCI-Express',
reference: 'pcie',
name: 'pcie',
},
{
xtype: 'textfield',
name: 'sub-vendor-id',
fieldLabel: Ext.String.format(gettext('{0} ID'), gettext('Sub-Vendor')),
emptyText: gettext('From Device'),
vtype: 'PciId',
allowBlank: true,
submitEmpty: false,
},
{
xtype: 'textfield',
name: 'sub-device-id',
fieldLabel: Ext.String.format(gettext('{0} ID'), gettext('Sub-Device')),
emptyText: gettext('From Device'),
vtype: 'PciId',
allowBlank: true,
submitEmpty: false,
},
];
me.callParent();
},
});
Ext.define('PVE.qemu.PCIEdit', {
extend: 'Proxmox.window.Edit',
subject: gettext('PCI Device'),
vmconfig: undefined,
isAdd: true,
initComponent: function() {
let me = this;
me.isCreate = !me.confid;
let ipanel = Ext.create('PVE.qemu.PCIInputPanel', {
confid: me.confid,
pveSelNode: me.pveSelNode,
});
Ext.apply(me, {
items: [ipanel],
});
me.callParent();
me.load({
success: ({ result }) => ipanel.setVMConfig(result.data),
});
},
});