public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI
@ 2021-10-28 11:41 Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC pve-qemu 1/9] Add -list-flags command line option Stefan Reiter
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Stefan Reiter @ 2021-10-28 11:41 UTC (permalink / raw)
  To: pve-devel

For someone to pick up, please :)

This is some rather old code I had lying around, rebased and slightly fixed up
to build. Doesn't currently work as intended, but gets somewhat close.

Includes the API (mostly stable AFAICT, only some stuff broken during the time
it was lying around) and the GUI (structure looks decent, but editor window is
bugged heavily).

The QEMU patch is necessary to query the CPU flags added by default CPU and
machine type combinations, it could also be exported the same way we do for the
"all recognized flags" file, but that would be quite many files and would have
to stay up-to-date with supported CPU models in QEMU.


 pve-qemu: Stefan Reiter (1):
  Add -list-flags command line option

 ...E-Add-list-flags-command-line-option.patch | 213 ++++++++++++++++++
 debian/patches/series                         |   1 +
 2 files changed, 214 insertions(+)
 create mode 100644 debian/patches/pve/0049-PVE-Add-list-flags-command-line-option.patch

 qemu-server: Stefan Reiter (3):
  api: add recognized-flags and supported-flags endpoints
  api: add /cpu/model/* get endpoint
  api: add /cpu/model/* get/create/delete/update endpoints

 PVE/API2/Qemu/CPU.pm        | 334 +++++++++++++++++++++++++++++++++++-
 PVE/QemuServer/CPUConfig.pm |  24 ++-
 2 files changed, 356 insertions(+), 2 deletions(-)

 manager: Stefan Reiter (5):
  gui: VMCPUFlagSelector: fix unknownFlags behaviour
  gui: CPUModelSelector: fix dirty state on default
  gui: CPUModelSelector: add 'allowCustom'
  gui: add basic custom CPU model editor
  Initial attempt at CPU flag editor for custom models

 www/css/ext6-pve.css                   |   4 +
 www/manager6/Makefile                  |   3 +
 www/manager6/dc/CPUTypeEdit.js         |  89 +++++++++++++++
 www/manager6/dc/CPUTypeView.js         | 148 +++++++++++++++++++++++++
 www/manager6/dc/Config.js              |   6 +
 www/manager6/form/CPUModelSelector.js  |  22 ++++
 www/manager6/form/PhysBitsSelector.js  | 128 +++++++++++++++++++++
 www/manager6/form/VMCPUFlagSelector.js | 127 ++++++++++++++++-----
 8 files changed, 501 insertions(+), 26 deletions(-)
 create mode 100644 www/manager6/dc/CPUTypeEdit.js
 create mode 100644 www/manager6/dc/CPUTypeView.js
 create mode 100644 www/manager6/form/PhysBitsSelector.js

-- 
2.30.2




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

* [pve-devel] [RFC pve-qemu 1/9] Add -list-flags command line option
  2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
@ 2021-10-28 11:41 ` Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 2/9] api: add recognized-flags and supported-flags endpoints Stefan Reiter
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Reiter @ 2021-10-28 11:41 UTC (permalink / raw)
  To: pve-devel

Useful for retrieving flags from default CPU models.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
 ...E-Add-list-flags-command-line-option.patch | 213 ++++++++++++++++++
 debian/patches/series                         |   1 +
 2 files changed, 214 insertions(+)
 create mode 100644 debian/patches/pve/0049-PVE-Add-list-flags-command-line-option.patch

diff --git a/debian/patches/pve/0049-PVE-Add-list-flags-command-line-option.patch b/debian/patches/pve/0049-PVE-Add-list-flags-command-line-option.patch
new file mode 100644
index 0000000..56ced90
--- /dev/null
+++ b/debian/patches/pve/0049-PVE-Add-list-flags-command-line-option.patch
@@ -0,0 +1,213 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Stefan Reiter <s.reiter@proxmox.com>
+Date: Wed, 27 May 2020 13:51:52 +0200
+Subject: [PATCH] PVE: Add -list-flags command line option
+
+Prints all flags for a given cpu model to stdout.
+
+Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
+---
+ include/sysemu/cpus.h    |  1 +
+ qemu-options.hx          |  8 ++++++
+ softmmu/cpus.c           | 10 +++++++
+ softmmu/vl.c             | 20 ++++++++++++++
+ target/i386/cpu-sysemu.c |  2 +-
+ target/i386/cpu.c        | 58 ++++++++++++++++++++++++++++++++++++++++
+ target/i386/cpu.h        |  4 +++
+ 7 files changed, 102 insertions(+), 1 deletion(-)
+
+diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h
+index 868f1192de..433265b166 100644
+--- a/include/sysemu/cpus.h
++++ b/include/sysemu/cpus.h
+@@ -56,5 +56,6 @@ extern int smp_threads;
+ #endif
+ 
+ void list_cpus(const char *optarg);
++int list_cpu_flags(const char *model_name, Error **errp);
+ 
+ #endif
+diff --git a/qemu-options.hx b/qemu-options.hx
+index 83aa59a920..33a8a31aa4 100644
+--- a/qemu-options.hx
++++ b/qemu-options.hx
+@@ -137,6 +137,14 @@ SRST
+     selection)
+ ERST
+ 
++DEF("list-flags", 0, QEMU_OPTION_list_flags,
++    "-list-flags     list all flags of specified -cpu on stdout, then exit. Respects -machine versions.\n", QEMU_ARCH_I386)
++SRST
++``-list-flags``
++    List all flags of specified -cpu on stdout, then exit. Respects
++    -machine versions.
++ERST
++
+ DEF("accel", HAS_ARG, QEMU_OPTION_accel,
+     "-accel [accel=]accelerator[,prop[=value][,...]]\n"
+     "                select accelerator (kvm, xen, hax, hvf, nvmm, whpx or tcg; use 'help' for a list)\n"
+diff --git a/softmmu/cpus.c b/softmmu/cpus.c
+index 071085f840..f8351e2154 100644
+--- a/softmmu/cpus.c
++++ b/softmmu/cpus.c
+@@ -726,6 +726,16 @@ void list_cpus(const char *optarg)
+ #endif
+ }
+ 
++int list_cpu_flags(const char *model_name, Error **errp)
++{
++#if defined(cpu_list_flags)
++    return cpu_list_flags(model_name, errp);
++#endif
++
++    fprintf(stderr, "-list-flags not available for this architecture\n");
++    return 1;
++}
++
+ void qmp_memsave(int64_t addr, int64_t size, const char *filename,
+                  bool has_cpu, int64_t cpu_index, Error **errp)
+ {
+diff --git a/softmmu/vl.c b/softmmu/vl.c
+index 5ca11e7469..f47f076a1f 100644
+--- a/softmmu/vl.c
++++ b/softmmu/vl.c
+@@ -145,6 +145,7 @@ typedef struct ObjectOption {
+ } ObjectOption;
+ 
+ static const char *cpu_option;
++bool list_cpu_flags_opt = false;
+ static const char *mem_path;
+ static const char *incoming;
+ static const char *loadvm;
+@@ -2817,6 +2818,9 @@ void qemu_init(int argc, char **argv, char **envp)
+                 /* hw initialization will check this */
+                 cpu_option = optarg;
+                 break;
++            case QEMU_OPTION_list_flags:
++                list_cpu_flags_opt = true;
++                break;
+             case QEMU_OPTION_hda:
+             case QEMU_OPTION_hdb:
+             case QEMU_OPTION_hdc:
+@@ -3635,6 +3639,22 @@ void qemu_init(int argc, char **argv, char **envp)
+     }
+     trace_init_file();
+ 
++    /* list flags if requested, then exit */
++    if (list_cpu_flags_opt) {
++        if (!cpu_option) {
++            printf("-cpu argument required for -list-flags\n");
++            exit(1);
++        }
++
++        /* Check for and remove cpu parameters other than model */
++        char *commaIdx = strchr(cpu_option, ',');
++        if (commaIdx != NULL) {
++            *commaIdx = 0;
++        }
++
++        exit(list_cpu_flags(cpu_option, &error_fatal));
++    }
++
+     qemu_init_main_loop(&error_fatal);
+     cpu_timers_init();
+ 
+diff --git a/target/i386/cpu-sysemu.c b/target/i386/cpu-sysemu.c
+index 1078e3d157..1ebff3d847 100644
+--- a/target/i386/cpu-sysemu.c
++++ b/target/i386/cpu-sysemu.c
+@@ -142,7 +142,7 @@ static void object_apply_props(Object *obj, QDict *props, Error **errp)
+ }
+ 
+ /* Create X86CPU object according to model+props specification */
+-static X86CPU *x86_cpu_from_model(const char *model, QDict *props, Error **errp)
++X86CPU *x86_cpu_from_model(const char *model, QDict *props, Error **errp)
+ {
+     X86CPU *xc = NULL;
+     X86CPUClass *xcc;
+diff --git a/target/i386/cpu.c b/target/i386/cpu.c
+index 34a7ce865b..a972dff4ed 100644
+--- a/target/i386/cpu.c
++++ b/target/i386/cpu.c
+@@ -6855,3 +6855,61 @@ static void x86_cpu_register_types(void)
+ }
+ 
+ type_init(x86_cpu_register_types)
++
++int x86_cpu_list_flags(const char *model_name, Error **errp) {
++    X86CPU *cpu;
++
++    cpu = x86_cpu_from_model(model_name, NULL, errp);
++    if (!cpu) {
++        fprintf(stderr, "Unknown CPU model '%s'\n", model_name);
++        return 1;
++    }
++
++    strList *result = NULL;
++    x86_cpu_list_feature_names(cpu->env.features, &result);
++    if (!result) {
++        fprintf(stderr, "No flags found for CPU model '%s'\n", model_name);
++        return 1;
++    }
++
++    strList *flag = result;
++    strList *seen = NULL;
++    strList **seenPtr;
++
++    while (flag) {
++        /* with 'host' and 'max' there are some flags without a value */
++        if (!flag->value) {
++            goto nextLoop;
++        }
++
++        /* don't print duplicates */
++        bool newFlag = true;
++        seenPtr = &seen;
++        while (*seenPtr) {
++            if (newFlag && strcmp((*seenPtr)->value, flag->value) == 0) {
++                newFlag = false;
++            }
++            seenPtr = &(*seenPtr)->next;
++        }
++
++        if (newFlag) {
++            printf("%s ", flag->value);
++
++            /* insert into 'seen' list; seenPtr points to first empty '->next'
++             * after loop above, so we can insert there directly */
++            strList *new = g_new0(strList, 1);
++            new->value = g_strdup(flag->value);
++            *seenPtr = new;
++        }
++
++nextLoop:
++        flag = flag->next;
++    }
++    printf("\n");
++
++    if (seen) {
++        g_free(seen);
++    }
++    g_free(result);
++    return 0;
++}
+diff --git a/target/i386/cpu.h b/target/i386/cpu.h
+index 6c50d3ab4f..bb88f53a2d 100644
+--- a/target/i386/cpu.h
++++ b/target/i386/cpu.h
+@@ -2013,6 +2013,7 @@ uint64_t cpu_get_tsc(CPUX86State *env);
+ 
+ #define cpu_signal_handler cpu_x86_signal_handler
+ #define cpu_list x86_cpu_list
++#define cpu_list_flags x86_cpu_list_flags
+ 
+ /* MMU modes definitions */
+ #define MMU_KSMAP_IDX   0
+@@ -2246,4 +2247,7 @@ static inline uint64_t cr4_reserved_bits(CPUX86State *env)
+ # define TARGET_VSYSCALL_PAGE  (UINT64_C(-10) << 20)
+ #endif
+ 
++X86CPU *x86_cpu_from_model(const char *model, QDict *props, Error **errp);
++int x86_cpu_list_flags(const char *model_name, Error **errp);
++
+ #endif /* I386_CPU_H */
diff --git a/debian/patches/series b/debian/patches/series
index fb5c213..70ce86e 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -59,3 +59,4 @@ pve/0045-block-io-accept-NULL-qiov-in-bdrv_pad_request.patch
 pve/0046-block-add-alloc-track-driver.patch
 pve/0047-PVE-whitelist-invalid-QAPI-names-for-backwards-compa.patch
 pve/0048-PVE-savevm-async-register-yank-before-migration_inco.patch
+pve/0049-PVE-Add-list-flags-command-line-option.patch
-- 
2.30.2





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

* [pve-devel] [RFC qemu-server 2/9] api: add recognized-flags and supported-flags endpoints
  2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC pve-qemu 1/9] Add -list-flags command line option Stefan Reiter
@ 2021-10-28 11:41 ` Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 3/9] api: add /cpu/model/* get endpoint Stefan Reiter
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Reiter @ 2021-10-28 11:41 UTC (permalink / raw)
  To: pve-devel

For supporting a GUI to easily create custom CPU models based on cluster
CPU availability.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
 PVE/API2/Qemu/CPU.pm | 86 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)

diff --git a/PVE/API2/Qemu/CPU.pm b/PVE/API2/Qemu/CPU.pm
index b0bb32d..610ffb9 100644
--- a/PVE/API2/Qemu/CPU.pm
+++ b/PVE/API2/Qemu/CPU.pm
@@ -5,6 +5,7 @@ use warnings;
 
 use PVE::RESTHandler;
 use PVE::JSONSchema qw(get_standard_option);
+use PVE::QemuServer;
 use PVE::QemuServer::CPUConfig;
 
 use base qw(PVE::RESTHandler);
@@ -58,4 +59,89 @@ __PACKAGE__->register_method({
 	return PVE::QemuServer::CPUConfig::get_cpu_models($include_custom);
     }});
 
+__PACKAGE__->register_method({
+    name => 'recognized-flags',
+    path => 'recognized-flags',
+    method => 'GET',
+    description => 'List all CPU flags recognized by QEMU on this node.',
+    permissions => {
+	check => [ 'perm', '/nodes', ['Sys.Audit'] ],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => 'string',
+	},
+    },
+    code => sub {
+	return PVE::QemuServer::query_understood_cpu_flags();
+    }});
+
+__PACKAGE__->register_method({
+    name => 'supported-flags',
+    path => 'supported-flags',
+    method => 'GET',
+    description => 'List all CPU flags actually supported on each node.',
+    permissions => {
+	check => [ 'perm', '/nodes', ['Sys.Audit'] ],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => 'object',
+	    properties => {
+		node => get_standard_option('pve-node'),
+		kvm => {
+		    type => 'array',
+		    items => {
+			type => 'string',
+		    },
+		},
+		tcg => {
+		    type => 'array',
+		    items => {
+			type => 'string',
+		    },
+		},
+	    },
+	},
+    },
+    code => sub {
+	my $retval = [];
+
+	# Note: These get_node_kv calls can potentially race if the KV store
+	# gets updated inbetween. This is okay, since it is very unlikely to
+	# happen (these entries rarely change), and this API call should only
+	# be used to inform a user anyway
+	my $flags_kvm = PVE::Cluster::get_node_kv("cpuflags-kvm");
+	my $flags_tcg = PVE::Cluster::get_node_kv("cpuflags-tcg");
+
+	for my $node (keys %$flags_kvm) {
+	    next if !exists $flags_tcg->{$node};
+
+	    my @split_kvm = split(' ', $flags_kvm->{$node});
+	    my @split_tcg = split(' ', $flags_tcg->{$node});
+
+	    push @$retval, {
+		node => $node,
+		kvm => \@split_kvm,
+		tcg => \@split_tcg,
+	    };
+	}
+
+	return $retval;
+    }});
+
 1;
-- 
2.30.2





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

* [pve-devel] [RFC qemu-server 3/9] api: add /cpu/model/* get endpoint
  2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC pve-qemu 1/9] Add -list-flags command line option Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 2/9] api: add recognized-flags and supported-flags endpoints Stefan Reiter
@ 2021-10-28 11:41 ` Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 4/9] api: add /cpu/model/* get/create/delete/update endpoints Stefan Reiter
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Reiter @ 2021-10-28 11:41 UTC (permalink / raw)
  To: pve-devel

For custom models, this returns the saved configuration, for default
models it returns the cputype, vendor and a list of flags that the guest
OS will see when running with this CPU type (using the -list-flags QEMU
parameter).

For custom models it also includes the digest of the whole config, in
case someone retrieved it to modify it with a PUT call afterwards.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
 PVE/API2/Qemu/CPU.pm        | 76 ++++++++++++++++++++++++++++++++++++-
 PVE/QemuServer/CPUConfig.pm |  7 ++++
 2 files changed, 82 insertions(+), 1 deletion(-)

diff --git a/PVE/API2/Qemu/CPU.pm b/PVE/API2/Qemu/CPU.pm
index 610ffb9..9cc1d77 100644
--- a/PVE/API2/Qemu/CPU.pm
+++ b/PVE/API2/Qemu/CPU.pm
@@ -5,8 +5,10 @@ use warnings;
 
 use PVE::RESTHandler;
 use PVE::JSONSchema qw(get_standard_option);
+use PVE::INotify;
 use PVE::QemuServer;
 use PVE::QemuServer::CPUConfig;
+use PVE::Tools qw(run_command get_host_arch);
 
 use base qw(PVE::RESTHandler);
 
@@ -49,7 +51,7 @@ __PACKAGE__->register_method({
 		},
 	    },
 	},
-	links => [ { rel => 'child', href => '{name}' } ],
+	links => [ { rel => 'child', href => 'model/{name}' } ],
     },
     code => sub {
 	my $rpcenv = PVE::RPCEnvironment::get();
@@ -144,4 +146,76 @@ __PACKAGE__->register_method({
 	return $retval;
     }});
 
+__PACKAGE__->register_method({
+    name => 'info',
+    path => 'model/{cputype}',
+    method => 'GET',
+    description => 'Retrieve details about a specific CPU model.',
+    permissions => {
+	check => [ 'perm', '/nodes', ['Sys.Audit'] ],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	    cputype => {
+		type => 'string',
+		description => "Name of the CPU model to query. Prefix with"
+			     . " '-custom' for custom models.",
+	    },
+	},
+    },
+    returns => {
+	type => 'object',
+	properties => PVE::QemuServer::CPUConfig::add_cpu_json_properties({
+	    vendor => {
+		type => 'string',
+		description => 'The CPU vendor reported to the guest OS. Only'
+			    . ' relevant for default models.',
+		optional => 1,
+	    },
+	    digest => get_standard_option('pve-config-digest'),
+	}),
+    },
+    code => sub {
+	my ($param) = @_;
+	my $cputype = $param->{cputype};
+
+	if ($cputype =~ m/^custom-/) {
+	    my $conf = PVE::QemuServer::CPUConfig::load_custom_model_conf();
+	    my $digest = $conf->{digest};
+	    my $retval = PVE::QemuServer::CPUConfig::get_custom_model($cputype);
+	    $retval->{digest} = $digest;
+	    return $retval;
+	}
+
+	# this correctly errors in case $cputype is unknown
+	my $vendor = PVE::QemuServer::CPUConfig::get_cpu_vendor($cputype);
+
+	my $retval = {
+	    cputype => $cputype,
+	    vendor => $vendor,
+	};
+
+	# for built-in models return all flags that QEMU assigns to this model
+	# by default (note that these flags might still differ very slightly
+	# with certain machine versions, but since this output is only intended
+	# for assisting a user in a GUI environment this should be fine)
+	my @flags;
+	my $qemu_binary = PVE::QemuServer::get_command_for_arch(get_host_arch());
+	my $retcode = run_command([$qemu_binary, '-list-flags', '-cpu', $cputype],
+	    outfunc => sub { @flags = split(/\s/, shift); }, noerr => 1);
+
+	if ($retcode || !@flags) {
+	    my $nodename = PVE::INotify::nodename();
+	    warn "warning: could not retrieve QEMU flags for built-in cpu type"
+	       . " '$cputype'. Make sure node '$nodename' has the latest"
+	       . " pve-qemu-kvm.\n";
+	} else {
+	    $retval->{flags} = join(";", map {"+$_"} @flags);
+	}
+
+	return $retval;
+    }});
+
 1;
diff --git a/PVE/QemuServer/CPUConfig.pm b/PVE/QemuServer/CPUConfig.pm
index b9981c8..9be8022 100644
--- a/PVE/QemuServer/CPUConfig.pm
+++ b/PVE/QemuServer/CPUConfig.pm
@@ -320,6 +320,13 @@ sub is_custom_model {
     return $cputype =~ m/^custom-/;
 }
 
+sub get_cpu_vendor {
+    my ($cputype) = @_;
+    my $retval = $cpu_vendor_list->{$cputype} or
+	die "Built-in CPU type '$cputype' does not exist\n";
+    return $retval;
+}
+
 # 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 {
-- 
2.30.2





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

* [pve-devel] [RFC qemu-server 4/9] api: add /cpu/model/* get/create/delete/update endpoints
  2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
                   ` (2 preceding siblings ...)
  2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 3/9] api: add /cpu/model/* get endpoint Stefan Reiter
@ 2021-10-28 11:41 ` Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC manager 5/9] gui: VMCPUFlagSelector: fix unknownFlags behaviour Stefan Reiter
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Reiter @ 2021-10-28 11:41 UTC (permalink / raw)
  To: pve-devel

Standard API, loosely based on Storage config API code. Uses digests and
'delete'-parameter (parameters not given to an update call are
untouched).

Locking and writing helpers are added to CPUConfig. write_config is
fixed with adding 'type' to every section, otherwise SectionConfig
fails.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
 PVE/API2/Qemu/CPU.pm        | 174 +++++++++++++++++++++++++++++++++++-
 PVE/QemuServer/CPUConfig.pm |  17 +++-
 2 files changed, 189 insertions(+), 2 deletions(-)

diff --git a/PVE/API2/Qemu/CPU.pm b/PVE/API2/Qemu/CPU.pm
index 9cc1d77..8a3dcfd 100644
--- a/PVE/API2/Qemu/CPU.pm
+++ b/PVE/API2/Qemu/CPU.pm
@@ -8,7 +8,8 @@ use PVE::JSONSchema qw(get_standard_option);
 use PVE::INotify;
 use PVE::QemuServer;
 use PVE::QemuServer::CPUConfig;
-use PVE::Tools qw(run_command get_host_arch);
+use PVE::SectionConfig;
+use PVE::Tools qw(run_command get_host_arch extract_param);
 
 use base qw(PVE::RESTHandler);
 
@@ -218,4 +219,175 @@ __PACKAGE__->register_method({
 	return $retval;
     }});
 
+__PACKAGE__->register_method({
+    name => 'config',
+    path => 'model',
+    method => 'GET',
+    description => "Read all custom CPU model definitions.",
+    permissions => {
+	check => ['perm', '/nodes', ['Sys.Audit']],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	},
+    },
+    returns => { type => 'object' },
+    code => sub {
+	my $conf = PVE::QemuServer::CPUConfig::load_custom_model_conf();
+	delete $conf->{order};
+	my $ids = [];
+	foreach my $id (keys %{$conf->{ids}}) {
+	    delete $conf->{ids}->{$id}->{type};
+	    push @$ids, $conf->{ids}->{$id};
+	}
+	$conf->{ids} = $ids;
+	return $conf;
+    }});
+
+__PACKAGE__->register_method({
+    name => 'create',
+    path => 'model',
+    method => 'POST',
+    description => "Add a custom CPU model definition.",
+    permissions => {
+	check => ['perm', '/nodes', ['Sys.Console']],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => PVE::QemuServer::CPUConfig::add_cpu_json_properties({
+	    digest => get_standard_option('pve-config-digest'),
+	    node => get_standard_option('pve-node'),
+	}),
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+	delete $param->{node};
+
+	my $digest = extract_param($param, 'digest');
+
+	my $name = $param->{'cputype'};
+	die "custom cpu models 'cputype' must start with 'custom-' prefix\n"
+	    if $name !~ m/^custom-/;
+	(my $name_no_prefix = $name) =~ s/^custom-//;
+
+	PVE::QemuServer::CPUConfig::lock_cpu_config(sub {
+	    my $conf = PVE::QemuServer::CPUConfig::load_custom_model_conf();
+	    PVE::SectionConfig::assert_if_modified($conf, $digest);
+
+	    die "custom cpu model '$name' already exists\n"
+		if defined($conf->{ids}->{$name_no_prefix});
+	    $conf->{ids}->{$name_no_prefix} = $param;
+
+	    PVE::QemuServer::CPUConfig::write_custom_model_conf($conf);
+	});
+    }});
+
+__PACKAGE__->register_method({
+    name => 'delete',
+    path => 'model/{cputype}',
+    method => 'DELETE',
+    description => "Delete a custom CPU model definition.",
+    permissions => {
+	check => ['perm', '/nodes', ['Sys.Console']],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => {
+	    node => get_standard_option('pve-node'),
+	    digest => get_standard_option('pve-config-digest'),
+	    cputype => {
+		type => 'string',
+		format_description => 'string',
+		description => "The custom model to delete. Must be prefixed"
+			     . " with 'custom-'.",
+	    },
+	},
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $digest = extract_param($param, 'digest');
+
+	my $name = $param->{'cputype'};
+	die "cputype must start with 'custom-' prefix\n"
+	    if $name !~ m/^custom-/;
+	(my $name_no_prefix = $name) =~ s/^custom-//;
+
+	PVE::QemuServer::CPUConfig::lock_cpu_config(sub {
+	    my $conf = PVE::QemuServer::CPUConfig::load_custom_model_conf();
+	    PVE::SectionConfig::assert_if_modified($conf, $digest);
+
+	    die "custom cpu model '$name' doesn't exist\n"
+		if !defined($conf->{ids}->{$name_no_prefix});
+	    delete $conf->{ids}->{$name_no_prefix};
+
+	    PVE::QemuServer::CPUConfig::write_custom_model_conf($conf);
+	});
+    }});
+
+__PACKAGE__->register_method({
+    name => 'update',
+    path => 'model/{cputype}',
+    method => 'PUT',
+    description => "Update a custom CPU model definition.",
+    permissions => {
+	check => ['perm', '/nodes', ['Sys.Console']],
+    },
+    parameters => {
+	additionalProperties => 0,
+	properties => PVE::QemuServer::CPUConfig::add_cpu_json_properties({
+	    node => get_standard_option('pve-node'),
+	    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) = @_;
+	delete $param->{node};
+
+	my $digest = extract_param($param, 'digest');
+	my $delete = extract_param($param, 'delete') // '';
+	my %delete_hash = map { $_ => 1 } PVE::Tools::split_list($delete);
+
+	my $name = $param->{'cputype'};
+	die "custom cpu models 'cputype' must start with 'custom-' prefix\n"
+	    if $name !~ m/^custom-/;
+	(my $name_no_prefix = $name) =~ s/^custom-//;
+
+	PVE::QemuServer::CPUConfig::lock_cpu_config(sub {
+	    my $conf = PVE::QemuServer::CPUConfig::load_custom_model_conf();
+
+	    PVE::SectionConfig::assert_if_modified($conf, $digest);
+
+	    my $model = $conf->{ids}->{$name_no_prefix};
+	    die "custom cpu model '$name' doesn't exist\n"
+		if !defined($model);
+
+	    my $props = PVE::QemuServer::CPUConfig::add_cpu_json_properties({});
+	    for my $p (keys %$props) {
+		if ($delete_hash{$p}) {
+		    die "cannot delete 'cputype' property\n"
+			if $p eq 'cputype';
+		    die "cannot set and delete property '$p' at once\n"
+			if $param->{$p};
+		    delete $model->{$p};
+		} elsif (defined($param->{$p})) {
+		    $model->{$p} = $param->{$p};
+		}
+	    }
+
+	    PVE::QemuServer::CPUConfig::write_custom_model_conf($conf);
+	});
+    }});
+
 1;
diff --git a/PVE/QemuServer/CPUConfig.pm b/PVE/QemuServer/CPUConfig.pm
index 9be8022..08a174f 100644
--- a/PVE/QemuServer/CPUConfig.pm
+++ b/PVE/QemuServer/CPUConfig.pm
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 
 use PVE::JSONSchema;
-use PVE::Cluster qw(cfs_register_file cfs_read_file);
+use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
 use PVE::QemuServer::Helpers qw(min_version);
 
 use base qw(PVE::SectionConfig Exporter);
@@ -29,6 +29,19 @@ sub load_custom_model_conf {
     return cfs_read_file($default_filename);
 }
 
+sub write_custom_model_conf {
+    my ($conf) = @_;
+    cfs_write_file($default_filename, $conf);
+}
+
+sub lock_cpu_config {
+    my ($code) = @_;
+    cfs_lock_file($default_filename, undef, $code);
+    if (my $err = $@) {
+	die $err;
+    }
+}
+
 my $cpu_vendor_list = {
     # Intel CPUs
     486 => 'GenuineIntel',
@@ -270,6 +283,8 @@ sub write_config {
 
 	# saved in section header
 	delete $model_conf->{cputype};
+
+	$model_conf->{type} = $class->type();
     }
 
     $class->SUPER::write_config($filename, $cfg);
-- 
2.30.2





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

* [pve-devel] [RFC manager 5/9] gui: VMCPUFlagSelector: fix unknownFlags behaviour
  2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
                   ` (3 preceding siblings ...)
  2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 4/9] api: add /cpu/model/* get/create/delete/update endpoints Stefan Reiter
@ 2021-10-28 11:41 ` Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC manager 6/9] gui: CPUModelSelector: fix dirty state on default Stefan Reiter
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Reiter @ 2021-10-28 11:41 UTC (permalink / raw)
  To: pve-devel

...and typo in name.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
 www/manager6/form/VMCPUFlagSelector.js | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/www/manager6/form/VMCPUFlagSelector.js b/www/manager6/form/VMCPUFlagSelector.js
index ace3c531..a6d17930 100644
--- a/www/manager6/form/VMCPUFlagSelector.js
+++ b/www/manager6/form/VMCPUFlagSelector.js
@@ -14,7 +14,7 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
     scrollable: 'y',
     height: 200,
 
-    unkownFlags: [],
+    unknownFlags: [],
 
     store: {
 	type: 'store',
@@ -59,7 +59,7 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
 	    }
 	});
 
-	flags += me.unkownFlags.join(';');
+	flags += (flags.length > 0 ? ';' : '') + me.unknownFlags.join(';');
 
 	return flags;
     },
@@ -70,7 +70,7 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
 
 	me.value = value || '';
 
-	me.unkownFlags = [];
+	me.unknownFlags = [];
 
 	me.getStore().queryBy(Ext.returnTrue).each(function(rec) {
 	    rec.set('state', '=');
@@ -79,13 +79,13 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
 	var flags = value ? value.split(';') : [];
 	flags.forEach(function(flag) {
 	    var sign = flag.substr(0, 1);
-	    flag = flag.substr(1);
+	    let flagName = flag.substr(1);
 
-	    var rec = store.findRecord('flag', flag, 0, false, true, true);
+	    var rec = store.findRecord('flag', flagName, 0, false, true, true);
 	    if (rec !== null) {
 		rec.set('state', sign);
 	    } else {
-		me.unkownFlags.push(flag);
+		me.unknownFlags.push(flag);
 	    }
 	});
 	store.reload();
-- 
2.30.2





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

* [pve-devel] [RFC manager 6/9] gui: CPUModelSelector: fix dirty state on default
  2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
                   ` (4 preceding siblings ...)
  2021-10-28 11:41 ` [pve-devel] [RFC manager 5/9] gui: VMCPUFlagSelector: fix unknownFlags behaviour Stefan Reiter
@ 2021-10-28 11:41 ` Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC manager 7/9] gui: CPUModelSelector: add 'allowCustom' Stefan Reiter
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Reiter @ 2021-10-28 11:41 UTC (permalink / raw)
  To: pve-devel

"originalValue" is set to "null" in case it's the default, but
getValue() returns an empty string. This means that when editing a VM's
CPU config when the model is "default", the form would always be marked
dirty.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
 www/manager6/form/CPUModelSelector.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/www/manager6/form/CPUModelSelector.js b/www/manager6/form/CPUModelSelector.js
index a30dd1fb..a4d883a4 100644
--- a/www/manager6/form/CPUModelSelector.js
+++ b/www/manager6/form/CPUModelSelector.js
@@ -45,6 +45,12 @@ Ext.define('PVE.form.CPUModelSelector', {
 	width: 360,
     },
 
+    getValue: function() {
+	let me = this;
+	let val = me.callParent();
+	return val === "" ? null : val;
+    },
+
     store: {
 	autoLoad: true,
 	model: 'PVE.data.CPUModel',
-- 
2.30.2





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

* [pve-devel] [RFC manager 7/9] gui: CPUModelSelector: add 'allowCustom'
  2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
                   ` (5 preceding siblings ...)
  2021-10-28 11:41 ` [pve-devel] [RFC manager 6/9] gui: CPUModelSelector: fix dirty state on default Stefan Reiter
@ 2021-10-28 11:41 ` Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC manager 8/9] gui: add basic custom CPU model editor Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC manager 9/9] Initial attempt at CPU flag editor for custom models Stefan Reiter
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Reiter @ 2021-10-28 11:41 UTC (permalink / raw)
  To: pve-devel

When set to false, only default CPU models will be shown.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
 www/manager6/form/CPUModelSelector.js | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/www/manager6/form/CPUModelSelector.js b/www/manager6/form/CPUModelSelector.js
index a4d883a4..54286d79 100644
--- a/www/manager6/form/CPUModelSelector.js
+++ b/www/manager6/form/CPUModelSelector.js
@@ -25,6 +25,10 @@ Ext.define('PVE.form.CPUModelSelector', {
 
     deleteEmpty: true,
 
+    config: {
+	allowCustom: true,
+    },
+
     listConfig: {
 	columns: [
 	    {
@@ -107,4 +111,16 @@ Ext.define('PVE.form.CPUModelSelector', {
 	    },
 	},
     },
+
+    initComponent: function() {
+	let me = this;
+
+	if (!me.allowCustom) {
+	    me.store.filters = [{
+		filterFn: rec => !rec.data.custom,
+	    }];
+	}
+
+	me.callParent();
+    },
 });
-- 
2.30.2





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

* [pve-devel] [RFC manager 8/9] gui: add basic custom CPU model editor
  2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
                   ` (6 preceding siblings ...)
  2021-10-28 11:41 ` [pve-devel] [RFC manager 7/9] gui: CPUModelSelector: add 'allowCustom' Stefan Reiter
@ 2021-10-28 11:41 ` Stefan Reiter
  2021-10-28 11:41 ` [pve-devel] [RFC manager 9/9] Initial attempt at CPU flag editor for custom models Stefan Reiter
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Reiter @ 2021-10-28 11:41 UTC (permalink / raw)
  To: pve-devel

Supports viewing, deleting, adding and editing existing custom CPU
models. All properties except CPU flags are supported in the editor.

A new control for selecting between different Phys-Bits (default, host,
custom number) is added.

This also seems to be the first use of a non-fa icon in the sidebar, so
add a new class that correctly aligns pve-itype-icon-* elements.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
 www/css/ext6-pve.css                  |   4 +
 www/manager6/Makefile                 |   3 +
 www/manager6/dc/CPUTypeEdit.js        |  81 ++++++++++++++
 www/manager6/dc/CPUTypeView.js        | 148 ++++++++++++++++++++++++++
 www/manager6/dc/Config.js             |   6 ++
 www/manager6/form/PhysBitsSelector.js | 128 ++++++++++++++++++++++
 6 files changed, 370 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/css/ext6-pve.css b/www/css/ext6-pve.css
index e5d792f8..bb9bd8c0 100644
--- a/www/css/ext6-pve.css
+++ b/www/css/ext6-pve.css
@@ -447,6 +447,10 @@
     padding: 0;
 }
 
+.pve-icon-sidebar {
+    margin-top: 5px;
+}
+
 /* displayfield minheight is wrong */
 .x-form-display-field-default {
     min-height: 20px;
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index e5e85aed..abca0a05 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -48,6 +48,7 @@ JSSRC= 							\
 	form/NodeSelector.js				\
 	form/PCISelector.js				\
 	form/PermPathSelector.js			\
+	form/PhysBitsSelector.js			\
 	form/PoolSelector.js				\
 	form/PreallocationSelector.js			\
 	form/PrivilegesSelector.js			\
@@ -131,6 +132,8 @@ JSSRC= 							\
 	dc/ClusterEdit.js				\
 	dc/Config.js					\
 	dc/CorosyncLinkEdit.js				\
+	dc/CPUTypeView.js				\
+	dc/CPUTypeEdit.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..3ad527c4
--- /dev/null
+++ b/www/manager6/dc/CPUTypeEdit.js
@@ -0,0 +1,81 @@
+Ext.define('PVE.dc.CPUTypeEdit', {
+    extend: 'Proxmox.window.Edit',
+    alias: ['widget.pveCpuTypeEdit'],
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    subject: gettext('CPU Type'),
+
+    cbindData: {
+	cputype: '',
+	isCreate: (cfg) => !cfg.cputype,
+    },
+
+    cbind: {
+	autoLoad: get => !get('isCreate'),
+	url: get => `/api2/extjs/nodes/localhost/capabilities/qemu/cpu/model/${get('cputype')}`,
+	method: get => get('isCreate') ? 'POST' : 'PUT',
+	isCreate: get => get('isCreate'),
+    },
+
+    getValues: function() {
+	let me = this;
+	let values = me.callParent();
+
+	PVE.Utils.delete_if_default(values, 'reported-model', '', me.isCreate);
+	PVE.Utils.delete_if_default(values, 'hv-vendor-id', '', me.isCreate);
+	PVE.Utils.delete_if_default(values, 'phys-bits', '', me.isCreate);
+	PVE.Utils.delete_if_default(values, 'flags', '', me.isCreate);
+
+	if (me.isCreate && !values.cputype.match(/^custom-/)) {
+	    values.cputype = 'custom-' + values.cputype;
+	}
+
+	return values;
+    },
+
+    items: [
+	{
+	    xtype: 'inputpanel',
+	    column1: [
+		{
+		    xtype: 'pmxDisplayEditField',
+		    fieldLabel: gettext('Name'),
+		    cbind: {
+			editable: '{isCreate}',
+			value: '{cputype}',
+		    },
+		    name: 'cputype',
+		    allowBlank: false,
+		},
+		{
+		    xtype: 'CPUModelSelector',
+		    fieldLabel: gettext('Reported Model'),
+		    allowCustom: false,
+		    name: 'reported-model',
+		},
+		{
+		    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..0d560369
--- /dev/null
+++ b/www/manager6/dc/CPUTypeView.js
@@ -0,0 +1,148 @@
+Ext.define('PVE.dc.CPUTypeView', {
+    extend: 'Ext.grid.GridPanel',
+    alias: ['widget.pveCPUTypeView'],
+
+    onlineHelp: 'qm_cpu',
+
+    store: {
+	model: 'pve-custom-cpu-type',
+	proxy: {
+	    type: 'proxmox',
+	    url: "/api2/json/nodes/localhost/capabilities/qemu/cpu/model",
+	    root: 'data.ids',
+	},
+	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 = {};
+	    if (cputype) {
+		Ext.apply(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;
+	    let grid = me.getView();
+	    let store = grid.store;
+	    store.reload();
+	},
+    },
+
+    columns: [
+	{
+	    header: 'Name',
+	    flex: 1,
+	    sortable: true,
+	    dataIndex: 'cputype',
+	    renderer: val => val.replace(/^custom-/, ''),
+	},
+	{
+	    header: 'Reported Model',
+	    width: '80px',
+	    sortable: true,
+	    dataIndex: 'reported-model',
+	},
+	{
+	    header: 'Phys-Bits',
+	    width: '40px',
+	    sortable: true,
+	    dataIndex: 'phys-bits',
+	},
+	{
+	    header: 'Hidden',
+	    width: '40px',
+	    sortable: true,
+	    dataIndex: 'hidden',
+	},
+	{
+	    header: 'HyperV-Vendor',
+	    width: '80px',
+	    sortable: true,
+	    dataIndex: 'hv-vendor-id',
+	},
+	{
+	    header: 'Flags',
+	    flex: 2,
+	    sortable: true,
+	    dataIndex: 'flags',
+	},
+    ],
+
+    tbar: [
+	{
+	    text: gettext('Add'),
+	    handler: 'onAdd',
+	},
+	'-',
+	{
+	    xtype: 'proxmoxStdRemoveButton',
+	    baseurl: '/api2/extjs/nodes/localhost/capabilities/qemu/cpu/model/',
+	    getRecordName: (rec) => rec.data.cputype,
+	    getUrl: function(rec) {
+		let me = this;
+		return me.baseurl + rec.data.cputype;
+	    },
+	    callback: 'reload',
+	},
+	{
+	    text: gettext('Edit'),
+	    handler: 'onEdit',
+	},
+    ],
+
+    selModel: {
+	xtype: 'Ext.selection.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);
+    },
+
+}, function() {
+    Ext.define('pve-custom-cpu-type', {
+	extend: 'Ext.data.Model',
+	fields: [
+	    'cputype', 'reported-model', 'hv-vendor-id', 'flags', 'phys-bits',
+	    { name: 'hidden', type: 'boolean' },
+	],
+    });
+});
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 934952d9..cbaabb9e 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -70,6 +70,12 @@ Ext.define('PVE.dc.Config', {
 		title: gettext('Replication'),
 		itemId: 'replication',
 	    },
+	    {
+		xtype: 'pveCPUTypeView',
+		iconCls: 'pve-itype-icon-processor pve-icon pve-icon-sidebar',
+		title: gettext('CPU Types'),
+		itemId: 'cputypes',
+	    },
 	    {
 		xtype: 'pveACLView',
 		title: gettext('Permissions'),
diff --git a/www/manager6/form/PhysBitsSelector.js b/www/manager6/form/PhysBitsSelector.js
new file mode 100644
index 00000000..a18b675f
--- /dev/null
+++ b/www/manager6/form/PhysBitsSelector.js
@@ -0,0 +1,128 @@
+Ext.define('PVE.form.PhysBitsSelector', {
+    extend: 'Ext.form.FieldContainer',
+    alias: 'widget.PhysBitsSelector',
+    mixins: ['Ext.form.field.Field'],
+
+    layout: 'vbox',
+    initialValue: '',
+    originalValue: '',
+
+    controller: {
+	xclass: 'Ext.app.ViewController',
+
+	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 = me.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();
+	return ctrl.lookupReference('customNum').isValid();
+    },
+
+    items: [
+	{
+	    xtype: 'radiofield',
+	    boxLabel: gettext('Default'),
+	    inputValue: 'default',
+	    checked: true,
+	    reference: 'modeDefault',
+	    isFormField: false,
+	},
+	{
+	    xtype: 'radiofield',
+	    boxLabel: gettext('Host'),
+	    inputValue: 'host',
+	    reference: 'modeHost',
+	    isFormField: false,
+	},
+	{
+	    xtype: 'fieldcontainer',
+	    layout: 'hbox',
+	    items: [
+		{
+		    xtype: 'radiofield',
+		    boxLabel: gettext('Custom'),
+		    inputValue: 'custom',
+		    listeners: {
+			change: 'updateNumberField',
+		    },
+		    reference: 'modeCustom',
+		    isFormField: false,
+		},
+		{
+		    xtype: 'numberfield',
+		    width: '60px',
+		    margin: '0 0 0 10px',
+		    minValue: 8,
+		    maxValue: 64,
+		    reference: 'customNum',
+		    allowBlank: false,
+		    isFormField: false,
+		    disabled: true,
+		},
+	    ],
+	},
+    ],
+});
+
-- 
2.30.2





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

* [pve-devel] [RFC manager 9/9] Initial attempt at CPU flag editor for custom models
  2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
                   ` (7 preceding siblings ...)
  2021-10-28 11:41 ` [pve-devel] [RFC manager 8/9] gui: add basic custom CPU model editor Stefan Reiter
@ 2021-10-28 11:41 ` Stefan Reiter
  8 siblings, 0 replies; 10+ messages in thread
From: Stefan Reiter @ 2021-10-28 11:41 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---

I believe this one is the most outdated, it's original title in my working
branch was "tmp", dating back to more than a year ago ;)

 www/manager6/dc/CPUTypeEdit.js         |   8 ++
 www/manager6/form/VMCPUFlagSelector.js | 125 ++++++++++++++++++++-----
 2 files changed, 108 insertions(+), 25 deletions(-)

diff --git a/www/manager6/dc/CPUTypeEdit.js b/www/manager6/dc/CPUTypeEdit.js
index 3ad527c4..8479c61e 100644
--- a/www/manager6/dc/CPUTypeEdit.js
+++ b/www/manager6/dc/CPUTypeEdit.js
@@ -76,6 +76,14 @@ Ext.define('PVE.dc.CPUTypeEdit', {
 		    name: 'phys-bits',
 		},
 	    ],
+	    columnB: [
+		{
+		    xtype: 'vmcpuflagselector',
+		    fieldLabel: gettext('Extra CPU Flags'),
+		    name: 'flags',
+		    restrictToVMFlags: false,
+		},
+	    ],
 	},
     ],
 });
diff --git a/www/manager6/form/VMCPUFlagSelector.js b/www/manager6/form/VMCPUFlagSelector.js
index a6d17930..69ef2331 100644
--- a/www/manager6/form/VMCPUFlagSelector.js
+++ b/www/manager6/form/VMCPUFlagSelector.js
@@ -6,6 +6,10 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
 	field: 'Ext.form.field.Field',
     },
 
+    config: {
+	restrictToVMFlags: true,
+    },
+
     disableSelection: true,
     columnLines: false,
     selectable: false,
@@ -59,7 +63,7 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
 	    }
 	});
 
-	flags += (flags.length > 0 ? ';' : '') + me.unknownFlags.join(';');
+	flags += (me.unknownFlags.length > 0 ? ';' : '') + me.unknownFlags.join(';');
 
 	return flags;
     },
@@ -70,30 +74,22 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
 
 	me.value = value || '';
 
-	me.unknownFlags = [];
-
-	me.getStore().queryBy(Ext.returnTrue).each(function(rec) {
-	    rec.set('state', '=');
-	});
-
-	var flags = value ? value.split(';') : [];
-	flags.forEach(function(flag) {
-	    var sign = flag.substr(0, 1);
-	    let flagName = flag.substr(1);
-
-	    var rec = store.findRecord('flag', flagName, 0, false, true, true);
-	    if (rec !== null) {
-		rec.set('state', sign);
-	    } else {
-		me.unknownFlags.push(flag);
-	    }
-	});
-	store.reload();
+	// if the store is already reloading it will apply me.value to itself
+	// once it's done, otherwise trigger reload now
+	if (!store.isLoading()) {
+	    store.reload();
+	}
 
 	var res = me.mixins.field.setValue.call(me, value);
 
 	return res;
     },
+
+    isDirty: function() {
+	let me = this;
+	return me.originalValue !== me.getValue();
+    },
+
     columns: [
 	{
 	    dataIndex: 'state',
@@ -165,12 +161,91 @@ Ext.define('PVE.form.VMCPUFlagSelector', {
     ],
 
     initComponent: function() {
-	var me = this;
+	let me = this;
 
-	// static class store, thus gets not recreated, so ensure defaults are set!
-	me.getStore().data.forEach(function(v) {
-	    v.state = '=';
-	});
+	let data = [
+	    { flag: 'md-clear', desc: 'Required to let the guest OS know if MDS is mitigated correctly' },
+	    { flag: 'pcid', desc: 'Meltdown fix cost reduction on Westmere, Sandy-, and IvyBridge Intel CPUs' },
+	    { flag: 'spec-ctrl', desc: 'Allows improved Spectre mitigation with Intel CPUs' },
+	    { flag: 'ssbd', desc: 'Protection for "Speculative Store Bypass" for Intel models' },
+	    { flag: 'ibpb', desc: 'Allows improved Spectre mitigation with AMD CPUs' },
+	    { flag: 'virt-ssbd', desc: 'Basis for "Speculative Store Bypass" protection for AMD models' },
+	    { flag: 'amd-ssbd', desc: 'Improves Spectre mitigation performance with AMD CPUs, best used with "virt-ssbd"' },
+	    { flag: 'amd-no-ssb', desc: 'Notifies guest OS that host is not vulnerable for Spectre on AMD CPUs' },
+	    { flag: 'pdpe1gb', desc: 'Allow guest OS to use 1GB size pages, if host HW supports it' },
+	    { flag: 'hv-tlbflush', desc: 'Improve performance in overcommitted Windows guests. May lead to guest bluescreens on old CPUs.' },
+	    { flag: 'hv-evmcs', desc: 'Improve performance for nested virtualization. Only supported on Intel CPUs.' },
+	    { flag: 'aes', desc: 'Activate AES instruction set for HW acceleration.' },
+	];
+
+	let afterLoadFn = function(store) {
+	    // apply me.value to store after loading is complete
+	    me.unknownFlags = [];
+	    let flags = me.value ? me.value.split(';') : [];
+
+	    flags.forEach(function(flag) {
+		let sign = flag.substr(0, 1);
+		let flagName = flag.substr(1);
+
+		let rec = store.findRecord('flag', flagName);
+		if (rec) {
+		    rec.set('state', sign);
+		    rec.commit();
+		} else {
+		    me.unknownFlags.push(flag);
+		}
+	    });
+
+	    setTimeout(function() { me.getView().refresh(); }, 1);
+	};
+
+	if (me.restrictToVMFlags) {
+	    me.store = Ext.create('Ext.data.Store', {
+		type: 'store',
+		data: data,
+		fields: ['flag', { name: 'state', defaultValue: '=' }, 'desc'],
+		listeners: {
+		    load: function(store, records, success) {
+			if (success) {
+			    afterLoadFn(store);
+			}
+		    },
+		    update: function() {
+			this.commitChanges();
+		    },
+		},
+	    });
+	} else {
+	    me.store = Ext.create('Ext.data.Store', {
+		fields: ['flag', { name: 'state', defaultValue: '=' },
+		    {
+			name: 'desc',
+			calculate: function(rec) {
+			    let vmFlag = data.find(x => x.flag === rec.flag);
+			    return vmFlag ? vmFlag.desc : gettext("auto-detected");
+			},
+		    },
+		],
+		proxy: {
+		    type: 'proxmox',
+		    url: '/api2/json/nodes/localhost/capabilities/qemu/cpu/recognized-flags',
+		},
+		listeners: {
+		    load: function(store, records, success) {
+			if (success) {
+			    afterLoadFn(store);
+			}
+		    },
+		    update: function() {
+			this.commitChanges();
+		    },
+		},
+		autoLoad: true,
+	    });
+	    me.store.onAfter("load", function() {
+		me.checkDirty();
+	    });
+	}
 
 	me.value = me.originalValue = '';
 
-- 
2.30.2





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

end of thread, other threads:[~2021-10-28 11:42 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC pve-qemu 1/9] Add -list-flags command line option Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 2/9] api: add recognized-flags and supported-flags endpoints Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 3/9] api: add /cpu/model/* get endpoint Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 4/9] api: add /cpu/model/* get/create/delete/update endpoints Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC manager 5/9] gui: VMCPUFlagSelector: fix unknownFlags behaviour Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC manager 6/9] gui: CPUModelSelector: fix dirty state on default Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC manager 7/9] gui: CPUModelSelector: add 'allowCustom' Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC manager 8/9] gui: add basic custom CPU model editor Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC manager 9/9] Initial attempt at CPU flag editor for custom models Stefan Reiter

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