* [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