From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id CB8861FF138 for ; Wed, 04 Feb 2026 11:04:37 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id BADD5F2B0; Wed, 4 Feb 2026 11:05:02 +0100 (CET) From: Dominik Csapak To: pve-devel@lists.proxmox.com Subject: [PATCH qemu-server v2 1/2] tests: improve multiarch build support Date: Wed, 4 Feb 2026 11:01:01 +0100 Message-ID: <20260204100425.1303295-1-d.csapak@proxmox.com> X-Mailer: git-send-email 2.47.3 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.031 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: OEXQLKHMJEKBUXO75SHKNE7UVPKYBV2T X-Message-ID-Hash: OEXQLKHMJEKBUXO75SHKNE7UVPKYBV2T X-MailFrom: d.csapak@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: instead of simply saving 'host_arch' in the global package namespace, use the PVE::Tools::get_host_arch helper, which caches the result anyway. For easier testing: * introduce our own helper for getting the host arch, which uses the PVE::Tools one. So we only have to mock the Tools one to get correct behavior in the tests. Use this everywhere here where we need it. * move the cpu_models_by_arch initialization into a sub that can be called by the tests to re-initialize. Signed-off-by: Dominik Csapak --- changes from v1: * add helpers to be more easily used and mocked by the tests instead of modifying the module level variable src/PVE/QemuServer.pm | 5 +- src/PVE/QemuServer/CPUConfig.pm | 258 ++++++++++++++------------- src/PVE/QemuServer/Helpers.pm | 8 +- src/test/run_config2command_tests.pl | 8 +- 4 files changed, 146 insertions(+), 133 deletions(-) diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm index 5d2dbe03..8bf23945 100644 --- a/src/PVE/QemuServer.pm +++ b/src/PVE/QemuServer.pm @@ -46,8 +46,7 @@ use PVE::SafeSyslog; use PVE::Storage; use PVE::SysFSTools; use PVE::Systemd; -use PVE::Tools - qw(run_command file_read_firstline file_get_contents dir_glob_foreach get_host_arch $IPV6RE); +use PVE::Tools qw(run_command file_read_firstline file_get_contents dir_glob_foreach $IPV6RE); use PVE::QMPClient; use PVE::QemuConfig; @@ -58,7 +57,7 @@ use PVE::QemuServer::Blockdev; use PVE::QemuServer::BlockJob; use PVE::QemuServer::Cfg2Cmd; use PVE::QemuServer::Helpers - qw(config_aware_timeout get_iscsi_initiator_name min_version kvm_user_version windows_version); + qw(config_aware_timeout get_iscsi_initiator_name get_host_arch min_version kvm_user_version windows_version); use PVE::QemuServer::Cloudinit; use PVE::QemuServer::CGroup; use PVE::QemuServer::CPUConfig qw( diff --git a/src/PVE/QemuServer/CPUConfig.pm b/src/PVE/QemuServer/CPUConfig.pm index 32ec4954..7d5559b0 100644 --- a/src/PVE/QemuServer/CPUConfig.pm +++ b/src/PVE/QemuServer/CPUConfig.pm @@ -11,7 +11,7 @@ use PVE::ProcFSTools; use PVE::RESTEnvironment qw(log_warn); use PVE::Tools qw(run_command); -use PVE::QemuServer::Helpers qw(min_version); +use PVE::QemuServer::Helpers qw(min_version get_host_arch); use base qw(PVE::SectionConfig Exporter); @@ -25,8 +25,6 @@ our @EXPORT_OK = qw( get_cvm_type ); -my $host_arch = PVE::Tools::get_host_arch(); - my $arch_desc = { description => "Virtual processor architecture. Defaults to the host architecture.", type => 'string', @@ -100,130 +98,138 @@ my $cputypes_32bit = { 'qemu32' => 1, }; -my $cpu_models_by_arch = { - x86_64 => { - # Intel CPUs - 486 => 'GenuineIntel', - pentium => 'GenuineIntel', - pentium2 => 'GenuineIntel', - pentium3 => 'GenuineIntel', - coreduo => 'GenuineIntel', - core2duo => 'GenuineIntel', - Conroe => 'GenuineIntel', - Penryn => 'GenuineIntel', - Nehalem => 'GenuineIntel', - 'Nehalem-IBRS' => 'GenuineIntel', - Westmere => 'GenuineIntel', - 'Westmere-IBRS' => 'GenuineIntel', - SandyBridge => 'GenuineIntel', - 'SandyBridge-IBRS' => 'GenuineIntel', - IvyBridge => 'GenuineIntel', - 'IvyBridge-IBRS' => 'GenuineIntel', - Haswell => 'GenuineIntel', - 'Haswell-IBRS' => 'GenuineIntel', - 'Haswell-noTSX' => 'GenuineIntel', - 'Haswell-noTSX-IBRS' => 'GenuineIntel', - Broadwell => 'GenuineIntel', - 'Broadwell-IBRS' => 'GenuineIntel', - 'Broadwell-noTSX' => 'GenuineIntel', - 'Broadwell-noTSX-IBRS' => 'GenuineIntel', - 'Skylake-Client' => 'GenuineIntel', - 'Skylake-Client-IBRS' => 'GenuineIntel', - 'Skylake-Client-noTSX-IBRS' => 'GenuineIntel', - 'Skylake-Client-v4' => 'GenuineIntel', - 'Skylake-Server' => 'GenuineIntel', - 'Skylake-Server-IBRS' => 'GenuineIntel', - 'Skylake-Server-noTSX-IBRS' => 'GenuineIntel', - 'Skylake-Server-v4' => 'GenuineIntel', - 'Skylake-Server-v5' => 'GenuineIntel', - 'Cascadelake-Server' => 'GenuineIntel', - 'Cascadelake-Server-v2' => 'GenuineIntel', - 'Cascadelake-Server-noTSX' => 'GenuineIntel', - 'Cascadelake-Server-v4' => 'GenuineIntel', - 'Cascadelake-Server-v5' => 'GenuineIntel', - 'Cooperlake' => 'GenuineIntel', - 'Cooperlake-v2' => 'GenuineIntel', - KnightsMill => 'GenuineIntel', - 'Icelake-Client' => 'GenuineIntel', # depreacated, removed with QEMU 7.1 - 'Icelake-Client-noTSX' => 'GenuineIntel', # depreacated, removed with QEMU 7.1 - 'Icelake-Server' => 'GenuineIntel', - 'Icelake-Server-noTSX' => 'GenuineIntel', - 'Icelake-Server-v3' => 'GenuineIntel', - 'Icelake-Server-v4' => 'GenuineIntel', - 'Icelake-Server-v5' => 'GenuineIntel', - 'Icelake-Server-v6' => 'GenuineIntel', - 'Icelake-Server-v7' => 'GenuineIntel', - 'SapphireRapids' => 'GenuineIntel', - 'SapphireRapids-v2' => 'GenuineIntel', - 'SapphireRapids-v3' => 'GenuineIntel', - 'SapphireRapids-v4' => 'GenuineIntel', - 'GraniteRapids' => 'GenuineIntel', - 'GraniteRapids-v2' => 'GenuineIntel', - 'GraniteRapids-v3' => 'GenuineIntel', - 'SierraForest' => 'GenuineIntel', - 'SierraForest-v2' => 'GenuineIntel', - 'SierraForest-v3' => 'GenuineIntel', - 'ClearwaterForest' => 'GenuineIntel', - - # AMD CPUs - athlon => 'AuthenticAMD', - phenom => 'AuthenticAMD', - Opteron_G1 => 'AuthenticAMD', - Opteron_G2 => 'AuthenticAMD', - Opteron_G3 => 'AuthenticAMD', - Opteron_G4 => 'AuthenticAMD', - Opteron_G5 => 'AuthenticAMD', - EPYC => 'AuthenticAMD', - 'EPYC-IBPB' => 'AuthenticAMD', - 'EPYC-v3' => 'AuthenticAMD', - 'EPYC-v4' => 'AuthenticAMD', - 'EPYC-v5' => 'AuthenticAMD', - 'EPYC-Rome' => 'AuthenticAMD', - 'EPYC-Rome-v2' => 'AuthenticAMD', - 'EPYC-Rome-v3' => 'AuthenticAMD', - 'EPYC-Rome-v4' => 'AuthenticAMD', - 'EPYC-Rome-v5' => 'AuthenticAMD', - 'EPYC-Milan' => 'AuthenticAMD', - 'EPYC-Milan-v2' => 'AuthenticAMD', - 'EPYC-Milan-v3' => 'AuthenticAMD', - 'EPYC-Genoa' => 'AuthenticAMD', - 'EPYC-Genoa-v2' => 'AuthenticAMD', - 'EPYC-Turin' => 'AuthenticAMD', - - # generic types, use vendor from host node - kvm32 => 'default', - kvm64 => 'default', - qemu32 => 'default', - qemu64 => 'default', - max => 'default', - }, - aarch64 => { - 'a64fx' => 'ARM', - 'cortex-a35' => 'ARM', - 'cortex-a53' => 'ARM', - 'cortex-a55' => 'ARM', - 'cortex-a57' => 'ARM', - 'cortex-a710' => 'ARM', - 'cortex-a72' => 'ARM', - 'cortex-a76' => 'ARM', - 'neoverse-n1' => 'ARM', - 'neoverse-n2' => 'ARM', - 'neoverse-v1' => 'ARM', - # 32 bit and deprecated models were not added - max => 'default', - }, -}; +my $cpu_models_by_arch; +my $all_cpu_models; -# The host CPU model only exists if the arch matches -$cpu_models_by_arch->{$host_arch}->{host} = 'default'; +# helper to make it easier for testing +sub initialize_cpu_models { + $cpu_models_by_arch = { + x86_64 => { + # Intel CPUs + 486 => 'GenuineIntel', + pentium => 'GenuineIntel', + pentium2 => 'GenuineIntel', + pentium3 => 'GenuineIntel', + coreduo => 'GenuineIntel', + core2duo => 'GenuineIntel', + Conroe => 'GenuineIntel', + Penryn => 'GenuineIntel', + Nehalem => 'GenuineIntel', + 'Nehalem-IBRS' => 'GenuineIntel', + Westmere => 'GenuineIntel', + 'Westmere-IBRS' => 'GenuineIntel', + SandyBridge => 'GenuineIntel', + 'SandyBridge-IBRS' => 'GenuineIntel', + IvyBridge => 'GenuineIntel', + 'IvyBridge-IBRS' => 'GenuineIntel', + Haswell => 'GenuineIntel', + 'Haswell-IBRS' => 'GenuineIntel', + 'Haswell-noTSX' => 'GenuineIntel', + 'Haswell-noTSX-IBRS' => 'GenuineIntel', + Broadwell => 'GenuineIntel', + 'Broadwell-IBRS' => 'GenuineIntel', + 'Broadwell-noTSX' => 'GenuineIntel', + 'Broadwell-noTSX-IBRS' => 'GenuineIntel', + 'Skylake-Client' => 'GenuineIntel', + 'Skylake-Client-IBRS' => 'GenuineIntel', + 'Skylake-Client-noTSX-IBRS' => 'GenuineIntel', + 'Skylake-Client-v4' => 'GenuineIntel', + 'Skylake-Server' => 'GenuineIntel', + 'Skylake-Server-IBRS' => 'GenuineIntel', + 'Skylake-Server-noTSX-IBRS' => 'GenuineIntel', + 'Skylake-Server-v4' => 'GenuineIntel', + 'Skylake-Server-v5' => 'GenuineIntel', + 'Cascadelake-Server' => 'GenuineIntel', + 'Cascadelake-Server-v2' => 'GenuineIntel', + 'Cascadelake-Server-noTSX' => 'GenuineIntel', + 'Cascadelake-Server-v4' => 'GenuineIntel', + 'Cascadelake-Server-v5' => 'GenuineIntel', + 'Cooperlake' => 'GenuineIntel', + 'Cooperlake-v2' => 'GenuineIntel', + KnightsMill => 'GenuineIntel', + 'Icelake-Client' => 'GenuineIntel', # depreacated, removed with QEMU 7.1 + 'Icelake-Client-noTSX' => 'GenuineIntel', # depreacated, removed with QEMU 7.1 + 'Icelake-Server' => 'GenuineIntel', + 'Icelake-Server-noTSX' => 'GenuineIntel', + 'Icelake-Server-v3' => 'GenuineIntel', + 'Icelake-Server-v4' => 'GenuineIntel', + 'Icelake-Server-v5' => 'GenuineIntel', + 'Icelake-Server-v6' => 'GenuineIntel', + 'Icelake-Server-v7' => 'GenuineIntel', + 'SapphireRapids' => 'GenuineIntel', + 'SapphireRapids-v2' => 'GenuineIntel', + 'SapphireRapids-v3' => 'GenuineIntel', + 'SapphireRapids-v4' => 'GenuineIntel', + 'GraniteRapids' => 'GenuineIntel', + 'GraniteRapids-v2' => 'GenuineIntel', + 'GraniteRapids-v3' => 'GenuineIntel', + 'SierraForest' => 'GenuineIntel', + 'SierraForest-v2' => 'GenuineIntel', + 'SierraForest-v3' => 'GenuineIntel', + 'ClearwaterForest' => 'GenuineIntel', + + # AMD CPUs + athlon => 'AuthenticAMD', + phenom => 'AuthenticAMD', + Opteron_G1 => 'AuthenticAMD', + Opteron_G2 => 'AuthenticAMD', + Opteron_G3 => 'AuthenticAMD', + Opteron_G4 => 'AuthenticAMD', + Opteron_G5 => 'AuthenticAMD', + EPYC => 'AuthenticAMD', + 'EPYC-IBPB' => 'AuthenticAMD', + 'EPYC-v3' => 'AuthenticAMD', + 'EPYC-v4' => 'AuthenticAMD', + 'EPYC-v5' => 'AuthenticAMD', + 'EPYC-Rome' => 'AuthenticAMD', + 'EPYC-Rome-v2' => 'AuthenticAMD', + 'EPYC-Rome-v3' => 'AuthenticAMD', + 'EPYC-Rome-v4' => 'AuthenticAMD', + 'EPYC-Rome-v5' => 'AuthenticAMD', + 'EPYC-Milan' => 'AuthenticAMD', + 'EPYC-Milan-v2' => 'AuthenticAMD', + 'EPYC-Milan-v3' => 'AuthenticAMD', + 'EPYC-Genoa' => 'AuthenticAMD', + 'EPYC-Genoa-v2' => 'AuthenticAMD', + 'EPYC-Turin' => 'AuthenticAMD', + + # generic types, use vendor from host node + kvm32 => 'default', + kvm64 => 'default', + qemu32 => 'default', + qemu64 => 'default', + max => 'default', + }, + aarch64 => { + 'a64fx' => 'ARM', + 'cortex-a35' => 'ARM', + 'cortex-a53' => 'ARM', + 'cortex-a55' => 'ARM', + 'cortex-a57' => 'ARM', + 'cortex-a710' => 'ARM', + 'cortex-a72' => 'ARM', + 'cortex-a76' => 'ARM', + 'neoverse-n1' => 'ARM', + 'neoverse-n2' => 'ARM', + 'neoverse-v1' => 'ARM', + # 32 bit and deprecated models were not added + max => 'default', + }, + }; -my $all_cpu_models; -for my $arch (keys $cpu_models_by_arch->%*) { - for my $model (keys $cpu_models_by_arch->{$arch}->%*) { - $all_cpu_models->{$model} = $cpu_models_by_arch->{$arch}->{$model}; + my $host_arch = get_host_arch(); + # The host CPU model only exists if the arch matches + $cpu_models_by_arch->{$host_arch}->{host} = 'default'; + + for my $arch (keys $cpu_models_by_arch->%*) { + for my $model (keys $cpu_models_by_arch->{$arch}->%*) { + $all_cpu_models->{$model} = $cpu_models_by_arch->{$arch}->{$model}; + } } } +initialize_cpu_models(); + my $supported_cpu_flags_by_arch = { x86_64 => [ { @@ -294,7 +300,7 @@ my $supported_cpu_flags_by_arch = { sub get_supported_cpu_flags { my ($arch) = @_; - $arch = $host_arch if !defined($arch); + $arch = get_host_arch() if !defined($arch); return $supported_cpu_flags_by_arch->{$arch}; } @@ -582,7 +588,7 @@ sub add_cpu_json_properties { sub get_cpu_models { my ($include_custom, $arch) = @_; - $arch = $host_arch if !defined($arch); + $arch = get_host_arch() if !defined($arch); my $cpu_vendor_list = $cpu_models_by_arch->{$arch}; my $models = []; @@ -1077,13 +1083,13 @@ sub get_default_cpu_type { sub is_native_arch($) { my ($arch) = @_; - return $host_arch eq $arch; + return get_host_arch() eq $arch; } sub get_cpu_bitness { my ($cpu_prop_str, $arch) = @_; - $arch //= $host_arch; + $arch //= get_host_arch(); my $cputype = get_default_cpu_type($arch, 0); diff --git a/src/PVE/QemuServer/Helpers.pm b/src/PVE/QemuServer/Helpers.pm index 35c00754..e898440c 100644 --- a/src/PVE/QemuServer/Helpers.pm +++ b/src/PVE/QemuServer/Helpers.pm @@ -10,7 +10,7 @@ use JSON; use PVE::Cluster; use PVE::INotify; use PVE::ProcFSTools; -use PVE::Tools qw(get_host_arch); +use PVE::Tools; use base 'Exporter'; our @EXPORT_OK = qw( @@ -20,6 +20,7 @@ our @EXPORT_OK = qw( kvm_user_version parse_number_sets windows_version + get_host_arch ); my $nodename = PVE::INotify::nodename(); @@ -29,6 +30,11 @@ my $arch_to_qemu_binary = { x86_64 => '/usr/bin/qemu-system-x86_64', }; +# wrapper around the Tools helper, having it here makes it easier for testing +sub get_host_arch { + return PVE::Tools::get_host_arch(); +} + sub get_command_for_arch($) { my ($arch) = @_; return '/usr/bin/kvm' if get_host_arch() eq $arch; # i.e. native arch diff --git a/src/test/run_config2command_tests.pl b/src/test/run_config2command_tests.pl index 4c872d1c..7ca2014e 100755 --- a/src/test/run_config2command_tests.pl +++ b/src/test/run_config2command_tests.pl @@ -235,6 +235,8 @@ sub parse_test($config_fn) { $testname = "'$testname' - $desc"; } $current_test->{testname} = $testname; + + PVE::QemuServer::CPUConfig::initialize_cpu_models(); } sub get_test_qemu_version { @@ -253,9 +255,6 @@ $qemu_server_module->mock( kernel_has_vhost_net => sub { return 1; # TODO: make this per-test configurable? }, - get_host_arch => sub() { - return $current_test->{host_arch} // 'x86_64'; - }, get_iscsi_initiator_name => sub { return 'iqn.1993-08.org.debian:01:aabbccddeeff'; }, @@ -388,6 +387,9 @@ $pve_common_tools->mock( }, ); }, + get_host_arch => sub() { + return $current_test->{host_arch} // 'x86_64'; + }, ); my $pve_cpuconfig; -- 2.47.3