Arthur Bied-Charreton <[email protected]> writes:
> Add CPU flag editor to the CPUTypeEdit component, using the VMCPUFlagSelector
> also used in the VM creation flow. By default, only show the CPU flags that
> are currently meant to be shown in the VM creation window, see [0]. When in
> CPUTypeEdit, show all available flags.
>
> For each flag in VMCPUFlagSelector, also display which node(s) it is available
> on to limit misconfigurations.
>
> Original patch:
> https://lore.proxmox.com/pve-devel/[email protected]
>
> [0]
> https://git.proxmox.com/?p=qemu-server.git;a=blob;f=src/PVE/QemuServer/CPUConfig.pm;h=32ec495422791422f20caa928d6b632b3de4fcd9;hb=refs/heads/master#l349
>
> Originally-by: Stefan Reiter <[email protected]>
> Signed-off-by: Arthur Bied-Charreton <[email protected]>
> ---
> www/manager6/dc/CPUTypeEdit.js | 11 ++-
> www/manager6/form/CPUModelSelector.js | 1 +
> www/manager6/form/VMCPUFlagSelector.js | 121 ++++++++++++++++++++++---
> 3 files changed, 117 insertions(+), 16 deletions(-)
>
> diff --git a/www/manager6/dc/CPUTypeEdit.js b/www/manager6/dc/CPUTypeEdit.js
> index 8cf508b4..d6e06601 100644
> --- a/www/manager6/dc/CPUTypeEdit.js
> +++ b/www/manager6/dc/CPUTypeEdit.js
> @@ -84,7 +84,16 @@ Ext.define('PVE.dc.CPUTypeEdit', {
> name: 'phys-bits',
> },
> ],
> -
> + columnB: [
> + {
> + xtype: 'vmcpuflagselector',
> + fieldLabel: gettext('Extra CPU flags'),
> + name: 'flags',
> + restrictToVMFlags: false,
> + height: 380,
> + hideHeaders: false,
> + },
> + ],
> },
> ],
> });
> diff --git a/www/manager6/form/CPUModelSelector.js
> b/www/manager6/form/CPUModelSelector.js
> index 2154ff46..683fa469 100644
> --- a/www/manager6/form/CPUModelSelector.js
> +++ b/www/manager6/form/CPUModelSelector.js
> @@ -17,6 +17,7 @@ Ext.define('PVE.form.CPUModelSelector', {
> anyMatch: true,
> forceSelection: true,
> autoSelect: false,
> + triggerAction: 'query',
>
> deleteEmpty: true,
> config: {
> diff --git a/www/manager6/form/VMCPUFlagSelector.js
> b/www/manager6/form/VMCPUFlagSelector.js
> index 74b1a2c4..06c9d9f1 100644
> --- a/www/manager6/form/VMCPUFlagSelector.js
> +++ b/www/manager6/form/VMCPUFlagSelector.js
> @@ -1,3 +1,19 @@
> +const VM_CPU_FLAGS_SUBSET = {
> + aes: true,
> + 'amd-no-ssb': true,
> + 'amd-ssbd': true,
> + 'hv-evmcs': true,
> + 'hv-tlbflush': true,
> + ibpb: true,
> + 'md-clear': true,
> + 'nested-virt': true,
> + pcid: true,
> + pdpe1gb: true,
> + 'spec-ctrl': true,
> + ssbd: true,
> + 'virt-ssbd': true,
> +};
> +
> Ext.define('PVE.form.VMCPUFlagSelector', {
> extend: 'Ext.grid.Panel',
> alias: 'widget.vmcpuflagselector',
> @@ -6,6 +22,10 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
> field: 'Ext.form.field.Field',
> },
>
> + config: {
> + restrictToVMFlags: true,
> + },
> +
> disableSelection: true,
> columnLines: false,
> selectable: false,
> @@ -17,27 +37,18 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
> unknownFlags: [],
>
> store: {
> - type: 'store',
> - fields: ['name', { name: 'state', defaultValue: '=' },
> 'description'],
> - autoLoad: true,
> + fields: ['name', { name: 'state', defaultValue: '=' },
> 'description', 'supported-on'],
> + autoLoad: false,
> proxy: {
> type: 'proxmox',
> url: '/api2/json/nodes/localhost/capabilities/qemu/cpu-flags',
> + extraParams: { accel: 'kvm' },
> },
> listeners: {
> update: function () {
> this.commitChanges();
> },
> - refresh: function (store, eOpts) {
> - let me = this;
> - let view = me.view;
> -
> - if (store.adjustedForValue !== view.value) {
> - view.adjustStoreForValue();
> - }
> - },
> },
> - adjustedForValue: undefined,
> },
>
> getValue: function () {
> @@ -86,14 +97,18 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
> let rec = store.findRecord('name', flagName, 0, false, true,
> true);
> if (rec !== null) {
> rec.set('state', sign);
> + rec.commit();
> } else {
> me.unknownFlags.push(flag);
> }
> });
>
> - store.adjustedForValue = value;
> + me.getView().refresh();
> + },
> + isDirty: function () {
> + let me = this;
> + return me.originalValue !== me.getValue();
> },
> -
> setValue: function (value) {
> let me = this;
>
> @@ -109,6 +124,7 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
> },
> columns: [
> {
> + text: gettext('State'),
> dataIndex: 'state',
> renderer: function (v) {
> switch (v) {
> @@ -125,6 +141,7 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
> width: 65,
> },
> {
> + text: gettext('Set'),
This would benefit from a TRANSLATORS comment. Is this a noun (set as in
set theory)? is it a verb (to set)? From the point of view of
translators there is not enough context to decide.
> xtype: 'widgetcolumn',
> dataIndex: 'state',
> width: 95,
> @@ -171,22 +188,96 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
> },
> },
> {
> + text: gettext('Flag'),
> dataIndex: 'name',
> width: 100,
> },
> {
> + text: gettext('Description'),
> dataIndex: 'description',
> + sortable: false,
> + cellWrap: true,
> + flex: 3,
> + },
> + {
> + text: gettext('Supported On'),
> + dataIndex: 'supported-on',
> cellWrap: true,
> flex: 1,
> + renderer: (v) => (Array.isArray(v) ? v.join(', ') : ''),
> },
> ],
>
> initComponent: function () {
> let me = this;
>
> + me.unknownFlags = [];
> me.value = me.originalValue = '';
> - me.store.view = me;
> +
> + me.dockedItems = [
> + {
> + xtype: 'toolbar',
> + dock: 'top',
> + items: [
> + {
> + xtype: 'tbtext',
> + text: gettext('Acceleration:'),
> + autoEl: {
> + tag: 'span',
> + 'data-qtip': gettext(
> + 'A custom CPU model using
> acceleration-specific flags should only be assigned to VMs configured with
> the matching acceleration type, i.e., `kvm: off` for TCG, or `kvm: on` for
> KVM.',
I would recommend to use quotes `"`, single-quotes `'` in user-facing
strings. Backticks are a markup/markdown concept. See [1].
off-topic: In principle one should use “” rather than "", but I don't
think there is precedent for this the codebase.
[1] https://en.wikipedia.org/wiki/Quotation_mark#In_English
> + ),
> + },
> + },
> + {
> + xtype: 'radiogroup',
> + layout: 'hbox',
> + validateOnChange: false,
> + items: [
> + {
> + boxLabel: 'KVM',
> + inputValue: 'kvm',
> + name: 'accel',
> + checked: true,
> + isFormField: false,
> + },
> + {
> + boxLabel: 'TCG',
> + inputValue: 'tcg',
> + name: 'accel',
> + margin: '0 0 0 10',
> + isFormField: false,
> + },
> + ],
> + listeners: {
> + change: function (_, value) {
> + let view = this.up('grid');
> + let proxy = view.getStore().getProxy();
> + let accel = value.accel;
> + if (accel) {
> + proxy.setExtraParam('accel', accel);
> + } else {
> + delete proxy.extraParams.accel;
> + }
> + view.getStore().load();
> + },
> + },
> + },
> + ],
> + },
> + ];
>
> me.callParent(arguments);
> +
> + me.getStore().on('load', function (store, _, success) {
> + if (success) {
> + if (me.restrictToVMFlags) {
> + store.filterBy((rec) =>
> VM_CPU_FLAGS_SUBSET[rec.get('name')] === true);
> + }
> + me.adjustStoreForValue();
> + me.checkDirty();
> + }
> + });
> + me.getStore().load();
> },
> });
--
Maximiliano