public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models
@ 2026-05-15  9:28 Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-docs v5 01/21] qm: add anchor to "CPU Type" section Arthur Bied-Charreton
                   ` (21 more replies)
  0 siblings, 22 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

This picks up and extends an old series [0] by Stefan Reiter.

This series adds a full CRUD API and a UI editor for custom CPU models
which allows users to manage them in the Datacenter interface rather
than editing /etc/pve/virtual-guest/cpu-models.conf manually.

It also improves on the existing VM CPU flags selector by providing a
list of nodes supporting each flag to help gauge cluster compatibility.

qemu-server gets a new CPUFlags module grouping cpu-flags-related
helpers. A new helper (query_available_cpu_flags) is introduced, which
derives the set of flags accepted by QEMU as -cpu arguments and
annotates each with the cluster nodes that report supporting it. The
existing node-level endpoint is extended with an accel parameter to
filter flags by acceleration type. Its default behavior is preserved
modulo the added 'supported-on' field per flag in the return value.

pve-manager gets two new cluster-level endpoints:

1. /cluster/qemu/cpu-flags queries flags available across the cluster
   and reports which node supports each of them. It differs from the
   node-level endpoint in qemu-server in that it returns *all* flags,
   as opposed to the VM-specific ones.

2. /cluster/qemu/custom-cpu-models provides full CRUD for custom CPU
   model definitions.

The UI gets a "Custom CPU models" view in the Datacenter panel (at the
bottom under "Guest Resources/Hardware") with an editor for model
properties. The flags can be filtered by acceleration type when
creating/editing a custom CPU model. In the ProcessorEdit window, a new
checkbox is added that allows setting the acceleration type in the VM
config.


Future work:

Future UX improvements could make acceleration-specific flags in custom
CPU models more explicit. Since custom CPU models have no configuration
field indicating whether they are meant to be used with KVM or TCG, the
UI always defaults to KVM, which would be quite annoying for a user
with a custom model meant to be used only with TCG.


Dependencies:

 Build:
  pve-manager requires bumped pve-doc-generator
  pve-manager requires bumped qemu-server

 Runtime:
  pve-manager requires bumped pve-docs
  pve-manager requires bumped qemu-server


Changes since v1 (thanks @Fiona):
https://lore.proxmox.com/pve-devel/20260401080028.62513-1-a.bied-charreton@proxmox.com/

Changes since v2:
https://lore.proxmox.com/pve-devel/20260430153505.527032-1-a.bied-charreton@proxmox.com/

Changes since v3:
https://lore.proxmox.com/pve-devel/20260430160109.565536-1-a.bied-charreton@proxmox.com/

Changes since v4 (thanks @David, @Fiona, @Thomas):

 pve-docs:
  - Change "CPU Type" anchor to _cpu_type

 qemu-server:
  - Use PVE::File instead of PVE::Tools for file_get_contents
  - Improve $understood_cpu_flag_dir doc comment in CPUFlags
  - Sort the cpu flag names only once at module load in CPUFlags
  - Normalize CPU flag names to QEMU's format
  - Compare booleans against JSON::true in query_supported_cpu_flags
  - Use postfix dereference style
  - Rename the *_custom_cpu_config helpers
  - Register the custom CPU config standard option as
    'pve-qm-custom-cpu-model'
  - Improve arch description in cpu-flags endpoint
  - Remove the added explicit type field in the CPU models config
  - Fix some broken formatting and perlcritic complaints
  - Avoid redundant config loads by get_custom_model
  - Rephrase doc comment of query_available_cpu_flags from "... flags
    that will be accepted by QEMU as -cpu arguments" to "... by PVE
    in processor configs", since the list includes 'nested-virt'

 pve-manager:
  - Change permissions for new cluster-wide endpoints to Sys.Audit/
    Sys.Modify
  - Use check_config in create and update custom CPU model CRUD
    operations
  - Use delete_from_config for deleting fields
  - Remove assert_if_modified from POST and DELETE endpoints
  - Require base model instead of defaulting to kvm64 for new custom
    models
  - Use plain panel for the new datacenter grouping
  - Avoid using private Ext.js APIs (BufferedRenderer.refreshView) when
    refreshing the CPU flag grid after editing
  - Replace the in-flag selector acceleration radio with a dedicated
    "KVM hardware virtualization" checkbox in VM ProcessorEdit, which
    now also persists in the VM config

[0] https://lore.proxmox.com/pve-devel/20211028114150.3245864-1-s.reiter@proxmox.com/


pve-docs:

Arthur Bied-Charreton (1):
  qm: add anchor to "CPU Type" section

 qm.adoc | 1 +
 1 file changed, 1 insertion(+)


qemu-server:

Arthur Bied-Charreton (10):
  cpu config: rename CPU models config path variable
  cpu flags: move cpu flags-related utilities to their own module
  cpu flags: compare against JSON::true when querying supported flags
  cpu flags: normalize CPU flags to QEMU's format
  cpu flags: add helper querying CPU flags with nodes supporting them
  cpu config: rename custom CPU model config loader
  cpu config: add helpers to lock and write config
  cpu: register standard option for CPU format
  api: cpu flags: improve flags list returned by endpoint
  custom cpu models: avoid redundant config load

 src/PVE/API2/Qemu/CPUFlags.pm        |  26 ++-
 src/PVE/QemuServer.pm                |  33 +--
 src/PVE/QemuServer/CPUConfig.pm      | 117 +++-------
 src/PVE/QemuServer/CPUFlags.pm       | 308 +++++++++++++++++++++++++++
 src/PVE/QemuServer/Makefile          |   1 +
 src/test/run_config2command_tests.pl |   2 +-
 6 files changed, 365 insertions(+), 122 deletions(-)
 create mode 100644 src/PVE/QemuServer/CPUFlags.pm


pve-manager:

Arthur Bied-Charreton (10):
  cluster: reorder imports
  cluster: makefile: reorder perl sources and align backslashes
  api: add endpoint querying available CPU flags cluster-wide
  api: add CRUD handlers for custom CPU models
  ui: cpu model selector: allow filtering out custom models
  ui: add basic custom CPU model editor
  ui: cpu flags selector: add CPU flag editor for custom models
  ui: cpu flags selector: allow filtering out flags supported on 0 nodes
  ui: cpu flags selector: add search bar for large lists of flags
  ui: group custom CPU with resource mappings

 PVE/API2/Cluster.pm                      |   9 +-
 PVE/API2/Cluster/Makefile                |  12 +-
 PVE/API2/Cluster/Qemu.pm                 |  47 +++++
 PVE/API2/Cluster/Qemu/CPUFlags.pm        |  68 +++++++
 PVE/API2/Cluster/Qemu/CustomCPUModels.pm | 204 ++++++++++++++++++++
 PVE/API2/Cluster/Qemu/Makefile           |  18 ++
 www/manager6/Makefile                    |   3 +
 www/manager6/dc/CPUTypeEdit.js           | 153 +++++++++++++++
 www/manager6/dc/CPUTypeView.js           | 150 +++++++++++++++
 www/manager6/dc/Config.js                |  27 +++
 www/manager6/form/CPUModelSelector.js    |  11 ++
 www/manager6/form/PhysBitsSelector.js    | 153 +++++++++++++++
 www/manager6/form/VMCPUFlagSelector.js   | 231 +++++++++++++++++++----
 www/manager6/qemu/ProcessorEdit.js       |  42 +++++
 14 files changed, 1090 insertions(+), 38 deletions(-)
 create mode 100644 PVE/API2/Cluster/Qemu.pm
 create mode 100644 PVE/API2/Cluster/Qemu/CPUFlags.pm
 create mode 100644 PVE/API2/Cluster/Qemu/CustomCPUModels.pm
 create mode 100644 PVE/API2/Cluster/Qemu/Makefile
 create mode 100644 www/manager6/dc/CPUTypeEdit.js
 create mode 100644 www/manager6/dc/CPUTypeView.js
 create mode 100644 www/manager6/form/PhysBitsSelector.js


Summary over all repositories:
  21 files changed, 1456 insertions(+), 160 deletions(-)

-- 
Generated by murpp 0.11.0



^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH pve-docs v5 01/21] qm: add anchor to "CPU Type" section
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 02/21] cpu config: rename CPU models config path variable Arthur Bied-Charreton
                   ` (20 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Allows frontend components to link directly to the "CPU Type" section,
instead of "CPU", which may be too broad for some usecases.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 qm.adoc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/qm.adoc b/qm.adoc
index e6b7918..b1e4777 100644
--- a/qm.adoc
+++ b/qm.adoc
@@ -441,6 +441,7 @@ list can contain numbers but also number ranges. For example, the *affinity*
 `0-1,8-11` (expanded `0, 1, 8, 9, 10, 11`) would allow the VM to run on only
 these six specific host cores.
 
+[[_cpu_type]]
 CPU Type
 ^^^^^^^^
 
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH qemu-server v5 02/21] cpu config: rename CPU models config path variable
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-docs v5 01/21] qm: add anchor to "CPU Type" section Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 03/21] cpu flags: move cpu flags-related utilities to their own module Arthur Bied-Charreton
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Change $default_filename to $cpu_models_filename to be more
descriptive of the contents of the file.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
---
 src/PVE/QemuServer/CPUConfig.pm | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/PVE/QemuServer/CPUConfig.pm b/src/PVE/QemuServer/CPUConfig.pm
index fb9af277..7adfdf45 100644
--- a/src/PVE/QemuServer/CPUConfig.pm
+++ b/src/PVE/QemuServer/CPUConfig.pm
@@ -38,15 +38,15 @@ if (PVE::Cluster::check_cfs_is_mounted(1)) {
     mkdir "/etc/pve/virtual-guest";
 }
 
-my $default_filename = "virtual-guest/cpu-models.conf";
+my $cpu_models_filename = "virtual-guest/cpu-models.conf";
 cfs_register_file(
-    $default_filename,
+    $cpu_models_filename,
     sub { PVE::QemuServer::CPUConfig->parse_config(@_); },
     sub { PVE::QemuServer::CPUConfig->write_config(@_); },
 );
 
 sub load_custom_model_conf {
-    return cfs_read_file($default_filename);
+    return cfs_read_file($cpu_models_filename);
 }
 
 #builtin models : reported-model is mandatory
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH qemu-server v5 03/21] cpu flags: move cpu flags-related utilities to their own module
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-docs v5 01/21] qm: add anchor to "CPU Type" section Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 02/21] cpu config: rename CPU models config path variable Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 04/21] cpu flags: compare against JSON::true when querying supported flags Arthur Bied-Charreton
                   ` (18 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Introduce PVE::QemuServer::CPUFlags module as the new home for cpu
flags-related helpers, and move various utilities from QemuServer
and CPUConfig into it.

`query_supported_cpu_flags` is not yet moved to the new module, as it is
trickier to migrate without creating circular dependencies.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/API2/Qemu/CPUFlags.pm   |   4 +-
 src/PVE/QemuServer.pm           |  24 +-----
 src/PVE/QemuServer/CPUConfig.pm |  89 ++-------------------
 src/PVE/QemuServer/CPUFlags.pm  | 137 ++++++++++++++++++++++++++++++++
 src/PVE/QemuServer/Makefile     |   1 +
 5 files changed, 149 insertions(+), 106 deletions(-)
 create mode 100644 src/PVE/QemuServer/CPUFlags.pm

diff --git a/src/PVE/API2/Qemu/CPUFlags.pm b/src/PVE/API2/Qemu/CPUFlags.pm
index 672bd2d2..4b409a40 100644
--- a/src/PVE/API2/Qemu/CPUFlags.pm
+++ b/src/PVE/API2/Qemu/CPUFlags.pm
@@ -6,7 +6,7 @@ use PVE::JSONSchema qw(get_standard_option);
 use PVE::RESTHandler;
 use PVE::Tools qw(extract_param);
 
-use PVE::QemuServer::CPUConfig;
+use PVE::QemuServer::CPUFlags;
 
 use base qw(PVE::RESTHandler);
 
@@ -44,7 +44,7 @@ __PACKAGE__->register_method({
 
         my $arch = extract_param($param, 'arch');
 
-        return PVE::QemuServer::CPUConfig::get_supported_cpu_flags($arch);
+        return PVE::QemuServer::CPUFlags::get_supported_cpu_flags($arch);
     },
 });
 
diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm
index a894684a..e58dddec 100644
--- a/src/PVE/QemuServer.pm
+++ b/src/PVE/QemuServer.pm
@@ -2916,9 +2916,10 @@ sub vga_conf_has_spice {
     return $1 || 1;
 }
 
-# To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
-# to use in a QEMU command line (-cpu element), first array_intersect the result
-# of query_supported_ with query_understood_. This is necessary because:
+# To use query_supported_cpu_flags and query_understood_cpu_flags (moved to the
+# PVE::QemuServer::CPUFlags module) to get flags to use in a QEMU command line
+# (-cpu element), first array_intersect the result of query_supported_ with
+# query_understood_. This is necessary because:
 #
 # a) query_understood_ returns flags the host cannot use and
 # b) query_supported_ (rather the QMP call) doesn't actually return CPU
@@ -3025,23 +3026,6 @@ sub query_supported_cpu_flags {
     return $flags;
 }
 
-# Understood CPU flags are written to a file at 'pve-qemu' compile time
-my $understood_cpu_flag_dir = "/usr/share/kvm";
-
-sub query_understood_cpu_flags {
-    my $arch = get_host_arch();
-    my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
-
-    die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
-        if !-e $filepath;
-
-    my $raw = file_get_contents($filepath);
-    $raw =~ s/^\s+|\s+$//g;
-    my @flags = split(/\s+/, $raw);
-
-    return \@flags;
-}
-
 # Since commit 277d33454f77ec1d1e0bc04e37621e4dd2424b67 in pve-qemu, smm is not off by default
 # anymore. But smm=off seems to be required when using SeaBIOS and serial display.
 my sub should_disable_smm {
diff --git a/src/PVE/QemuServer/CPUConfig.pm b/src/PVE/QemuServer/CPUConfig.pm
index 7adfdf45..0ee9b991 100644
--- a/src/PVE/QemuServer/CPUConfig.pm
+++ b/src/PVE/QemuServer/CPUConfig.pm
@@ -12,6 +12,7 @@ use PVE::RESTEnvironment qw(log_warn);
 use PVE::Tools qw(run_command);
 
 use PVE::QemuServer::Helpers qw(min_version get_host_arch);
+use PVE::QemuServer::CPUFlags qw(cpu_flag_supported_re cpu_flag_any_re supported_cpu_flags_names);
 
 use base qw(PVE::SectionConfig Exporter);
 
@@ -261,89 +262,9 @@ sub get_all_cpu_models {
     return $all_cpu_models;
 }
 
-my $supported_cpu_flags_by_arch = {
-    x86_64 => [
-        {
-            name => 'nested-virt',
-            description =>
-                "Controls nested virtualization, namely 'svm' for AMD CPUs and 'vmx' for"
-                . " Intel CPUs. Live migration still only works if it's the same flag on both sides."
-                . " Use a CPU model similar to the host, with the same vendor, not x86-64-vX!",
-        },
-        {
-            name => 'md-clear',
-            description => "Required to let the guest OS know if MDS is mitigated correctly.",
-        },
-        {
-            name => 'pcid',
-            description =>
-                "Meltdown fix cost reduction on Westmere, Sandy-, and IvyBridge Intel CPUs.",
-        },
-        {
-            name => 'spec-ctrl',
-            description => "Allows improved Spectre mitigation with Intel CPUs.",
-        },
-        {
-            name => 'ssbd',
-            description => "Protection for 'Speculative Store Bypass' for Intel models.",
-        },
-        {
-            name => 'ibpb',
-            description => "Allows improved Spectre mitigation with AMD CPUs.",
-        },
-        {
-            name => 'virt-ssbd',
-            description => "Basis for 'Speculative Store Bypass' protection for AMD models.",
-        },
-        {
-            name => 'amd-ssbd',
-            description =>
-                "Improves Spectre mitigation performance with AMD CPUs, best used with"
-                . " 'virt-ssbd'.",
-        },
-        {
-            name => 'amd-no-ssb',
-            description =>
-                "Notifies guest OS that host is not vulnerable for Spectre on AMD CPUs.",
-        },
-        {
-            name => 'pdpe1gb',
-            description => "Allow guest OS to use 1GB size pages, if host HW supports it.",
-        },
-        {
-            name => 'hv-tlbflush',
-            description =>
-                "Improve performance in overcommitted Windows guests. May lead to guest"
-                . " bluescreens on old CPUs.",
-        },
-        {
-            name => 'hv-evmcs',
-            description =>
-                "Improve performance for nested virtualization. Only supported on Intel" . " CPUs.",
-        },
-        {
-            name => 'aes',
-            description => "Activate AES instruction set for HW acceleration.",
-        },
-    ],
-    aarch64 => [],
-};
-
-sub get_supported_cpu_flags {
-    my ($arch) = @_;
-    $arch = get_host_arch() if !defined($arch);
-    return $supported_cpu_flags_by_arch->{$arch};
-}
-
-my $all_supported_cpu_flags = {};
-for my $arch ($supported_cpu_flags_by_arch->%*) {
-    for my $flag ($supported_cpu_flags_by_arch->{$arch}->@*) {
-        $all_supported_cpu_flags->{ $flag->{name} } = 1;
-    }
-}
-my @supported_cpu_flags_names = sort keys $all_supported_cpu_flags->%*;
-my $cpu_flag_supported_re = qr/([+-])(@{[join('|', @supported_cpu_flags_names)]})/;
-my $cpu_flag_any_re = qr/([+-])([a-zA-Z0-9\-_\.]+)/;
+my $cpu_flag_supported_re = cpu_flag_supported_re();
+my $cpu_flag_any_re = cpu_flag_any_re();
+my @supported_cpu_flags_names = (supported_cpu_flags_names());
 
 our $qemu_cmdline_cpu_re = qr/^((?>[+-]?[\w\-\._=]+,?)+)$/;
 
@@ -388,7 +309,7 @@ my $cpu_fmt = {
             . " controls nested virtualization for the current CPU ('svm' for AMD and 'vmx' for"
             . " Intel). Custom CPU models can specify any flag supported by QEMU/KVM, VM-specific"
             . " flags must be from the following set for security reasons: "
-            . join(', ', @supported_cpu_flags_names),
+            . join(', ', PVE::QemuServer::CPUFlags::supported_cpu_flags_names()),
         format_description => '+FLAG[;-FLAG...]',
         type => 'string',
         pattern => qr/$cpu_flag_any_re(;$cpu_flag_any_re)*/,
diff --git a/src/PVE/QemuServer/CPUFlags.pm b/src/PVE/QemuServer/CPUFlags.pm
new file mode 100644
index 00000000..a681eb75
--- /dev/null
+++ b/src/PVE/QemuServer/CPUFlags.pm
@@ -0,0 +1,137 @@
+package PVE::QemuServer::CPUFlags;
+
+use v5.36;
+
+use Exporter qw(import);
+
+use PVE::Cluster;
+use PVE::File;
+use PVE::QemuServer::Helpers qw(get_host_arch);
+
+our @EXPORT_OK = qw(
+    cpu_flag_supported_re
+    cpu_flag_any_re
+    supported_cpu_flags_names
+    get_supported_cpu_flags
+    query_understood_cpu_flags
+);
+
+my $supported_vm_specific_cpu_flags_by_arch = {
+    x86_64 => [
+        {
+            name => 'nested-virt',
+            description =>
+                "Controls nested virtualization, namely 'svm' for AMD CPUs and 'vmx' for"
+                . " Intel CPUs. Live migration still only works if it's the same flag on both sides."
+                . " Use a CPU model similar to the host, with the same vendor, not x86-64-vX!",
+        },
+        {
+            name => 'md-clear',
+            description => "Required to let the guest OS know if MDS is mitigated correctly.",
+        },
+        {
+            name => 'pcid',
+            description =>
+                "Meltdown fix cost reduction on Westmere, Sandy-, and IvyBridge Intel CPUs.",
+        },
+        {
+            name => 'spec-ctrl',
+            description => "Allows improved Spectre mitigation with Intel CPUs.",
+        },
+        {
+            name => 'ssbd',
+            description => "Protection for 'Speculative Store Bypass' for Intel models.",
+        },
+        {
+            name => 'ibpb',
+            description => "Allows improved Spectre mitigation with AMD CPUs.",
+        },
+        {
+            name => 'virt-ssbd',
+            description => "Basis for 'Speculative Store Bypass' protection for AMD models.",
+        },
+        {
+            name => 'amd-ssbd',
+            description =>
+                "Improves Spectre mitigation performance with AMD CPUs, best used with"
+                . " 'virt-ssbd'.",
+        },
+        {
+            name => 'amd-no-ssb',
+            description =>
+                "Notifies guest OS that host is not vulnerable for Spectre on AMD CPUs.",
+        },
+        {
+            name => 'pdpe1gb',
+            description => "Allow guest OS to use 1GB size pages, if host HW supports it.",
+        },
+        {
+            name => 'hv-tlbflush',
+            description =>
+                "Improve performance in overcommitted Windows guests. May lead to guest"
+                . " bluescreens on old CPUs.",
+        },
+        {
+            name => 'hv-evmcs',
+            description =>
+                "Improve performance for nested virtualization. Only supported on Intel" . " CPUs.",
+        },
+        {
+            name => 'aes',
+            description => "Activate AES instruction set for HW acceleration.",
+        },
+    ],
+    aarch64 => [],
+};
+
+my $all_supported_vm_specific_cpu_flags = {};
+for my $arch ($supported_vm_specific_cpu_flags_by_arch->%*) {
+    for my $flag ($supported_vm_specific_cpu_flags_by_arch->{$arch}->@*) {
+        $all_supported_vm_specific_cpu_flags->{ $flag->{name} } = 1;
+    }
+}
+
+my @supported_cpu_flags_name_sorted = sort keys $all_supported_vm_specific_cpu_flags->%*;
+
+# Understood CPU flags are written to a file at 'pve-qemu' compile time and
+# shipped below this directory by the pve-qemu-kvm package.
+my $understood_cpu_flag_dir = "/usr/share/kvm";
+
+sub supported_cpu_flags_names() {
+    return @supported_cpu_flags_name_sorted;
+}
+
+sub cpu_flag_supported_re() {
+    return qr/([+-])(@{[join('|', supported_cpu_flags_names())]})/;
+}
+
+sub cpu_flag_any_re() {
+    return qr/([+-])([a-zA-Z0-9\-_\.]+)/;
+}
+
+=head3 get_supported_cpu_flags($arch)
+
+Return supported VM-specific CPU flags for $arch. $arch defaults to the host architecture
+if C<undef>.
+
+=cut
+
+sub get_supported_cpu_flags($arch) {
+    $arch = get_host_arch() if !defined($arch);
+    return $supported_vm_specific_cpu_flags_by_arch->{$arch};
+}
+
+sub query_understood_cpu_flags($arch) {
+    my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
+
+    die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
+        if !-e $filepath;
+
+    my $raw = PVE::File::file_get_contents($filepath);
+    $raw =~ s/^\s+|\s+$//g;
+    my @flags = split(/\s+/, $raw);
+
+    return \@flags;
+}
+
+1;
diff --git a/src/PVE/QemuServer/Makefile b/src/PVE/QemuServer/Makefile
index 821556ef..060fac23 100644
--- a/src/PVE/QemuServer/Makefile
+++ b/src/PVE/QemuServer/Makefile
@@ -9,6 +9,7 @@ SOURCES=Agent.pm	\
 	CGroup.pm	\
 	Cloudinit.pm	\
 	CPUConfig.pm	\
+	CPUFlags.pm	\
 	DBusVMState.pm	\
 	Drive.pm	\
 	DriveDevice.pm	\
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH qemu-server v5 04/21] cpu flags: compare against JSON::true when querying supported flags
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (2 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH qemu-server v5 03/21] cpu flags: move cpu flags-related utilities to their own module Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 05/21] cpu flags: normalize CPU flags to QEMU's format Arthur Bied-Charreton
                   ` (17 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/QemuServer.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm
index e58dddec..57123e56 100644
--- a/src/PVE/QemuServer.pm
+++ b/src/PVE/QemuServer.pm
@@ -2991,7 +2991,7 @@ sub query_supported_cpu_flags {
 
             my $props = $cmd_result->{model}->{props};
             foreach my $prop (keys %$props) {
-                next if $props->{$prop} ne '1';
+                next if $props->{$prop} ne JSON::true;
                 # QEMU returns some flags multiple times, with '_', '.' or '-'
                 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
                 # We only keep those with underscores, to match /proc/cpuinfo
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH qemu-server v5 05/21] cpu flags: normalize CPU flags to QEMU's format
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (3 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH qemu-server v5 04/21] cpu flags: compare against JSON::true when querying supported flags Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 06/21] cpu flags: add helper querying CPU flags with nodes supporting them Arthur Bied-Charreton
                   ` (16 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

The QMP commands used in query_supported_cpu_flags() return some flags
multiple times in different formats, like for example 'sse4.2', 'sse4-2'
and 'sse4_2'.

In order to have a unified format, add the normalize_cpu_flag helper to
the CPUFlags module, which returns the alias QEMU has for a given flag
value. The list is sourced from x86_cpu_initfn() in
qemu/target/i386/cpu.c.

Suggested-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/QemuServer.pm          |  7 ++---
 src/PVE/QemuServer/CPUFlags.pm | 53 ++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm
index 57123e56..af55c322 100644
--- a/src/PVE/QemuServer.pm
+++ b/src/PVE/QemuServer.pm
@@ -69,6 +69,7 @@ use PVE::QemuServer::CPUConfig qw(
     get_intel_tdx_object
     get_cvm_type
 );
+use PVE::QemuServer::CPUFlags;
 use PVE::QemuServer::Drive qw(
     is_valid_drivename
     checked_volume_format
@@ -2992,10 +2993,8 @@ sub query_supported_cpu_flags {
             my $props = $cmd_result->{model}->{props};
             foreach my $prop (keys %$props) {
                 next if $props->{$prop} ne JSON::true;
-                # QEMU returns some flags multiple times, with '_', '.' or '-'
-                # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
-                # We only keep those with underscores, to match /proc/cpuinfo
-                $prop =~ s/\.|-/_/g;
+                $prop = PVE::QemuServer::CPUFlags::normalize_cpu_flag($prop);
+
                 $flags->{$prop} = 1;
             }
         };
diff --git a/src/PVE/QemuServer/CPUFlags.pm b/src/PVE/QemuServer/CPUFlags.pm
index a681eb75..e17ed00c 100644
--- a/src/PVE/QemuServer/CPUFlags.pm
+++ b/src/PVE/QemuServer/CPUFlags.pm
@@ -14,6 +14,7 @@ our @EXPORT_OK = qw(
     supported_cpu_flags_names
     get_supported_cpu_flags
     query_understood_cpu_flags
+    normalize_cpu_flag
 );
 
 my $supported_vm_specific_cpu_flags_by_arch = {
@@ -93,6 +94,58 @@ for my $arch ($supported_vm_specific_cpu_flags_by_arch->%*) {
 
 my @supported_cpu_flags_name_sorted = sort keys $all_supported_vm_specific_cpu_flags->%*;
 
+# qemu/target/i386/cpu.c, x86_cpu_initfn()
+my $qemu_cpu_flag_alias_map = {
+    sse3 => 'pni',
+    pclmuldq => 'pclmulqdq',
+    'sse4-1' => 'sse4.1',
+    'sse4-2' => 'sse4.2',
+    xd => 'nx',
+    ffxsr => 'fxsr-opt',
+    i64 => 'lm',
+    ds_cpl => 'ds-cpl',
+    tsc_adjust => 'tsc-adjust',
+    fxsr_opt => 'fxsr-opt',
+    lahf_lm => 'lahf-lm',
+    cmp_legacy => 'cmp-legacy',
+    nodeid_msr => 'nodeid-msr',
+    perfctr_core => 'perfctr-core',
+    perfctr_nb => 'perfctr-nb',
+    kvm_nopiodelay => 'kvm-nopiodelay',
+    kvm_mmu => 'kvm-mmu',
+    kvm_asyncpf => 'kvm-asyncpf',
+    kvm_asyncpf_int => 'kvm-asyncpf-int',
+    kvm_steal_time => 'kvm-steal-time',
+    kvm_pv_eoi => 'kvm-pv-eoi',
+    kvm_pv_unhalt => 'kvm-pv-unhalt',
+    kvm_poll_control => 'kvm-poll-control',
+    svm_lock => 'svm-lock',
+    nrip_save => 'nrip-save',
+    tsc_scale => 'tsc-scale',
+    vmcb_clean => 'vmcb-clean',
+    pause_filter => 'pause-filter',
+    sse4_1 => 'sse4.1',
+    sse4_2 => 'sse4.2',
+    'hv-apicv' => 'hv-avic',
+    lbr_fmt => 'lbr-fmt',
+};
+
+=head3 normalize_cpu_flag($flag)
+
+Normalize a CPU flag to its QEMU form.
+
+QEMU defines aliases for some CPU flags (see C<x86_cpu_initfn()> in
+C<target/i386/cpu.c>). For example, C<sse4_2> and C<sse4-2> are both aliases for
+C<sse4.2>.
+
+If C<$flag> has a known alias, return that, otherwise return C<$flag> unchanged.
+
+=cut
+
+sub normalize_cpu_flag($flag) {
+    return $qemu_cpu_flag_alias_map->{$flag} // $flag;
+}
+
 # Understood CPU flags are written to a file at 'pve-qemu' compile time and
 # shipped below this directory by the pve-qemu-kvm package.
 my $understood_cpu_flag_dir = "/usr/share/kvm";
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH qemu-server v5 06/21] cpu flags: add helper querying CPU flags with nodes supporting them
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (4 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH qemu-server v5 05/21] cpu flags: normalize CPU flags to QEMU's format Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 07/21] cpu config: rename custom CPU model config loader Arthur Bied-Charreton
                   ` (15 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

The get_supported_cpu_flags helper returns a hardcoded list of
VM-specific flags, without any information on which nodes actually
support them.

Add query_available_cpu_flags, which annotates the flags QEMU recognizes
as `-cpu` arguments with the nodes that actually support them to give an
accurate picture of which flags will be accepted when starting a VM. Flags
can be filtered by scope (vm-specific or all) and queried for the
specific acceleration type (kvm/tcg), which determines which nodes report
supporting which flag.

This currently returns early when $arch is set to `aarch64`. This is
intended for vm-specific flags, since there are none that are currently
meant to be settable for VMs. For the general flags list, this is a
limitation due to the fact that it is a lot harder to get a list of
understood flags at QEMU build time for `aarch64`, as opposed to
`x86_64`, where `-cpu help` will just list all of them. It is left for a
future TODO.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/QemuServer/CPUFlags.pm | 132 +++++++++++++++++++++++++++++++++
 1 file changed, 132 insertions(+)

diff --git a/src/PVE/QemuServer/CPUFlags.pm b/src/PVE/QemuServer/CPUFlags.pm
index e17ed00c..709828d7 100644
--- a/src/PVE/QemuServer/CPUFlags.pm
+++ b/src/PVE/QemuServer/CPUFlags.pm
@@ -15,6 +15,7 @@ our @EXPORT_OK = qw(
     get_supported_cpu_flags
     query_understood_cpu_flags
     normalize_cpu_flag
+    query_available_cpu_flags
 );
 
 my $supported_vm_specific_cpu_flags_by_arch = {
@@ -154,6 +155,11 @@ sub supported_cpu_flags_names() {
     return @supported_cpu_flags_name_sorted;
 }
 
+sub supported_cpu_flags_names_by_arch($arch) {
+    my @res = sort map { $_->{name} } $supported_vm_specific_cpu_flags_by_arch->{$arch}->@*;
+    return @res;
+}
+
 sub cpu_flag_supported_re() {
     return qr/([+-])(@{[join('|', supported_cpu_flags_names())]})/;
 }
@@ -187,4 +193,130 @@ sub query_understood_cpu_flags($arch) {
     return \@flags;
 }
 
+=head3 flag_is_vm_specific($flag)
+
+Return true if `$flag` may be set for a VM's CPU flags configuration.
+
+=cut
+
+sub flag_is_vm_specific($flag) {
+    return defined($all_supported_vm_specific_cpu_flags->{$flag});
+}
+
+sub flag_descriptions($arch) {
+    return { map { $_->{name} => $_->{description} }
+        $supported_vm_specific_cpu_flags_by_arch->{$arch}->@* };
+}
+
+=head3 query_available_cpu_flags($accel, $vm_specific, $arch)
+
+Retrieve a list of available flags, i.e., flags that will be accepted by PVE in a
+processor config when attempting to spawn a VM. Each flag is returned along with a list
+of nodes that support it. Flags that are not supported on any node are also returned;
+filtering them out is up to the consumer of this API.
+
+B<Parameters:>
+
+=over
+
+=item C<$accel> (C<kvm> | C<tcg>)
+
+Selects which acceleration type flag/node compatibility should be evaluated for.
+
+=item C<$vm_specific> (boolean)
+
+When set to 1, return only VM-specific flags, otherwise return all flags.
+
+=item C<$arch> (C<x86_64> | C<aarch64>)
+
+Specifies which architecture to query flags for. Note that in both scopes (VM-specific
+and all), C<aarch64> returns empty lists: this is intended for VM-specific flags, as no
+C<aarch64> flags are currently settable for a specific VM, however it is a known
+limitation for the "all" scope. PVE does not currently ship a list of understood flags
+for C<aarch64>, as it is not as trivial to obtain as for C<x86_64>, whose flags are easy
+to parse from QEMU's C<-cpu help> output.
+
+=back
+
+In order to get an accurate picture of which flags can actually be used, two sources are
+combined (see C<PVE::QemuServer::query_supported_cpu_flags> for details):
+
+=over
+
+=item 1.
+
+The B<understood> CPU flags, i.e., all flags QEMU accepts as C<-cpu> arguments in
+principle, regardless of whether the host CPU actually supports them.
+
+=item 2.
+
+The B<supported> CPU flags: the flags the host CPU actually supports, cached in the node
+KV store by C<pvestatd>. This is node-specific.
+
+=back
+
+Each flag from (1) is annotated with the subset of nodes from (2) that report supporting
+it.
+
+=cut
+
+sub query_available_cpu_flags($accel, $vm_specific, $arch) {
+    # TODO: a way to get supported flags for aarch64. This is not done because PVE
+    # does not currently ship a list of understood flags for aarch64, as it's more difficult
+    # to obtain during QEMU build - for x86_64, qemu -cpu help will just list the flags.
+    return [] if $arch eq 'aarch64';
+
+    my $descriptions = flag_descriptions($arch);
+    my $base =
+        !$vm_specific
+        ? query_understood_cpu_flags($arch)
+        : [supported_cpu_flags_names_by_arch($arch)];
+    my $available_flags = {
+        map {
+            $_ => { name => $_, 'supported-on' => {}, description => $descriptions->{$_} }
+        } @$base
+    };
+
+    my $kv_store = "cpuflags-$accel";
+    my $flags = PVE::Cluster::get_node_kv($kv_store);
+
+    $available_flags->{'nested-virt'} = {
+        name => 'nested-virt',
+        'supported-on' => {},
+        description => $descriptions->{'nested-virt'},
+    };
+
+    my $add_flag = sub($node, $name) {
+        return if !defined($available_flags->{$name});
+        $available_flags->{$name}->{'supported-on'}->{$node} = 1;
+    };
+
+    for my $node (keys %$flags) {
+        # This depends on `pvestatd` storing the flags in space-separated format, which
+        # is the case at the time of this commit.
+        for (split(' ', $flags->{$node})) {
+            my $pve_alias = undef;
+            $pve_alias = 'nested-virt' if $_ eq 'svm' || $_ eq 'vmx';
+            next if $vm_specific && !flag_is_vm_specific($_) && !$pve_alias;
+            $add_flag->($node, $_) if !($vm_specific && $pve_alias);
+            $add_flag->($node, $pve_alias) if $pve_alias;
+        }
+    }
+
+    for my $flag (values %$available_flags) {
+        $flag->{'supported-on'} = [sort keys $flag->{'supported-on'}->%*];
+    }
+
+    # Make sure 'nested-virt' is always shown first.
+    my $nested_virt = delete $available_flags->{'nested-virt'};
+
+    # Order flags that are not supported anywhere in the cluster to the end.
+    my @sorted = sort {
+        (scalar($a->{'supported-on'}->@*) == 0) <=> (scalar($b->{'supported-on'}->@*) == 0)
+            || $a->{name} cmp $b->{name}
+    } values %$available_flags;
+
+    return [defined($nested_virt) ? ($nested_virt, @sorted) : @sorted];
+}
+
 1;
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH qemu-server v5 07/21] cpu config: rename custom CPU model config loader
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (5 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH qemu-server v5 06/21] cpu flags: add helper querying CPU flags with nodes supporting them Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 08/21] cpu config: add helpers to lock and write config Arthur Bied-Charreton
                   ` (14 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

... from load_custom_model_conf to the more explicit
load_custom_cpu_model_config.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/QemuServer/CPUConfig.pm      | 6 +++---
 src/test/run_config2command_tests.pl | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/PVE/QemuServer/CPUConfig.pm b/src/PVE/QemuServer/CPUConfig.pm
index 0ee9b991..3f963218 100644
--- a/src/PVE/QemuServer/CPUConfig.pm
+++ b/src/PVE/QemuServer/CPUConfig.pm
@@ -46,7 +46,7 @@ cfs_register_file(
     sub { PVE::QemuServer::CPUConfig->write_config(@_); },
 );
 
-sub load_custom_model_conf {
+sub load_custom_cpu_model_config {
     return cfs_read_file($cpu_models_filename);
 }
 
@@ -571,7 +571,7 @@ sub get_cpu_models {
 
     return $models if !$include_custom;
 
-    my $conf = load_custom_model_conf();
+    my $conf = load_custom_cpu_model_config();
     for my $custom_model (keys %{ $conf->{ids} }) {
         my $reported_model = $conf->{ids}->{$custom_model}->{'reported-model'};
         $reported_model //= $cpu_fmt->{'reported-model'}->{default};
@@ -598,7 +598,7 @@ sub get_custom_model {
     my ($name, $noerr) = @_;
 
     $name =~ s/^custom-//;
-    my $conf = load_custom_model_conf();
+    my $conf = load_custom_cpu_model_config();
 
     my $entry = $conf->{ids}->{$name};
     if (!defined($entry)) {
diff --git a/src/test/run_config2command_tests.pl b/src/test/run_config2command_tests.pl
index ebe0dca1..0341d2f9 100755
--- a/src/test/run_config2command_tests.pl
+++ b/src/test/run_config2command_tests.pl
@@ -435,7 +435,7 @@ $pve_common_file->mock(
 my $pve_cpuconfig;
 $pve_cpuconfig = Test::MockModule->new('PVE::QemuServer::CPUConfig');
 $pve_cpuconfig->mock(
-    load_custom_model_conf => sub {
+    load_custom_cpu_model_config => sub {
         # mock custom CPU model config
         return PVE::QemuServer::CPUConfig->parse_config(
             "cpu-models.conf",
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH qemu-server v5 08/21] cpu config: add helpers to lock and write config
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (6 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH qemu-server v5 07/21] cpu config: rename custom CPU model config loader Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 09/21] cpu: register standard option for CPU format Arthur Bied-Charreton
                   ` (13 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Add lock/write primitives to allow programmatic access to
/etc/pve/virtual-guest/cpu-models.conf as a preparatory step for adding a
custom CPU models CRUD API to pve-manager.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/QemuServer/CPUConfig.pm | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/PVE/QemuServer/CPUConfig.pm b/src/PVE/QemuServer/CPUConfig.pm
index 3f963218..174ba5b7 100644
--- a/src/PVE/QemuServer/CPUConfig.pm
+++ b/src/PVE/QemuServer/CPUConfig.pm
@@ -6,7 +6,7 @@ use warnings;
 use JSON;
 
 use PVE::JSONSchema qw(json_bool);
-use PVE::Cluster qw(cfs_register_file cfs_read_file);
+use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_lock_file cfs_write_file);
 use PVE::ProcFSTools;
 use PVE::RESTEnvironment qw(log_warn);
 use PVE::Tools qw(run_command);
@@ -50,6 +50,17 @@ sub load_custom_cpu_model_config {
     return cfs_read_file($cpu_models_filename);
 }
 
+sub write_custom_cpu_model_config {
+    my ($conf) = @_;
+    cfs_write_file($cpu_models_filename, $conf);
+}
+
+sub lock_custom_cpu_model_config {
+    my ($code) = @_;
+    cfs_lock_file($cpu_models_filename, undef, $code);
+    die $@ if $@;
+}
+
 #builtin models : reported-model is mandatory
 my $builtin_models_by_arch = {
     x86_64 => {
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH qemu-server v5 09/21] cpu: register standard option for CPU format
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (7 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH qemu-server v5 08/21] cpu config: add helpers to lock and write config Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 10/21] api: cpu flags: improve flags list returned by endpoint Arthur Bied-Charreton
                   ` (12 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Expose $cpu_fmt as a standard option to allow using it in endpoint
definitions.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/QemuServer/CPUConfig.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/PVE/QemuServer/CPUConfig.pm b/src/PVE/QemuServer/CPUConfig.pm
index 174ba5b7..06c00616 100644
--- a/src/PVE/QemuServer/CPUConfig.pm
+++ b/src/PVE/QemuServer/CPUConfig.pm
@@ -344,6 +344,7 @@ my $cpu_fmt = {
         optional => 1,
     },
 };
+PVE::JSONSchema::register_standard_option('pve-qm-custom-cpu-model', $cpu_fmt);
 
 my $sev_fmt = {
     type => {
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH qemu-server v5 10/21] api: cpu flags: improve flags list returned by endpoint
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (8 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH qemu-server v5 09/21] cpu: register standard option for CPU format Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH qemu-server v5 11/21] custom cpu models: avoid redundant config load Arthur Bied-Charreton
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Enhance the cpu-flags endpoint's list of flags by using the new
query_available_cpu_flags utility, which for each flag, additionally
returns a list of nodes supporting it, e.g.:

```
name: 'aes',
'supported-on': ['node1', 'node2'],
description: '...',
```

An `accel` parameter selects the acceleration type, `kvm` (default) or
`tcg` for which the flag-to-node compatibility is evaluated. The now-
unused get_supported_cpu_flags helper is removed.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/API2/Qemu/CPUFlags.pm  | 24 +++++++++++++++++++++---
 src/PVE/QemuServer/CPUFlags.pm | 14 --------------
 2 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/src/PVE/API2/Qemu/CPUFlags.pm b/src/PVE/API2/Qemu/CPUFlags.pm
index 4b409a40..873441d9 100644
--- a/src/PVE/API2/Qemu/CPUFlags.pm
+++ b/src/PVE/API2/Qemu/CPUFlags.pm
@@ -7,6 +7,7 @@ use PVE::RESTHandler;
 use PVE::Tools qw(extract_param);
 
 use PVE::QemuServer::CPUFlags;
+use PVE::QemuServer::Helpers;
 
 use base qw(PVE::RESTHandler);
 
@@ -14,13 +15,22 @@ __PACKAGE__->register_method({
     name => 'index',
     path => '',
     method => 'GET',
-    description => 'List of available VM-specific CPU flags.',
+    description =>
+        "List of available VM-specific CPU flags. Returns an empty list for 'aarch64' "
+        . "as no VM-specific flags are defined for it yet.",
     permissions => { user => 'all' },
     parameters => {
         additionalProperties => 0,
         properties => {
             node => get_standard_option('pve-node'),
             arch => get_standard_option('pve-qm-cpu-arch', { optional => 1 }),
+            accel => {
+                description => 'Acceleration type to check node compatibility for.',
+                type => 'string',
+                enum => [qw(kvm tcg)],
+                optional => 1,
+                default => 'kvm',
+            },
         },
     },
     returns => {
@@ -36,15 +46,23 @@ __PACKAGE__->register_method({
                     type => 'string',
                     description => "Description of the CPU flag.",
                 },
+                'supported-on' => {
+                    description =>
+                        'List of nodes supporting the CPU flag with the selected acceleration type ("accel").',
+                    type => 'array',
+                    items => get_standard_option('pve-node'),
+                    optional => 1,
+                },
             },
         },
     },
     code => sub {
         my ($param) = @_;
 
-        my $arch = extract_param($param, 'arch');
+        my $arch = extract_param($param, 'arch') // PVE::QemuServer::Helpers::get_host_arch();
+        my $accel = extract_param($param, 'accel') // 'kvm';
 
-        return PVE::QemuServer::CPUFlags::get_supported_cpu_flags($arch);
+        return PVE::QemuServer::CPUFlags::query_available_cpu_flags($accel, 1, $arch);
     },
 });
 
diff --git a/src/PVE/QemuServer/CPUFlags.pm b/src/PVE/QemuServer/CPUFlags.pm
index 709828d7..09a4ab0b 100644
--- a/src/PVE/QemuServer/CPUFlags.pm
+++ b/src/PVE/QemuServer/CPUFlags.pm
@@ -6,13 +6,11 @@ use Exporter qw(import);
 
 use PVE::Cluster;
 use PVE::File;
-use PVE::QemuServer::Helpers qw(get_host_arch);
 
 our @EXPORT_OK = qw(
     cpu_flag_supported_re
     cpu_flag_any_re
     supported_cpu_flags_names
-    get_supported_cpu_flags
     query_understood_cpu_flags
     normalize_cpu_flag
     query_available_cpu_flags
@@ -168,18 +166,6 @@ sub cpu_flag_any_re() {
     return qr/([+-])([a-zA-Z0-9\-_\.]+)/;
 }
 
-=head3 get_supported_cpu_flags($arch)
-
-Return supported VM-specific CPU flags for $arch. $arch defaults to the host architecture
-if C<undef>.
-
-=cut
-
-sub get_supported_cpu_flags($arch) {
-    $arch = get_host_arch() if !defined($arch);
-    return $supported_vm_specific_cpu_flags_by_arch->{$arch};
-}
-
 sub query_understood_cpu_flags($arch) {
     my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
 
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH qemu-server v5 11/21] custom cpu models: avoid redundant config load
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (9 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH qemu-server v5 10/21] api: cpu flags: improve flags list returned by endpoint Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-manager v5 12/21] cluster: reorder imports Arthur Bied-Charreton
                   ` (10 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

get_custom_model unconditionally loads the custom CPU models config. In
order to allow avoiding a redundant load at a call site that already has
the config loaded, take an optional $conf parameter and only load the
config if that is undefined.

Suggested-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/QemuServer/CPUConfig.pm | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/PVE/QemuServer/CPUConfig.pm b/src/PVE/QemuServer/CPUConfig.pm
index 06c00616..0de8c7e3 100644
--- a/src/PVE/QemuServer/CPUConfig.pm
+++ b/src/PVE/QemuServer/CPUConfig.pm
@@ -607,10 +607,10 @@ sub is_custom_model {
 # Use this to get a single model in the format described by $cpu_fmt.
 # Allows names with and without custom- prefix.
 sub get_custom_model {
-    my ($name, $noerr) = @_;
+    my ($name, $noerr, $conf) = @_;
 
     $name =~ s/^custom-//;
-    my $conf = load_custom_cpu_model_config();
+    $conf //= load_custom_cpu_model_config();
 
     my $entry = $conf->{ids}->{$name};
     if (!defined($entry)) {
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH pve-manager v5 12/21] cluster: reorder imports
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (10 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH qemu-server v5 11/21] custom cpu models: avoid redundant config load Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-manager v5 13/21] cluster: makefile: reorder perl sources and align backslashes Arthur Bied-Charreton
                   ` (9 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 PVE/API2/Cluster.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/PVE/API2/Cluster.pm b/PVE/API2/Cluster.pm
index 5a663b79..a800dbbd 100644
--- a/PVE/API2/Cluster.pm
+++ b/PVE/API2/Cluster.pm
@@ -27,8 +27,8 @@ use PVE::API2::Backup;
 use PVE::API2::Cluster::BackupInfo;
 use PVE::API2::Cluster::BulkAction;
 use PVE::API2::Cluster::Ceph;
-use PVE::API2::Cluster::Mapping;
 use PVE::API2::Cluster::Jobs;
+use PVE::API2::Cluster::Mapping;
 use PVE::API2::Cluster::MetricServer;
 use PVE::API2::Cluster::Notifications;
 use PVE::API2::ClusterConfig;
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH pve-manager v5 13/21] cluster: makefile: reorder perl sources and align backslashes
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (11 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH pve-manager v5 12/21] cluster: reorder imports Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-manager v5 14/21] api: add endpoint querying available CPU flags cluster-wide Arthur Bied-Charreton
                   ` (8 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 PVE/API2/Cluster/Makefile | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/PVE/API2/Cluster/Makefile b/PVE/API2/Cluster/Makefile
index 6cffe4c9..1f9a95e6 100644
--- a/PVE/API2/Cluster/Makefile
+++ b/PVE/API2/Cluster/Makefile
@@ -8,11 +8,11 @@ SUBDIRS=Mapping		\
 PERLSOURCE= 			\
 	BackupInfo.pm		\
 	BulkAction.pm		\
+	Ceph.pm				\
+	Jobs.pm				\
+	Mapping.pm			\
 	MetricServer.pm		\
-	Mapping.pm		\
-	Notifications.pm		\
-	Jobs.pm			\
-	Ceph.pm
+	Notifications.pm
 
 all:
 
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH pve-manager v5 14/21] api: add endpoint querying available CPU flags cluster-wide
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (12 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH pve-manager v5 13/21] cluster: makefile: reorder perl sources and align backslashes Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:45   ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-manager v5 15/21] api: add CRUD handlers for custom CPU models Arthur Bied-Charreton
                   ` (7 subsequent siblings)
  21 siblings, 1 reply; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Add endpoint at `/cluster/qemu/cpu-flags` allowing to query the CPU
flags available on the whole cluster, filtered by acceleration type
(kvm/tcg).

For each flag, a list of nodes supporting it is returned. This gives an
accurate picture of which flags will actually be accepted by QEMU when
starting a VM.

Currently only return available flags for x86_64, see documentation of
query_available_cpu_flags.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 PVE/API2/Cluster.pm               |  7 ++++
 PVE/API2/Cluster/Makefile         |  6 ++-
 PVE/API2/Cluster/Qemu.pm          | 41 +++++++++++++++++++
 PVE/API2/Cluster/Qemu/CPUFlags.pm | 68 +++++++++++++++++++++++++++++++
 PVE/API2/Cluster/Qemu/Makefile    | 17 ++++++++
 5 files changed, 137 insertions(+), 2 deletions(-)
 create mode 100644 PVE/API2/Cluster/Qemu.pm
 create mode 100644 PVE/API2/Cluster/Qemu/CPUFlags.pm
 create mode 100644 PVE/API2/Cluster/Qemu/Makefile

diff --git a/PVE/API2/Cluster.pm b/PVE/API2/Cluster.pm
index a800dbbd..aa493013 100644
--- a/PVE/API2/Cluster.pm
+++ b/PVE/API2/Cluster.pm
@@ -31,6 +31,7 @@ use PVE::API2::Cluster::Jobs;
 use PVE::API2::Cluster::Mapping;
 use PVE::API2::Cluster::MetricServer;
 use PVE::API2::Cluster::Notifications;
+use PVE::API2::Cluster::Qemu;
 use PVE::API2::ClusterConfig;
 use PVE::API2::Firewall::Cluster;
 use PVE::API2::HAConfig;
@@ -59,6 +60,11 @@ __PACKAGE__->register_method({
     path => 'notifications',
 });
 
+__PACKAGE__->register_method({
+    subclass => "PVE::API2::Cluster::Qemu",
+    path => 'qemu',
+});
+
 __PACKAGE__->register_method({
     subclass => "PVE::API2::ClusterConfig",
     path => 'config',
@@ -166,6 +172,7 @@ __PACKAGE__->register_method({
             { name => 'notifications' },
             { name => 'nextid' },
             { name => 'options' },
+            { name => 'qemu' },
             { name => 'replication' },
             { name => 'resources' },
             { name => 'status' },
diff --git a/PVE/API2/Cluster/Makefile b/PVE/API2/Cluster/Makefile
index 1f9a95e6..d3a56830 100644
--- a/PVE/API2/Cluster/Makefile
+++ b/PVE/API2/Cluster/Makefile
@@ -1,7 +1,8 @@
 include ../../../defines.mk
 
 SUBDIRS=Mapping		\
-	BulkAction
+	BulkAction 		\
+	Qemu
 
 # for node independent, cluster-wide applicable, API endpoints
 # ensure we do not conflict with files shipped by pve-cluster!!
@@ -12,7 +13,8 @@ PERLSOURCE= 			\
 	Jobs.pm				\
 	Mapping.pm			\
 	MetricServer.pm		\
-	Notifications.pm
+	Notifications.pm	\
+	Qemu.pm
 
 all:
 
diff --git a/PVE/API2/Cluster/Qemu.pm b/PVE/API2/Cluster/Qemu.pm
new file mode 100644
index 00000000..f09481b2
--- /dev/null
+++ b/PVE/API2/Cluster/Qemu.pm
@@ -0,0 +1,41 @@
+package PVE::API2::Cluster::Qemu;
+
+use v5.36;
+
+use PVE::API2::Cluster::Qemu::CPUFlags;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method({
+    subclass => "PVE::API2::Cluster::Qemu::CPUFlags",
+    path => "cpu-flags",
+});
+
+__PACKAGE__->register_method({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "Cluster-wide QEMU index",
+    permissions => { user => 'all' },
+    parameters => {
+        additionalProperties => 0,
+        properties => {},
+    },
+    returns => {
+        type => 'array',
+        items => {
+            type => "object",
+            properties => {},
+        },
+        links => [{ rel => 'child', href => "{name}" }],
+    },
+    code => sub {
+        my ($param) = @_;
+
+        return [
+            { name => 'cpu-flags' },
+        ];
+    },
+});
+
+1;
diff --git a/PVE/API2/Cluster/Qemu/CPUFlags.pm b/PVE/API2/Cluster/Qemu/CPUFlags.pm
new file mode 100644
index 00000000..4d11197f
--- /dev/null
+++ b/PVE/API2/Cluster/Qemu/CPUFlags.pm
@@ -0,0 +1,68 @@
+package PVE::API2::Cluster::Qemu::CPUFlags;
+
+use v5.36;
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RESTHandler;
+use PVE::Tools qw(extract_param);
+
+use PVE::QemuServer::CPUFlags;
+use PVE::QemuServer::Helpers;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "List of available CPU flags. Currently only implemented for x86_64,"
+        . " returns an empty list for aarch64.",
+    permissions => { check => ['perm', '/nodes', ['Sys.Audit']] },
+    parameters => {
+        additionalProperties => 0,
+        properties => {
+            arch => get_standard_option('pve-qm-cpu-arch', { optional => 1 }),
+            accel => {
+                description => 'Acceleration type to check node compatibility for.',
+                type => 'string',
+                enum => [qw(kvm tcg)],
+                optional => 1,
+                default => 'kvm',
+            },
+        },
+    },
+    returns => {
+        type => 'array',
+        items => {
+            type => 'object',
+            properties => {
+                name => {
+                    type => 'string',
+                    description => "Name of the CPU flag.",
+                },
+                description => {
+                    type => 'string',
+                    description => "Description of the CPU flag.",
+                    optional => 1,
+                },
+                'supported-on' => {
+                    description =>
+                        'List of nodes supporting the flag with the selected acceleration type ("accel").',
+                    type => 'array',
+                    items => get_standard_option('pve-node'),
+                    optional => 1,
+                },
+            },
+        },
+    },
+    code => sub {
+        my ($param) = @_;
+
+        my $arch = extract_param($param, 'arch') // PVE::QemuServer::Helpers::get_host_arch();
+        my $accel = extract_param($param, 'accel') // 'kvm';
+
+        return PVE::QemuServer::CPUFlags::query_available_cpu_flags($accel, 0, $arch);
+    },
+});
+
+1;
diff --git a/PVE/API2/Cluster/Qemu/Makefile b/PVE/API2/Cluster/Qemu/Makefile
new file mode 100644
index 00000000..2ab3e0b1
--- /dev/null
+++ b/PVE/API2/Cluster/Qemu/Makefile
@@ -0,0 +1,17 @@
+include ../../../../defines.mk
+
+# for node independent, cluster-wide applicable, API endpoints
+# ensure we do not conflict with files shipped by pve-cluster!!
+PERLSOURCE= 	\
+	CPUFlags.pm
+
+all:
+
+.PHONY: clean
+clean:
+	rm -rf *~
+
+.PHONY: install
+install: ${PERLSOURCE}
+	install -d ${PERLLIBDIR}/PVE/API2/Cluster/Qemu
+	install -m 0644 ${PERLSOURCE} ${PERLLIBDIR}/PVE/API2/Cluster/Qemu
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH pve-manager v5 15/21] api: add CRUD handlers for custom CPU models
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (13 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH pve-manager v5 14/21] api: add endpoint querying available CPU flags cluster-wide Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-manager v5 16/21] ui: cpu model selector: allow filtering out custom models Arthur Bied-Charreton
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Add CRUD endpoints for custom CPU models, allowing to interact with
/etc/pve/virtual-guest/cpu-models.conf [0].

The `custom-` prefix is not required from the consumers for the
`cputype` parameter, rather the handlers normalize it such that `foo`
evaluates to the same custom model as `custom-foo`.

[0] https://pve.proxmox.com/wiki/Manual:_cpu-models.conf

Based on & adapted from the following patches by Stefan Reiter:
https://lore.proxmox.com/pve-devel/20211028114150.3245864-4-s.reiter@proxmox.com/
https://lore.proxmox.com/pve-devel/20211028114150.3245864-5-s.reiter@proxmox.com/

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 PVE/API2/Cluster/Qemu.pm                 |   8 +-
 PVE/API2/Cluster/Qemu/CustomCPUModels.pm | 204 +++++++++++++++++++++++
 PVE/API2/Cluster/Qemu/Makefile           |   5 +-
 3 files changed, 214 insertions(+), 3 deletions(-)
 create mode 100644 PVE/API2/Cluster/Qemu/CustomCPUModels.pm

diff --git a/PVE/API2/Cluster/Qemu.pm b/PVE/API2/Cluster/Qemu.pm
index f09481b2..38f59e44 100644
--- a/PVE/API2/Cluster/Qemu.pm
+++ b/PVE/API2/Cluster/Qemu.pm
@@ -3,6 +3,7 @@ package PVE::API2::Cluster::Qemu;
 use v5.36;
 
 use PVE::API2::Cluster::Qemu::CPUFlags;
+use PVE::API2::Cluster::Qemu::CustomCPUModels;
 
 use base qw(PVE::RESTHandler);
 
@@ -11,6 +12,11 @@ __PACKAGE__->register_method({
     path => "cpu-flags",
 });
 
+__PACKAGE__->register_method({
+    subclass => "PVE::API2::Cluster::Qemu::CustomCPUModels",
+    path => "custom-cpu-models",
+});
+
 __PACKAGE__->register_method({
     name => 'index',
     path => '',
@@ -33,7 +39,7 @@ __PACKAGE__->register_method({
         my ($param) = @_;
 
         return [
-            { name => 'cpu-flags' },
+            { name => 'cpu-flags' }, { name => 'custom-cpu-models' },
         ];
     },
 });
diff --git a/PVE/API2/Cluster/Qemu/CustomCPUModels.pm b/PVE/API2/Cluster/Qemu/CustomCPUModels.pm
new file mode 100644
index 00000000..e4b1523f
--- /dev/null
+++ b/PVE/API2/Cluster/Qemu/CustomCPUModels.pm
@@ -0,0 +1,204 @@
+package PVE::API2::Cluster::Qemu::CustomCPUModels;
+
+use v5.36;
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RESTHandler;
+use PVE::SectionConfig;
+use PVE::Tools qw(extract_param);
+
+use PVE::QemuServer::CPUConfig;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method({
+    name => 'config',
+    path => '',
+    method => 'GET',
+    description => 'Read all custom CPU models definitions',
+    permissions => {
+        check => ['perm', '/nodes', ['Sys.Audit']],
+    },
+    parameters => {
+        additionalProperties => 0,
+    },
+    returns => {
+        type => 'array',
+        items => {
+            type => 'object',
+            properties => get_standard_option('pve-qm-custom-cpu-model'),
+        },
+        links => [{ rel => 'child', href => "{cputype}" }],
+    },
+    code => sub {
+        my $conf = PVE::QemuServer::CPUConfig::load_custom_cpu_model_config();
+        delete $conf->{order};
+        my $res = [];
+        for my $id (keys $conf->{ids}->%*) {
+            delete $conf->{ids}->{$id}->{type};
+            push @$res, $conf->{ids}->{$id};
+        }
+        return $res;
+    },
+});
+
+__PACKAGE__->register_method({
+    name => 'create',
+    path => '',
+    method => 'POST',
+    protected => 1,
+    description => 'Add a custom CPU model definition',
+    permissions => {
+        check => ['perm', '/nodes', ['Sys.Modify']],
+    },
+    parameters => {
+        additionalProperties => 0,
+        properties => PVE::QemuServer::CPUConfig::add_cpu_json_properties({}),
+    },
+    returns => { type => 'null' },
+    code => sub {
+        my ($param) = @_;
+
+        (my $name = $param->{cputype}) =~ s/^custom-//;
+        $param->{cputype} = "custom-$name";
+
+        PVE::QemuServer::CPUConfig::lock_custom_cpu_model_config(sub {
+            my $conf = PVE::QemuServer::CPUConfig::load_custom_cpu_model_config();
+            my $opts = PVE::QemuServer::CPUConfig->check_config($name, $param, 1, 1);
+
+            die "custom CPU model '$name' already exists\n"
+                if defined($conf->{ids}->{$name});
+
+            $conf->{ids}->{$name} = $opts;
+
+            PVE::QemuServer::CPUConfig::write_custom_cpu_model_config($conf);
+        });
+    },
+});
+
+__PACKAGE__->register_method({
+    name => 'delete',
+    path => '{cputype}',
+    method => 'DELETE',
+    protected => 1,
+    description => 'Delete a custom CPU model definition',
+    permissions => {
+        check => ['perm', '/nodes', ['Sys.Modify']],
+    },
+    parameters => {
+        additionalProperties => 0,
+        properties => {
+            cputype => {
+                type => 'string',
+                description => "The custom model to delete.",
+            },
+        },
+    },
+    returns => { type => 'null' },
+    code => sub {
+        my ($param) = @_;
+
+        (my $name = $param->{cputype}) =~ s/^custom-//;
+
+        PVE::QemuServer::CPUConfig::lock_custom_cpu_model_config(sub {
+            my $conf = PVE::QemuServer::CPUConfig::load_custom_cpu_model_config();
+
+            die "custom CPU model '$name' does not exist\n"
+                if !defined($conf->{ids}->{$name});
+            delete $conf->{ids}->{$name};
+
+            PVE::QemuServer::CPUConfig::write_custom_cpu_model_config($conf);
+        });
+    },
+});
+
+__PACKAGE__->register_method({
+    name => 'update',
+    path => '{cputype}',
+    method => 'PUT',
+    protected => 1,
+    description => "Update a custom CPU model definition.",
+    permissions => {
+        check => ['perm', '/nodes', ['Sys.Modify']],
+    },
+    parameters => {
+        additionalProperties => 0,
+        properties => PVE::QemuServer::CPUConfig::add_cpu_json_properties({
+            digest => get_standard_option('pve-config-digest'),
+            delete => {
+                type => 'string',
+                format => 'pve-configid-list',
+                description => "A list of properties to delete.",
+                optional => 1,
+            },
+        }),
+    },
+    returns => { type => 'null' },
+    code => sub {
+        my ($param) = @_;
+
+        my $digest = extract_param($param, 'digest');
+        my $delete = extract_param($param, 'delete');
+
+        if ($delete) {
+            $delete = [PVE::Tools::split_list($delete)];
+        }
+
+        (my $name = $param->{cputype}) =~ s/^custom-//;
+        $param->{cputype} = "custom-$name";
+
+        PVE::QemuServer::CPUConfig::lock_custom_cpu_model_config(sub {
+            my $conf = PVE::QemuServer::CPUConfig::load_custom_cpu_model_config();
+
+            PVE::SectionConfig::assert_if_modified($conf, $digest);
+
+            my $opts = PVE::QemuServer::CPUConfig->check_config($name, $param, 0, 1);
+
+            my $model = $conf->{ids}->{$name};
+            die "custom CPU model '$name' does not exist\n" if !defined($model);
+
+            my $options = PVE::QemuServer::CPUConfig->private()->{options}->{'cpu-model'};
+
+            PVE::SectionConfig::delete_from_config($model, $options, $opts, $delete);
+
+            $model->{$_} = $opts->{$_} for keys $opts->%*;
+
+            PVE::QemuServer::CPUConfig::write_custom_cpu_model_config($conf);
+        });
+    },
+});
+
+__PACKAGE__->register_method({
+    name => 'info',
+    path => '{cputype}',
+    method => 'GET',
+    description => 'Retrieve details about a specific custom CPU model',
+    permissions => {
+        check => ['perm', '/nodes', ['Sys.Audit']],
+    },
+    parameters => {
+        additionalProperties => 0,
+        properties => {
+            cputype => {
+                type => 'string',
+                description => "Name of the CPU model to query.",
+            },
+        },
+    },
+    returns => {
+        type => 'object',
+        properties => PVE::QemuServer::CPUConfig::add_cpu_json_properties({
+            digest => get_standard_option('pve-config-digest'),
+        }),
+    },
+    code => sub {
+        my ($param) = @_;
+        (my $name = $param->{cputype}) =~ s/^custom-//;
+        my $conf = PVE::QemuServer::CPUConfig::load_custom_cpu_model_config();
+        my $retval = PVE::QemuServer::CPUConfig::get_custom_model($name, 0, $conf);
+        $retval->{digest} = $conf->{digest};
+        return $retval;
+    },
+});
+
+1;
diff --git a/PVE/API2/Cluster/Qemu/Makefile b/PVE/API2/Cluster/Qemu/Makefile
index 2ab3e0b1..76b661db 100644
--- a/PVE/API2/Cluster/Qemu/Makefile
+++ b/PVE/API2/Cluster/Qemu/Makefile
@@ -2,8 +2,9 @@ include ../../../../defines.mk
 
 # for node independent, cluster-wide applicable, API endpoints
 # ensure we do not conflict with files shipped by pve-cluster!!
-PERLSOURCE= 	\
-	CPUFlags.pm
+PERLSOURCE= 		\
+	CPUFlags.pm		\
+	CustomCPUModels.pm
 
 all:
 
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH pve-manager v5 16/21] ui: cpu model selector: allow filtering out custom models
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (14 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH pve-manager v5 15/21] api: add CRUD handlers for custom CPU models Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-manager v5 17/21] ui: add basic custom CPU model editor Arthur Bied-Charreton
                   ` (5 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Add 'showCustomModels' configuration parameter. When set to false, only
default CPU models will be shown.

This is needed when using the CPUModelSelector in the context of
creating a new custom CPU model, where we do not want to allow the
reported-model being another custom CPU model.

Based on patch by Stefan Reiter, with allowCustom renamed to
showCustomModels:
https://lore.proxmox.com/pve-devel/20211028114150.3245864-8-s.reiter@proxmox.com/

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
---
 www/manager6/form/CPUModelSelector.js | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/www/manager6/form/CPUModelSelector.js b/www/manager6/form/CPUModelSelector.js
index a55d473e..3b039dff 100644
--- a/www/manager6/form/CPUModelSelector.js
+++ b/www/manager6/form/CPUModelSelector.js
@@ -17,8 +17,12 @@ Ext.define('PVE.form.CPUModelSelector', {
     anyMatch: true,
     forceSelection: true,
     autoSelect: false,
+    triggerAction: 'query',
 
     deleteEmpty: true,
+    config: {
+        showCustomModels: true,
+    },
 
     getSubmitData: function () {
         let me = this,
@@ -135,4 +139,11 @@ Ext.define('PVE.form.CPUModelSelector', {
             },
         },
     },
+    initComponent: function () {
+        let me = this;
+        me.callParent();
+        if (!me.showCustomModels) {
+            me.getStore().addFilter({ filterFn: (rec) => !rec.data.custom });
+        }
+    },
 });
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH pve-manager v5 17/21] ui: add basic custom CPU model editor
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (15 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH pve-manager v5 16/21] ui: cpu model selector: allow filtering out custom models Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-manager v5 18/21] ui: cpu flags selector: add CPU flag editor for custom models Arthur Bied-Charreton
                   ` (4 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Add basic structure for viewing (CPUTypeView), creating and editing
(CPUTypeEdit, PhysBitsSelector) custom CPU models, allowing to interact
with the new custom-cpu-models API in the UI.

Based on & adapted from patch by Stefan Reiter:
https://lore.proxmox.com/pve-devel/20211028114150.3245864-9-s.reiter@proxmox.com/

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 www/manager6/Makefile                 |   3 +
 www/manager6/dc/CPUTypeEdit.js        | 107 ++++++++++++++++++
 www/manager6/dc/CPUTypeView.js        | 150 +++++++++++++++++++++++++
 www/manager6/dc/Config.js             |   9 ++
 www/manager6/form/PhysBitsSelector.js | 153 ++++++++++++++++++++++++++
 5 files changed, 422 insertions(+)
 create mode 100644 www/manager6/dc/CPUTypeEdit.js
 create mode 100644 www/manager6/dc/CPUTypeView.js
 create mode 100644 www/manager6/form/PhysBitsSelector.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 280a9ca6..e6ffc4a3 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -61,6 +61,7 @@ JSSRC= 							\
 	form/PCISelector.js				\
 	form/PCIMapSelector.js				\
 	form/PermPathSelector.js			\
+	form/PhysBitsSelector.js			\
 	form/PoolSelector.js				\
 	form/PreallocationSelector.js			\
 	form/PrivilegesSelector.js			\
@@ -175,6 +176,8 @@ JSSRC= 							\
 	dc/CmdMenu.js					\
 	dc/Config.js					\
 	dc/CorosyncLinkEdit.js				\
+	dc/CPUTypeEdit.js				\
+	dc/CPUTypeView.js				\
 	dc/GroupEdit.js					\
 	dc/GroupView.js					\
 	dc/Guests.js					\
diff --git a/www/manager6/dc/CPUTypeEdit.js b/www/manager6/dc/CPUTypeEdit.js
new file mode 100644
index 00000000..0b72d579
--- /dev/null
+++ b/www/manager6/dc/CPUTypeEdit.js
@@ -0,0 +1,107 @@
+Ext.define('PVE.dc.CPUTypeEdit', {
+    extend: 'Proxmox.window.Edit',
+    alias: ['widget.pveCpuTypeEdit'],
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    subject: gettext('CPU Type'),
+
+    // Avoid default-focusing the reported model dropdown while still
+    // focusing the name textfield if it is editable
+    defaultFocus: 'textfield',
+
+    onlineHelp: '_cpu_type',
+
+    height: 600,
+    width: 800,
+
+    cbindData: {
+        cputype: '',
+        isCreate: (cfg) => !cfg.cputype,
+    },
+
+    cbind: {
+        autoLoad: (get) => !get('isCreate'),
+        url: (get) => `/api2/extjs/cluster/qemu/custom-cpu-models/${get('cputype')}`,
+        method: (get) => (get('isCreate') ? 'POST' : 'PUT'),
+        isCreate: (get) => get('isCreate'),
+    },
+
+    items: [
+        {
+            xtype: 'inputpanel',
+            onGetValues: function (values) {
+                let win = this.up('window');
+
+                PVE.Utils.delete_if_default(values, 'reported-model', '', win.isCreate);
+                PVE.Utils.delete_if_default(values, 'hv-vendor-id', '', win.isCreate);
+                PVE.Utils.delete_if_default(values, 'phys-bits', '', win.isCreate);
+                PVE.Utils.delete_if_default(values, 'hidden', 0, win.isCreate);
+                PVE.Utils.delete_if_default(values, 'flags', '', win.isCreate);
+
+                if (win.isCreate) {
+                    delete values.delete;
+                }
+
+                return values;
+            },
+            column1: [
+                {
+                    xtype: 'pmxDisplayEditField',
+                    fieldLabel: gettext('Name'),
+                    cbind: {
+                        editable: '{isCreate}',
+                        value: '{cputype}',
+                    },
+                    name: 'cputype',
+                    renderer: (val) => val.replace(/^custom-/, ''),
+                    allowBlank: false,
+                },
+                {
+                    xtype: 'CPUModelSelector',
+                    fieldLabel: gettext('Base Model'),
+                    showCustomModels: false,
+                    name: 'reported-model',
+                    autoEl: {
+                        tag: 'div',
+                        'data-qtip': gettext(
+                            'CPU model the rest of the configuration is based on.',
+                        ),
+                    },
+                    cbind: {
+                        allowBlank: (get) => !get('isCreate'),
+                    },
+                    listeners: {
+                        afterrender: function (field) {
+                            let win = field.up('window');
+                            if (win.isCreate) {
+                                field.setEmptyText('');
+                            }
+                        },
+                    },
+                },
+                {
+                    xtype: 'textfield',
+                    fieldLabel: gettext('Hyper-V Vendor'),
+                    name: 'hv-vendor-id',
+                    allowBlank: true,
+                    emptyText: gettext('None'),
+                    maxLength: 12,
+                },
+            ],
+            column2: [
+                {
+                    xtype: 'checkbox',
+                    fieldLabel: gettext('Hidden'),
+                    name: 'hidden',
+                    inputValue: 1,
+                    uncheckedValue: 0,
+                },
+                {
+                    xtype: 'PhysBitsSelector',
+                    fieldLabel: gettext('Phys-Bits'),
+                    name: 'phys-bits',
+                },
+            ],
+        },
+    ],
+});
diff --git a/www/manager6/dc/CPUTypeView.js b/www/manager6/dc/CPUTypeView.js
new file mode 100644
index 00000000..9bed8408
--- /dev/null
+++ b/www/manager6/dc/CPUTypeView.js
@@ -0,0 +1,150 @@
+Ext.define('pve-custom-cpu-type', {
+    extend: 'Ext.data.Model',
+    fields: [
+        'cputype',
+        'reported-model',
+        'hv-vendor-id',
+        'flags',
+        'phys-bits',
+        { name: 'hidden', type: 'boolean' },
+    ],
+});
+
+Ext.define('PVE.dc.CPUTypeView', {
+    extend: 'Ext.grid.GridPanel',
+    alias: ['widget.pveCPUTypeView'],
+
+    onlineHelp: '_cpu_type',
+
+    store: {
+        model: 'pve-custom-cpu-type',
+        proxy: {
+            type: 'proxmox',
+            url: '/api2/json/cluster/qemu/custom-cpu-models',
+        },
+        autoLoad: true,
+        sorters: ['cputype'],
+    },
+
+    controller: {
+        xclass: 'Ext.app.ViewController',
+
+        getSelection: function () {
+            let me = this;
+            let grid = me.getView();
+            let selection = grid.getSelection();
+            if (selection.length === 1) {
+                return selection[0].data;
+            }
+            return null;
+        },
+
+        showEditor: function (cputype) {
+            let me = this;
+            let param = cputype ? { cputype } : {};
+            let win = Ext.create('PVE.dc.CPUTypeEdit', param);
+            win.on('destroy', () => me.reload());
+            win.show();
+        },
+
+        onAdd: function () {
+            let me = this;
+            me.showEditor();
+        },
+
+        onEdit: function () {
+            let me = this;
+            let selection = me.getSelection();
+            me.showEditor(selection.cputype);
+        },
+
+        reload: function () {
+            let me = this;
+            me.getView().getStore().reload();
+        },
+    },
+
+    columns: [
+        {
+            header: gettext('Name'),
+            flex: 1,
+            dataIndex: 'cputype',
+            renderer: (val) => val.replace(/^custom-/, ''),
+        },
+        {
+            header: gettext('Base Model'),
+            flex: 1,
+            dataIndex: 'reported-model',
+            autoEl: {
+                tag: 'div',
+                'data-qtip': gettext('CPU model the rest of the configuration is based on.'),
+            },
+        },
+        {
+            header: gettext('Phys-Bits'),
+            flex: 1,
+            dataIndex: 'phys-bits',
+        },
+        {
+            header: gettext('Hidden'),
+            flex: 1,
+            dataIndex: 'hidden',
+            renderer: (val) => Proxmox.Utils.format_boolean(val),
+        },
+        {
+            header: gettext('HyperV-Vendor'),
+            flex: 1,
+            dataIndex: 'hv-vendor-id',
+        },
+        {
+            header: gettext('Flags'),
+            flex: 2,
+            dataIndex: 'flags',
+        },
+    ],
+
+    tbar: [
+        {
+            text: gettext('Add'),
+            handler: 'onAdd',
+        },
+        '-',
+        {
+            xtype: 'proxmoxStdRemoveButton',
+            baseurl: '/api2/extjs/cluster/qemu/custom-cpu-models/',
+            getRecordName: (rec) => rec.data.cputype,
+            getUrl: function (rec) {
+                let me = this;
+                return me.baseurl + rec.data.cputype;
+            },
+            confirmMsg: function (rec) {
+                return Ext.String.format(
+                    gettext("Are you sure you want to remove the custom CPU model '{0}'?"),
+                    rec.data.cputype.replace(/^custom-/, ''),
+                );
+            },
+            callback: 'reload',
+        },
+        {
+            text: gettext('Edit'),
+            handler: 'onEdit',
+        },
+    ],
+
+    selModel: {
+        xtype: 'rowmodel',
+    },
+
+    listeners: {
+        itemdblclick: function (_, rec) {
+            let me = this;
+            me.getController().showEditor(rec.data.cputype);
+        },
+    },
+
+    initComponent: function () {
+        let me = this;
+        me.callParent();
+        Proxmox.Utils.monStoreErrors(me, me.store);
+    },
+});
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 2d1ced3e..cbd04066 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -439,6 +439,15 @@ Ext.define('PVE.dc.Config', {
             );
         }
 
+        if (caps.dc['Sys.Audit']) {
+            me.items.push({
+                xtype: 'pveCPUTypeView',
+                iconCls: 'fa fa-microchip',
+                title: gettext('Custom CPU models'),
+                itemId: 'cputypes',
+            });
+        }
+
         if (
             caps.mapping['Mapping.Audit'] ||
             caps.mapping['Mapping.Use'] ||
diff --git a/www/manager6/form/PhysBitsSelector.js b/www/manager6/form/PhysBitsSelector.js
new file mode 100644
index 00000000..5a9c13e1
--- /dev/null
+++ b/www/manager6/form/PhysBitsSelector.js
@@ -0,0 +1,153 @@
+Ext.define('PVE.form.PhysBitsSelector', {
+    extend: 'Ext.form.FieldContainer',
+    alias: 'widget.PhysBitsSelector',
+    mixins: ['Ext.form.field.Field'],
+
+    layout: 'vbox',
+    originalValue: '',
+
+    controller: {
+        xclass: 'Ext.app.ViewController',
+
+        onRadioChange: function (radio, value) {
+            let me = this;
+            if (value === undefined) {
+                return;
+            }
+
+            ['modeDefault', 'modeHost', 'modeCustom'].forEach(function (ref) {
+                let r = me.lookupReference(ref);
+                if (r !== radio) {
+                    r.suspendEvents();
+                    r.setValue(false);
+                    r.resumeEvents();
+                }
+            });
+
+            me.updateNumberField();
+        },
+
+        updateNumberField: function () {
+            let me = this;
+            let modeCustom = me.lookupReference('modeCustom');
+            let customNum = me.lookupReference('customNum');
+
+            customNum.setDisabled(!modeCustom.getValue());
+            me.getView().validate();
+        },
+
+        listen: {
+            component: {
+                '*': {
+                    change: function () {
+                        let me = this;
+                        me.getView().checkChange();
+                    },
+                },
+            },
+        },
+    },
+
+    getValue: function () {
+        let me = this;
+        let ctrl = me.getController();
+        if (ctrl.lookupReference('modeDefault').getValue()) {
+            return '';
+        } else if (ctrl.lookupReference('modeHost').getValue()) {
+            return 'host';
+        } else if (ctrl.lookupReference('modeCustom').getValue()) {
+            return ctrl.lookupReference('customNum').getValue();
+        }
+        return ''; // shouldn't happen
+    },
+
+    setValue: function (value) {
+        let me = this;
+        let ctrl = me.getController();
+        let modeField;
+
+        if (!value) {
+            modeField = ctrl.lookupReference('modeDefault');
+        } else if (value === 'host') {
+            modeField = ctrl.lookupReference('modeHost');
+        } else {
+            let customNum = ctrl.lookupReference('customNum');
+            customNum.setValue(value);
+            modeField = ctrl.lookupReference('modeCustom');
+        }
+
+        modeField.setValue(true);
+        me.checkChange();
+
+        return value;
+    },
+
+    getErrors: function () {
+        let me = this;
+        let ctrl = me.getController();
+        if (ctrl.lookupReference('modeCustom').getValue()) {
+            return ctrl.lookupReference('customNum').getErrors();
+        }
+        return [];
+    },
+
+    isValid: function () {
+        let me = this;
+        let ctrl = me.getController();
+        if (ctrl.lookupReference('modeCustom').getValue()) {
+            return ctrl.lookupReference('customNum').isValid();
+        }
+        return true;
+    },
+
+    items: [
+        {
+            xtype: 'radiofield',
+            boxLabel: gettext('Default from QEMU'),
+            inputValue: 'default',
+            checked: true,
+            reference: 'modeDefault',
+            listeners: {
+                change: 'onRadioChange',
+            },
+            isFormField: false,
+        },
+        {
+            xtype: 'radiofield',
+            boxLabel: gettext('Inherit from host CPU'),
+            inputValue: 'host',
+            reference: 'modeHost',
+            listeners: {
+                change: 'onRadioChange',
+            },
+            isFormField: false,
+        },
+        {
+            xtype: 'fieldcontainer',
+            layout: 'hbox',
+            items: [
+                {
+                    xtype: 'radiofield',
+                    boxLabel: gettext('Custom value'),
+                    inputValue: 'custom',
+                    listeners: {
+                        change: 'onRadioChange',
+                    },
+                    reference: 'modeCustom',
+                    isFormField: false,
+                },
+                {
+                    xtype: 'numberfield',
+                    width: 60,
+                    margin: '0 0 0 10px',
+                    minValue: 8,
+                    maxValue: 64,
+                    reference: 'customNum',
+                    allowBlank: false,
+                    isFormField: false,
+                    disabled: true,
+                },
+            ],
+        },
+    ],
+});
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH pve-manager v5 18/21] ui: cpu flags selector: add CPU flag editor for custom models
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (16 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH pve-manager v5 17/21] ui: add basic custom CPU model editor Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-manager v5 19/21] ui: cpu flags selector: allow filtering out flags supported on 0 nodes Arthur Bied-Charreton
                   ` (3 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Add CPU flag editor to the CPUTypeEdit component by extending the
VMCPUFlagSelector also used in the VM creation flow. This adds a config
field allowing to differentiate whether the component should display
only VM-specific flags, or all of them.

For each flag in the selector, also display which node(s) it is
available on.

In the VM ProcessorEdit panel, allow selecting the acceleration type
(kvm: 1/0). The selected value filters the available flags and is also
applied.

In the custom CPU model context, a radio group is added to filter by
acceleration type. It does not actually do anything besides filter the
flags, it is only there as a configuration helper.

Show unknown flags, i.e. flags that either the currently selected
acceleration type does not support or that do not exist at all, at the
top of the list to encourage users to reconsider whether they should be
set.

Based on & adapted from patch by Stefan Reiter:
https://lore.proxmox.com/pve-devel/20211028114150.3245864-10-s.reiter@proxmox.com

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 www/manager6/dc/CPUTypeEdit.js         |  46 ++++++++
 www/manager6/form/VMCPUFlagSelector.js | 141 +++++++++++++++++++------
 www/manager6/qemu/ProcessorEdit.js     |  42 ++++++++
 3 files changed, 198 insertions(+), 31 deletions(-)

diff --git a/www/manager6/dc/CPUTypeEdit.js b/www/manager6/dc/CPUTypeEdit.js
index 0b72d579..b1364bb3 100644
--- a/www/manager6/dc/CPUTypeEdit.js
+++ b/www/manager6/dc/CPUTypeEdit.js
@@ -87,6 +87,42 @@ Ext.define('PVE.dc.CPUTypeEdit', {
                     emptyText: gettext('None'),
                     maxLength: 12,
                 },
+                {
+                    xtype: 'radiogroup',
+                    fieldLabel: gettext('Acceleration'),
+                    layout: 'hbox',
+                    autoEl: {
+                        tag: 'span',
+                        'data-qtip': Ext.htmlEncode(
+                            gettext(
+                                'A custom CPU model using acceleration-specific flags can only be assigned to VMs configured with the matching acceleration type, i.e., "kvm: 1" for KVM, or "kvm: 0" for TCG.',
+                            ),
+                        ),
+                    },
+                    validateOnChange: false,
+                    items: [
+                        {
+                            boxLabel: 'KVM',
+                            inputValue: 1,
+                            name: 'kvm',
+                            checked: true,
+                            isFormField: false,
+                        },
+                        {
+                            boxLabel: 'TCG',
+                            inputValue: 0,
+                            name: 'kvm',
+                            margin: '0 0 0 10',
+                            isFormField: false,
+                        },
+                    ],
+                    listeners: {
+                        change: function (field, value) {
+                            let panel = field.up('pveCpuTypeEdit');
+                            panel.lookup('cpuFlags').setKvm(value.kvm);
+                        },
+                    },
+                },
             ],
             column2: [
                 {
@@ -102,6 +138,16 @@ Ext.define('PVE.dc.CPUTypeEdit', {
                     name: 'phys-bits',
                 },
             ],
+            columnB: [
+                {
+                    xtype: 'vmcpuflagselector',
+                    fieldLabel: gettext('Extra CPU flags'),
+                    name: 'flags',
+                    reference: 'cpuFlags',
+                    restrictToVMFlags: false,
+                    height: 380,
+                },
+            ],
         },
     ],
 });
diff --git a/www/manager6/form/VMCPUFlagSelector.js b/www/manager6/form/VMCPUFlagSelector.js
index 6e49ea45..e08a911b 100644
--- a/www/manager6/form/VMCPUFlagSelector.js
+++ b/www/manager6/form/VMCPUFlagSelector.js
@@ -6,22 +6,29 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
         field: 'Ext.form.field.Field',
     },
 
+    config: {
+        // Show only the flags that may be set for a specific VM.
+        restrictToVMFlags: true,
+    },
+
     disableSelection: true,
     columnLines: false,
     selectable: false,
-    hideHeaders: true,
 
     scrollable: 'y',
     height: 200,
 
-    unkownFlags: [],
-
     emptyText: gettext('No CPU flags available'),
 
     store: {
-        type: 'store',
-        fields: ['name', { name: 'state', defaultValue: '=' }, 'description'],
-        autoLoad: true,
+        fields: [
+            'name',
+            { name: 'state', defaultValue: '=' },
+            'description',
+            'supported-on',
+            'unknown',
+        ],
+        autoLoad: false,
         proxy: {
             type: 'proxmox',
             url: '/api2/json/nodes/localhost/capabilities/qemu/cpu-flags',
@@ -30,16 +37,7 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
             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 () {
@@ -64,49 +62,106 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
             }
         });
 
-        flags += me.unkownFlags.join(';');
-
         return flags;
     },
 
     setArch: function (arch) {
         let me = this;
         me.arch = arch;
-        let params = {};
+        let proxy = me.store.getProxy();
         if (arch) {
-            params.arch = arch;
+            proxy.setExtraParam('arch', arch);
+        } else {
+            delete proxy.extraParams.arch;
         }
-        me.store.getProxy().setExtraParams(params);
         me.store.reload();
     },
 
-    // Adjusts the store for the current value and determines the unkown flags based on what the
-    // store does not know.
+    setKvm: function (kvm) {
+        let me = this;
+        kvm = kvm ?? 1;
+        me.kvm = kvm;
+        let proxy = me.store.getProxy();
+        proxy.setExtraParam('accel', kvm === 1 ? 'kvm' : 'tcg');
+        me.store.reload();
+    },
+
+    // Adjusts the store for the current value. Flags not known to the API are added to the store
+    // as 'unknown' records so they stay visible and can be edited.
     adjustStoreForValue: function () {
         let me = this;
         let store = me.getStore();
         let value = me.value;
 
-        me.unkownFlags = [];
+        // Clear any previously added unknown records.
+        let unknownRecords = [];
+        let source = store.getDataSource();
+        source.each((rec) => {
+            if (rec.get('unknown')) {
+                unknownRecords.push(rec);
+            } else {
+                rec.set('state', '=');
+            }
+        });
+        store.remove(unknownRecords);
 
-        store.getData().each((rec) => rec.set('state', '='));
+        let newUnknownFlags = [];
+        let addUnknownFlag = function (flag, sign) {
+            newUnknownFlags.push({
+                name: flag,
+                state: sign,
+                // A TCG-only flag will be flagged as unknown when `accel` is set to `kvm` and vice-versa, hence the very general wording.
+                description: gettext(
+                    'This flag is not available for the selected acceleration type and/or not supported by any node in the cluster. It is very likely to lead to VM startup failure. You can remove it by setting it to "Default".',
+                ),
+                unknown: true,
+            });
+        };
 
         let flags = value ? value.split(';') : [];
         flags.forEach(function (flag) {
             let sign = flag.substr(0, 1);
             flag = flag.substr(1);
 
-            let rec = store.findRecord('name', flag, 0, false, true, true);
+            let rec = source.findBy((r) => r.get('name') === flag);
             if (rec !== null) {
-                rec.set('state', sign);
+                let supported = rec.get('supported-on');
+                // Treat flags that are set in the config but not supported anywhere as unknown
+                if (Array.isArray(supported) && supported.length === 0 && sign !== '=') {
+                    store.remove(rec);
+                    addUnknownFlag(flag, sign);
+                } else {
+                    rec.set('state', sign);
+                    rec.commit();
+                }
             } else {
-                me.unkownFlags.push(flag);
+                addUnknownFlag(flag, sign);
             }
         });
 
-        store.adjustedForValue = value;
-    },
+        // Make sure unknown flags are displayed at the top of the list
+        // so users reconsider them.
+        if (newUnknownFlags.length > 0) {
+            store.insert(0, newUnknownFlags);
+        }
 
+        // Ext.js uses buffered rendering [0] for larger lists like this one.
+        // AbstractView.refresh() [1], which was previously used here for refreshing,
+        // destroys and recreates all row elements but the buffered renderer only
+        // tracks a sliding window of DOM nodes, so the refresh skips rows outside
+        // the buffer, which leads to some elements not being fully rendered.
+        //
+        // Firing the 'refresh' event allows whatever view is currently rendering the
+        // table (i.e. buffered or not) to handle it accordingly.
+        //
+        // [0] https://docs.sencha.com/extjs/7.0.0/classic/Ext.grid.plugin.BufferedRenderer.html
+        // [1] https://docs.sencha.com/extjs/7.0.0/classic/Ext.view.AbstractView.html#method-refresh
+        store.fireEvent('refresh', store);
+    },
+    isDirty: function () {
+        let me = this;
+        return me.originalValue !== me.getValue();
+    },
     setValue: function (value) {
         let me = this;
 
@@ -122,6 +177,7 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
     },
     columns: [
         {
+            text: gettext('State'),
             dataIndex: 'state',
             renderer: function (v) {
                 switch (v) {
@@ -138,6 +194,7 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
             width: 65,
         },
         {
+            text: gettext('Value'),
             xtype: 'widgetcolumn',
             dataIndex: 'state',
             width: 95,
@@ -184,22 +241,44 @@ 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.value = me.originalValue = '';
-        me.store.view = me;
 
         me.callParent(arguments);
+
+        me.initialized = true;
+
+        me.getStore().on('load', function (store, _, success) {
+            if (success) {
+                me.adjustStoreForValue();
+                me.checkDirty();
+            }
+        });
+
+        if (!me.restrictToVMFlags) {
+            me.getStore().getProxy().setUrl('/api2/json/cluster/qemu/cpu-flags');
+            me.getStore().load();
+        }
     },
 });
diff --git a/www/manager6/qemu/ProcessorEdit.js b/www/manager6/qemu/ProcessorEdit.js
index 68caec16..e4cbce05 100644
--- a/www/manager6/qemu/ProcessorEdit.js
+++ b/www/manager6/qemu/ProcessorEdit.js
@@ -12,12 +12,14 @@ Ext.define('PVE.qemu.ProcessorInputPanel', {
             coreCount: 1,
             showCustomModelPermWarning: false,
             userIsRoot: false,
+            kvm: 1,
         },
         formulas: {
             totalCoreCount: (get) => get('socketCount') * get('coreCount'),
             cpuunitsDefault: (get) => (get('cgroupMode') === 1 ? 1024 : 100),
             cpuunitsMin: (get) => (get('cgroupMode') === 1 ? 2 : 1),
             cpuunitsMax: (get) => (get('cgroupMode') === 1 ? 262144 : 10000),
+            accel: (get) => (get('kvm') === 0 ? 'tcg' : 'kvm'),
         },
     },
 
@@ -117,6 +119,14 @@ Ext.define('PVE.qemu.ProcessorInputPanel', {
         me.lookup('cpuFlags').setArch(arch);
     },
 
+    setKvm: function (kvm) {
+        let me = this;
+        kvm = kvm ?? 1;
+        me.kvm = kvm;
+        me.getViewModel().set('kvm', kvm);
+        me.lookup('cpuFlags').setKvm(kvm);
+    },
+
     column1: [
         {
             xtype: 'proxmoxintegerfield',
@@ -241,6 +251,34 @@ Ext.define('PVE.qemu.ProcessorInputPanel', {
             name: 'numa',
             uncheckedValue: 0,
         },
+        {
+            xtype: 'proxmoxcheckbox',
+            fieldLabel: gettext('KVM hardware virtualization'),
+            name: 'kvm',
+            reference: 'kvmCheckbox',
+            defaultValue: 1,
+            checked: true,
+            uncheckedValue: 0,
+            listeners: {
+                change: function (field, value) {
+                    let panel = field.up('pveQemuProcessorPanel');
+                    let kvm = value ? 1 : 0;
+                    panel.getViewModel().set('kvm', kvm);
+                    panel.lookup('cpuFlags').setKvm(kvm);
+                },
+            },
+        },
+        {
+            xtype: 'displayfield',
+            userCls: 'pmx-hint',
+            value: gettext(
+                'TCG uses software emulation and is significantly slower than hardware-accelerated KVM.',
+            ),
+            hidden: true,
+            bind: {
+                hidden: '{kvm}',
+            },
+        },
     ],
     advancedColumnB: [
         {
@@ -252,6 +290,9 @@ Ext.define('PVE.qemu.ProcessorInputPanel', {
             xtype: 'vmcpuflagselector',
             reference: 'cpuFlags',
             name: 'flags',
+            bind: {
+                kvm: '{kvm}',
+            },
         },
     ],
 });
@@ -319,6 +360,7 @@ Ext.define('PVE.qemu.ProcessorEdit', {
                 }
                 me.setValues(data);
                 ipanel.setArch(arch);
+                ipanel.setKvm(data.kvm);
             },
         });
     },
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH pve-manager v5 19/21] ui: cpu flags selector: allow filtering out flags supported on 0 nodes
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (17 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH pve-manager v5 18/21] ui: cpu flags selector: add CPU flag editor for custom models Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-manager v5 20/21] ui: cpu flags selector: add search bar for large lists of flags Arthur Bied-Charreton
                   ` (2 subsequent siblings)
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

The cpu-flags endpoints may return some CPU flags that are not supported
on any node in the cluster.  Filter those out by default in the UI,
giving the option to display them via a checkbox.

Unknown flags are still shown at the top of the list.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 www/manager6/form/VMCPUFlagSelector.js | 46 ++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/www/manager6/form/VMCPUFlagSelector.js b/www/manager6/form/VMCPUFlagSelector.js
index e08a911b..f7a56b5a 100644
--- a/www/manager6/form/VMCPUFlagSelector.js
+++ b/www/manager6/form/VMCPUFlagSelector.js
@@ -40,6 +40,18 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
         },
     },
 
+    supportedFilterFn: function (rec) {
+        let state = rec.get('state');
+        if (state && state !== '=') {
+            return true;
+        }
+        if (rec.get('unknown')) {
+            return true;
+        }
+        let s = rec.get('supported-on');
+        return Array.isArray(s) && s.length > 0;
+    },
+
     getValue: function () {
         let me = this;
         let store = me.getStore();
@@ -265,10 +277,44 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
 
         me.value = me.originalValue = '';
 
+        me.dockedItems = [{
+            xtype: 'toolbar',
+            dock: 'bottom',
+            padding: '0 5',
+            items: [
+                {
+                    xtype: 'checkbox',
+                    checked: true,
+                    submitValue: false,
+                    isFormField: false,
+                    boxLabel: gettext('Only show flags supported by at least one node'),
+                    listeners: {
+                        change: function (cb, checked) {
+                            let grid = cb.up('grid');
+                            let store = grid.getStore();
+                            if (checked) {
+                                store.addFilter({
+                                    id: 'supported-filter',
+                                    filterFn: grid.supportedFilterFn,
+                                });
+                            } else {
+                                store.removeFilter('supported-filter');
+                            }
+                        },
+                    },
+                },
+            ],
+        }];
+
         me.callParent(arguments);
 
         me.initialized = true;
 
+        me.getStore().addFilter({
+            id: 'supported-filter',
+            filterFn: me.supportedFilterFn,
+        });
+
         me.getStore().on('load', function (store, _, success) {
             if (success) {
                 me.adjustStoreForValue();
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH pve-manager v5 20/21] ui: cpu flags selector: add search bar for large lists of flags
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (18 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH pve-manager v5 19/21] ui: cpu flags selector: allow filtering out flags supported on 0 nodes Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15  9:28 ` [PATCH pve-manager v5 21/21] ui: group custom CPU with resource mappings Arthur Bied-Charreton
  2026-05-15 17:05 ` [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Max R. Carrara
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

Add a search bar for the CPU flags list queried from
/cluster/qemu/cpu-flags, which is very long.

Do not display it for VM-specific flags (~15 flags), as they are
easier to scan through, and it would overload the ProcessorEdit window.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 www/manager6/form/VMCPUFlagSelector.js | 48 ++++++++++++++++++++++++--
 1 file changed, 45 insertions(+), 3 deletions(-)

diff --git a/www/manager6/form/VMCPUFlagSelector.js b/www/manager6/form/VMCPUFlagSelector.js
index f7a56b5a..574d44e0 100644
--- a/www/manager6/form/VMCPUFlagSelector.js
+++ b/www/manager6/form/VMCPUFlagSelector.js
@@ -62,7 +62,11 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
 
         let flags = '';
 
-        store.getData().each(function (rec) {
+        // Get the values directly from the data source. Using store.getData() here
+        // would iterate over the filtered values, potentially overwriting flags that
+        // are set but currently filtered out by the search bar.
+        let source = store.getDataSource();
+        source.each(function (rec) {
             let s = rec.get('state');
             if (s && s !== '=') {
                 let f = rec.get('name');
@@ -277,7 +281,45 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
 
         me.value = me.originalValue = '';
 
-        me.dockedItems = [{
+        me.dockedItems = [];
+
+        if (!me.restrictToVMFlags) {
+            me.dockedItems.push({
+                xtype: 'toolbar',
+                dock: 'top',
+                items: {
+                    xtype: 'textfield',
+                    emptyText: gettext('Search'),
+                    submitValue: false,
+                    listeners: {
+                        change: {
+                            buffer: 100,
+                            fn: function (field, value) {
+                                let store = field.up('grid').getStore();
+                                if (value) {
+                                    let lv = value.toLowerCase();
+                                    store.addFilter({
+                                        id: 'search-filter',
+                                        filterFn: (rec) => {
+                                            let name = rec.get('name') || '';
+                                            let desc = rec.get('description') || '';
+                                            return (
+                                                name.toLowerCase().includes(lv) ||
+                                                desc.toLowerCase().includes(lv)
+                                            );
+                                        },
+                                    });
+                                } else {
+                                    store.removeFilter('search-filter');
+                                }
+                            },
+                        },
+                    },
+                },
+            });
+        }
+
+        me.dockedItems.push({
             xtype: 'toolbar',
             dock: 'bottom',
             padding: '0 5',
@@ -304,7 +346,7 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
                     },
                 },
             ],
-        }];
+        });
 
         me.callParent(arguments);
 
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH pve-manager v5 21/21] ui: group custom CPU with resource mappings
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (19 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH pve-manager v5 20/21] ui: cpu flags selector: add search bar for large lists of flags Arthur Bied-Charreton
@ 2026-05-15  9:28 ` Arthur Bied-Charreton
  2026-05-15 17:05 ` [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Max R. Carrara
  21 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:28 UTC (permalink / raw)
  To: pve-devel

As suggested by Fiona on the list [0], create a
"Guest Resources/Hardware" group with "Custom CPU models", "Directory
Mappings", and "Resource Mappings" in the Datacenter menu.

[0]
https://lore.proxmox.com/pve-devel/b9274cb9-0ae1-46aa-a4b5-e10251c1a948@proxmox.com/T/#u

Suggested-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
---
 www/manager6/dc/Config.js | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index cbd04066..cb2937a5 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -391,6 +391,21 @@ Ext.define('PVE.dc.Config', {
             );
         }
 
+        if (
+            caps.mapping['Mapping.Audit'] ||
+            caps.mapping['Mapping.Use'] ||
+            caps.mapping['Mapping.Modify'] ||
+            caps.dc['Sys.Audit']
+        ) {
+            me.items.push({
+                xtype: 'panel',
+                title: gettext('Guest Resources/Hardware'),
+                iconCls: 'fa fa-sitemap',
+                itemId: 'guestresourcesandhardware',
+                expandedOnInit: true,
+            });
+        }
+
         if (
             caps.mapping['Mapping.Audit'] ||
             caps.mapping['Mapping.Use'] ||
@@ -399,6 +414,7 @@ Ext.define('PVE.dc.Config', {
             me.items.push(
                 {
                     xtype: 'container',
+                    groups: ['guestresourcesandhardware'],
                     onlineHelp: 'resource_mapping',
                     title: gettext('Resource Mappings'),
                     itemId: 'resources',
@@ -432,6 +448,7 @@ Ext.define('PVE.dc.Config', {
                 },
                 {
                     xtype: 'pveDcDirMapView',
+                    groups: ['guestresourcesandhardware'],
                     itemId: 'directories',
                     title: gettext('Directory Mappings'),
                     iconCls: 'fa fa-folder',
@@ -442,6 +459,7 @@ Ext.define('PVE.dc.Config', {
         if (caps.dc['Sys.Audit']) {
             me.items.push({
                 xtype: 'pveCPUTypeView',
+                groups: ['guestresourcesandhardware'],
                 iconCls: 'fa fa-microchip',
                 title: gettext('Custom CPU models'),
                 itemId: 'cputypes',
-- 
2.47.3




^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH pve-manager v5 14/21] api: add endpoint querying available CPU flags cluster-wide
  2026-05-15  9:28 ` [PATCH pve-manager v5 14/21] api: add endpoint querying available CPU flags cluster-wide Arthur Bied-Charreton
@ 2026-05-15  9:45   ` Arthur Bied-Charreton
  0 siblings, 0 replies; 24+ messages in thread
From: Arthur Bied-Charreton @ 2026-05-15  9:45 UTC (permalink / raw)
  To: pve-devel

On Fri, May 15, 2026 at 11:28:31AM +0200, Arthur Bied-Charreton wrote:
> Add endpoint at `/cluster/qemu/cpu-flags` allowing to query the CPU
> flags available on the whole cluster, filtered by acceleration type
> (kvm/tcg).
> 
> For each flag, a list of nodes supporting it is returned. This gives an
> accurate picture of which flags will actually be accepted by QEMU when
> starting a VM.
I meant to reword that, sorry - since the endpoint also returns the
'nested-virt' flag, this is not really true, should rather be 'accepted
by PVE in a processor config'.
> 
> Currently only return available flags for x86_64, see documentation of
> query_available_cpu_flags.
> 
> Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
[...]




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models
  2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
                   ` (20 preceding siblings ...)
  2026-05-15  9:28 ` [PATCH pve-manager v5 21/21] ui: group custom CPU with resource mappings Arthur Bied-Charreton
@ 2026-05-15 17:05 ` Max R. Carrara
  21 siblings, 0 replies; 24+ messages in thread
From: Max R. Carrara @ 2026-05-15 17:05 UTC (permalink / raw)
  To: Arthur Bied-Charreton, pve-devel

On Fri May 15, 2026 at 11:28 AM CEST, Arthur Bied-Charreton wrote:
> This picks up and extends an old series [0] by Stefan Reiter.
>
> This series adds a full CRUD API and a UI editor for custom CPU models
> which allows users to manage them in the Datacenter interface rather
> than editing /etc/pve/virtual-guest/cpu-models.conf manually.
>
> [...]

Did some preliminary testing and tried to configure a custom CPU based
on the 'x86-64-v4' model. Unfortunately it seems like that model isn't
included in the schema:

```
Parameter verification failed. (400)

reported-model: value 'x86-64-v4' does not have a value in the
enumeration '486, a64fx, athlon, Broadwell, Broadwell-IBRS,
Broadwell-noTSX, Broadwell-noTSX-IBRS, Cascadelake-Server,
Cascadelake-Server-noTSX, Cascadelake-Server-v2, Cascadelake-Server-v4,
Cascadelake-Server-v5, ClearwaterForest, Conroe, Cooperlake,
Cooperlake-v2, core2duo, coreduo, cortex-a35, cortex-a53, cortex-a55,
cortex-a57, cortex-a710, cortex-a72, cortex-a76, EPYC, EPYC-Genoa,
EPYC-Genoa-v2, EPYC-IBPB, EPYC-Milan, EPYC-Milan-v2, EPYC-Milan-v3,
EPYC-Rome, EPYC-Rome-v2, EPYC-Rome-v3, EPYC-Rome-v4, EPYC-Rome-v5,
EPYC-Turin, EPYC-v3, EPYC-v4, EPYC-v5, GraniteRapids, GraniteRapids-v2,
GraniteRapids-v3, Haswell, Haswell-IBRS, Haswell-noTSX,
Haswell-noTSX-IBRS, host, Icelake-Client, Icelake-Client-noTSX,
Icelake-Server, Icelake-Server-noTSX, Icelake-Server-v3,
Icelake-Server-v4, Icelake-Server-v5, Icelake-Server-v6,
Icelake-Server-v7, IvyBridge, IvyBridge-IBRS, KnightsMill, kvm32, kvm64,
max, Nehalem, Nehalem-IBRS, neoverse-n1, neoverse-n2, neoverse-v1,
Opteron_G1, Opteron_G2, Opteron_G3, Opteron_G4, Opteron_G5, Penryn,
pentium, pentium2, pentium3, phenom, qemu32, qemu64, SandyBridge,
SandyBridge-IBRS, SapphireRapids, SapphireRapids-v2, SapphireRapids-v3,
SapphireRapids-v4, SierraForest, SierraForest-v2, SierraForest-v3,
Skylake-Client, Skylake-Client-IBRS, Skylake-Client-noTSX-IBRS,
Skylake-Client-v4, Skylake-Server, Skylake-Server-IBRS,
Skylake-Server-noTSX-IBRS, Skylake-Server-v4, Skylake-Server-v5,
Westmere, Westmere-IBRS'
```

Choosing a different CPU type made it work.

To test whether the flags are actually passed on to the VM, I gave my
custom model the 'nested-virt' flag and checked whether it appears after
restarting the VM using `lscpu | grep -E '(svm|vmx)'`.

Indeed it does appear; when setting the CPU model back to
'x86-64-v2-AES' again, the flag disappears once rebooted.

So, the series seems to work; just that there's a schema issue.

Pretty neat overall!




^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2026-05-15 17:05 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-15  9:28 [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-docs v5 01/21] qm: add anchor to "CPU Type" section Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH qemu-server v5 02/21] cpu config: rename CPU models config path variable Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH qemu-server v5 03/21] cpu flags: move cpu flags-related utilities to their own module Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH qemu-server v5 04/21] cpu flags: compare against JSON::true when querying supported flags Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH qemu-server v5 05/21] cpu flags: normalize CPU flags to QEMU's format Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH qemu-server v5 06/21] cpu flags: add helper querying CPU flags with nodes supporting them Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH qemu-server v5 07/21] cpu config: rename custom CPU model config loader Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH qemu-server v5 08/21] cpu config: add helpers to lock and write config Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH qemu-server v5 09/21] cpu: register standard option for CPU format Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH qemu-server v5 10/21] api: cpu flags: improve flags list returned by endpoint Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH qemu-server v5 11/21] custom cpu models: avoid redundant config load Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-manager v5 12/21] cluster: reorder imports Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-manager v5 13/21] cluster: makefile: reorder perl sources and align backslashes Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-manager v5 14/21] api: add endpoint querying available CPU flags cluster-wide Arthur Bied-Charreton
2026-05-15  9:45   ` Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-manager v5 15/21] api: add CRUD handlers for custom CPU models Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-manager v5 16/21] ui: cpu model selector: allow filtering out custom models Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-manager v5 17/21] ui: add basic custom CPU model editor Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-manager v5 18/21] ui: cpu flags selector: add CPU flag editor for custom models Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-manager v5 19/21] ui: cpu flags selector: allow filtering out flags supported on 0 nodes Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-manager v5 20/21] ui: cpu flags selector: add search bar for large lists of flags Arthur Bied-Charreton
2026-05-15  9:28 ` [PATCH pve-manager v5 21/21] ui: group custom CPU with resource mappings Arthur Bied-Charreton
2026-05-15 17:05 ` [PATCH docs/manager/qemu-server v5 00/21] Add API and UI for custom CPU models Max R. Carrara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal