pve-manager/www/manager6/form/MultiPCISelector.js
Dominik Csapak aafc1f3040 ui: multi pci selector: reset the selection properly on nodename change
and mdev change.

giving an empty array did not have the desired effect of resetting the
selection, but giving no parameter at all does.

this now also clears the selection when the mdev filter/config changed
(was just forgotten)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
2023-06-19 13:05:17 +02:00

290 lines
5.6 KiB
JavaScript

Ext.define('PVE.form.MultiPCISelector', {
extend: 'Ext.grid.Panel',
alias: 'widget.pveMultiPCISelector',
emptyText: gettext('No Devices found'),
mixins: {
field: 'Ext.form.field.Field',
},
getValue: function() {
let me = this;
return me.value ?? [];
},
getSubmitData: function() {
let me = this;
let res = {};
res[me.name] = me.getValue();
return res;
},
setValue: function(value) {
let me = this;
value ??= [];
me.updateSelectedDevices(value);
return me.mixins.field.setValue.call(me, value);
},
getErrors: function() {
let me = this;
let errorCls = ['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid'];
if (me.getValue().length < 1) {
let error = gettext("Must choose at least one device");
me.addCls(errorCls);
me.getActionEl()?.dom.setAttribute('data-errorqtip', error);
return [error];
}
me.removeCls(errorCls);
me.getActionEl()?.dom.setAttribute('data-errorqtip', "");
return [];
},
viewConfig: {
getRowClass: function(record) {
if (record.data.disabled === true) {
return 'x-item-disabled';
}
return '';
},
},
updateSelectedDevices: function(value = []) {
let me = this;
let recs = [];
let store = me.getStore();
for (const map of value) {
let parsed = PVE.Parser.parsePropertyString(map);
if (parsed.node !== me.nodename) {
continue;
}
let rec = store.getById(parsed.path);
if (rec) {
recs.push(rec);
}
}
me.suspendEvent('change');
me.setSelection();
me.setSelection(recs);
me.resumeEvent('change');
},
setNodename: function(nodename) {
let me = this;
if (!nodename || me.nodename === nodename) {
return;
}
me.nodename = nodename;
me.getStore().setProxy({
type: 'proxmox',
url: '/api2/json/nodes/' + me.nodename + '/hardware/pci?pci-class-blacklist=',
});
me.setSelection();
me.getStore().load({
callback: (recs, op, success) => me.addSlotRecords(recs, op, success),
});
},
setMdev: function(mdev) {
let me = this;
if (mdev) {
me.getStore().addFilter({
id: 'mdev-filter',
property: 'mdev',
value: '1',
operator: '=',
});
} else {
me.getStore().removeFilter('mdev-filter');
}
me.setSelection();
},
// adds the virtual 'slot' records (e.g. '0000:01:00') to the store
addSlotRecords: function(records, _op, success) {
let me = this;
if (!success) {
return;
}
let slots = {};
records.forEach((rec) => {
let slotname = rec.data.id.slice(0, -2); // remove function
rec.set('slot', slotname);
if (slots[slotname] !== undefined) {
slots[slotname].count++;
return;
}
slots[slotname] = {
count: 1,
};
if (rec.data.id.endsWith('.0')) {
slots[slotname].device = rec.data;
}
});
let store = me.getStore();
for (const [slot, { count, device }] of Object.entries(slots)) {
if (count === 1) {
continue;
}
store.add(Ext.apply({}, {
id: slot,
mdev: undefined,
device_name: gettext('Pass through all functions as one device'),
}, device));
}
me.updateSelectedDevices(me.value);
},
selectionChange: function(_grid, selection) {
let me = this;
let ids = {};
selection
.filter(rec => rec.data.id.indexOf('.') === -1)
.forEach((rec) => { ids[rec.data.id] = true; });
let to_disable = [];
me.getStore().each(rec => {
let id = rec.data.id;
rec.set('disabled', false);
if (id.indexOf('.') === -1) {
return;
}
let slot = id.slice(0, -2); // remove function
if (ids[slot]) {
to_disable.push(rec);
rec.set('disabled', true);
}
});
me.suspendEvent('selectionchange');
me.getSelectionModel().deselect(to_disable);
me.resumeEvent('selectionchange');
me.value = me.getSelection().map((rec) => {
let res = {
path: rec.data.id,
node: me.nodename,
id: `${rec.data.vendor}:${rec.data.device}`.replace(/0x/g, ''),
'subsystem-id': `${rec.data.subsystem_vendor}:${rec.data.subsystem_device}`.replace(/0x/g, ''),
};
if (rec.data.iommugroup !== -1) {
res.iommugroup = rec.data.iommugroup;
}
return PVE.Parser.printPropertyString(res);
});
me.checkChange();
},
selModel: {
type: 'checkboxmodel',
mode: 'SIMPLE',
},
columns: [
{
header: 'ID',
dataIndex: 'id',
width: 150,
},
{
header: gettext('IOMMU Group'),
dataIndex: 'iommugroup',
renderer: (v, _md, rec) => rec.data.slot === rec.data.id ? '' : v === -1 ? '-' : v,
width: 50,
},
{
header: gettext('Vendor'),
dataIndex: 'vendor_name',
flex: 3,
},
{
header: gettext('Device'),
dataIndex: 'device_name',
flex: 6,
},
{
header: gettext('Mediated Devices'),
dataIndex: 'mdev',
flex: 1,
renderer: function(val) {
return Proxmox.Utils.format_boolean(!!val);
},
},
],
listeners: {
selectionchange: function() {
this.selectionChange(...arguments);
},
},
store: {
fields: [
'id', 'vendor_name', 'device_name', 'vendor', 'device', 'iommugroup', 'mdev',
'subsystem_vendor', 'subsystem_device', 'disabled',
{
name: 'subsystem-vendor',
calculate: function(data) {
return data.subsystem_vendor;
},
},
{
name: 'subsystem-device',
calculate: function(data) {
return data.subsystem_device;
},
},
],
sorters: [
{
property: 'id',
direction: 'ASC',
},
],
},
initComponent: function() {
let me = this;
let nodename = me.nodename;
me.nodename = undefined;
me.callParent();
Proxmox.Utils.monStoreErrors(me, me.getStore(), true);
me.setNodename(nodename);
me.initField();
},
});