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'),
             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.',
+                            ),
+                        },
+                    },
+                    {
+                        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();
     },
 });
-- 
2.47.3



Reply via email to