From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id A80FC1FF0C1 for ; Fri, 18 Sep 2026 18:11:40 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id AC8B32183A; Fri, 18 Sep 2026 18:09:10 +0200 (CEST) From: Fiona Ebner To: pve-devel@lists.proxmox.com Subject: [PATCH ha-manager v3 10/21] api: resources: migration: support additional migration options Date: Fri, 18 Sep 2026 18:08:16 +0200 Message-ID: <20260918160841.128088-11-f.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260918160841.128088-1-f.ebner@proxmox.com> References: <20260918160841.128088-1-f.ebner@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1789747729976 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.582 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust RCVD_IN_MSPIKE_H2 0.001 Average reputation (+2) 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: CXXDHM4ZFAX5TM7BLPI7W5GXNJZK4RFD X-Message-ID-Hash: CXXDHM4ZFAX5TM7BLPI7W5GXNJZK4RFD X-MailFrom: f.ebner@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: Additional migration options can now be passed from the guest API endpoints to the HA stack. From the HA resource API endpoint, the options are passed all the way to the resource plugins. Check that all nodes are recent enough to support the new-style JSON CRM command, otherwise produce a warning and fall-back to the previous behavior ignoring the additional migration options. The migration API endpoints for containers and VMs use different parameters. For example, the endpoint for containers does not have the 'with-conntrack-state' option. To keep the schema for the more abstract endpoint for HA resources clean, separate by type using a oneOf schema and let the resource plugins declare additional parameters via a migrate_json_properties() function. Initially, this will be used to fix #7053 and pass along the 'with-conntrack-state' migration option. Signed-off-by: Fiona Ebner --- The node version might need to be adapted when applying! New in v3. src/PVE/API2/HA/Resources.pm | 99 ++++++++++++++++++++++++++++++----- src/PVE/HA/Resources/PVECT.pm | 10 ++++ src/PVE/HA/Resources/PVEVM.pm | 10 ++++ 3 files changed, 105 insertions(+), 14 deletions(-) diff --git a/src/PVE/API2/HA/Resources.pm b/src/PVE/API2/HA/Resources.pm index b96572b..6d808ca 100644 --- a/src/PVE/API2/HA/Resources.pm +++ b/src/PVE/API2/HA/Resources.pm @@ -3,6 +3,8 @@ package PVE::API2::HA::Resources; use strict; use warnings; +use JSON qw(); + use PVE::SafeSyslog; use PVE::Tools qw(extract_param); use PVE::Cluster; @@ -12,6 +14,7 @@ use HTTP::Status qw(:constants); use Storable qw(dclone); use PVE::JSONSchema qw(get_standard_option); use PVE::RPCEnvironment; +use PVE::SafeSyslog; use PVE::RESTHandler; @@ -34,6 +37,34 @@ my $api_copy_config = sub { return $scfg; }; +my $common_migrate_json_properties = { + sid => get_standard_option( + 'pve-ha-resource-or-vm-id', + { completion => \&PVE::HA::Tools::complete_sid }, + ), + node => get_standard_option( + 'pve-node', + { + completion => \&PVE::Cluster::complete_migration_target, + description => "Target node.", + }, + ), +}; + +my sub json_crm_command_is_supported { + my $version_info = PVE::Cluster::get_node_kv('version-info'); + for my $node (sort keys $version_info->%*) { + my $node_info = eval { JSON::decode_json($version_info->{$node}); }; + if (my $err = $@) { # warn, but continue + syslog('warn', "cannot parse version info for $node as JSON - $err"); + } elsif (!PVE::Cluster::pvecfg_min_version($node_info->{version}, 9, 2, 21)) { + return 0; + } + } + + return 1; +} + sub check_service_state { my ($sid, $req_state) = @_; @@ -336,20 +367,34 @@ __PACKAGE__->register_method({ check => ['perm', '/', ['Sys.Console']], }, parameters => { - additionalProperties => 0, - properties => { - sid => get_standard_option( - 'pve-ha-resource-or-vm-id', - { completion => \&PVE::HA::Tools::complete_sid }, - ), - node => get_standard_option( - 'pve-node', - { - completion => \&PVE::Cluster::complete_migration_target, - description => "Target node.", - }, - ), + 'type-property' => 'resource-type', + 'type-property-schema' => { + type => 'string', + description => 'The resource type. Automatically determined from the service ID.', + enum => ['ct', 'vm'], }, + oneOf => [ + { + 'instance-type' => 'ct', + additionalProperties => 0, + properties => PVE::HA::Resources::PVECT::migrate_json_properties( + $common_migrate_json_properties), + }, + { + 'instance-type' => 'vm', + additionalProperties => 0, + properties => PVE::HA::Resources::PVEVM::migrate_json_properties( + $common_migrate_json_properties), + }, + ], + }, + resolve_type => sub { + my ($param) = @_; + my $type = (PVE::HA::Config::parse_sid($param->{sid}))[1]; + if (!($type eq 'ct' || $type eq 'vm')) { + die "Unknown service type '$type' for 'migrate' endpoint schema.\n"; + } + return $type; }, returns => { type => 'object', @@ -403,12 +448,38 @@ __PACKAGE__->register_method({ my ($sid, $type, $name) = PVE::HA::Config::parse_sid(extract_param($param, 'sid')); my $req_node = extract_param($param, 'node'); + my $resource_type = extract_param($param, 'resource-type'); + if ($resource_type ne $type) { + raise_param_exc({ + 'resource-type' => "$resource_type does not match type of $sid" }); + } + + # The rest of the parameters in $param are migration options passed along to the plugin. PVE::HA::Config::service_is_ha_managed($sid); check_service_state($sid); - PVE::HA::Config::queue_crm_commands("migrate $sid $req_node"); + my $crm_command; + if (json_crm_command_is_supported()) { + $crm_command = JSON::encode_json({ + kind => 'migrate', + sid => $sid, + node => $req_node, + options => $param, + }); + } else { + if (scalar(keys $param->%*) > 0) { + syslog( + 'warn', + "$sid migration to $req_node: ignoring additional migration options" + . " - not supported by CRM on all nodes", + ); + } + $crm_command = "migrate $sid $req_node"; + } + + PVE::HA::Config::queue_crm_commands($crm_command); $result->{sid} = $sid; $result->{'requested-node'} = $req_node; diff --git a/src/PVE/HA/Resources/PVECT.pm b/src/PVE/HA/Resources/PVECT.pm index 3d8a5b9..17dd697 100644 --- a/src/PVE/HA/Resources/PVECT.pm +++ b/src/PVE/HA/Resources/PVECT.pm @@ -174,4 +174,14 @@ sub get_static_stats_from_config { }; } +# FIXME: the extra properties are missing from the docs, since the PVE::API::LXC module can't be +# included in that environment, because of cyclic dependencies.. +sub migrate_json_properties { + my ($props) = @_; + my $extra_migrate_props = eval { PVE::API2::LXC::ha_migrate_json_properties() } // {}; + die $@ if $@ && !$ENV{PVE_GENERATING_DOCS}; + $props->{$_} = $extra_migrate_props->{$_} for keys $extra_migrate_props->%*; + return $props; +} + 1; diff --git a/src/PVE/HA/Resources/PVEVM.pm b/src/PVE/HA/Resources/PVEVM.pm index 964f374..52a48e4 100644 --- a/src/PVE/HA/Resources/PVEVM.pm +++ b/src/PVE/HA/Resources/PVEVM.pm @@ -194,4 +194,14 @@ sub get_static_stats_from_config { }; } +# FIXME: the extra properties are missing from the docs, since the PVE::API::QEMU module can't be +# included in that environment, because of cyclic dependencies.. +sub migrate_json_properties { + my ($props) = @_; + my $extra_migrate_props = eval { PVE::API2::Qemu::ha_migrate_json_properties() } // {}; + die $@ if $@ && !$ENV{PVE_GENERATING_DOCS}; + $props->{$_} = $extra_migrate_props->{$_} for keys $extra_migrate_props->%*; + return $props; +} + 1; -- 2.47.3