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 C45C91FF09C for ; Mon, 19 Oct 2026 16:49:30 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id B5C522170A; Mon, 19 Oct 2026 16:47:27 +0200 (CEST) From: Fiona Ebner To: pve-devel@lists.proxmox.com Subject: [PATCH ha-manager v5 15/28] api: resources: migration: support additional migration options Date: Mon, 19 Oct 2026 16:45:57 +0200 Message-ID: <20261019144629.215541-16-f.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261019144629.215541-1-f.ebner@proxmox.com> References: <20261019144629.215541-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: 1792421194387 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.362 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 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: S7Q7QXBPSQ3DAIEMCO6OUKYCPCKABTO7 X-Message-ID-Hash: S7Q7QXBPSQ3DAIEMCO6OUKYCPCKABTO7 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() method. Initially, this will be used to fix #7053 and pass along the 'with-conntrack-state' migration option. Signed-off-by: Fiona Ebner --- Dependency bump for pve-cluster needed! The pve-manager version in the check must be adapted upon applying! Changes in v5: * Add comment about why PVE::HA::Env::PVE2 must be included. * Rename JSON CRM command check helper functions to include 'motion'. * Bump required pve-manager version. * Rename 'kind' to 'task' for consistency. src/PVE/API2/HA/Resources.pm | 110 ++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 14 deletions(-) diff --git a/src/PVE/API2/HA/Resources.pm b/src/PVE/API2/HA/Resources.pm index b96572b..feaa29e 100644 --- a/src/PVE/API2/HA/Resources.pm +++ b/src/PVE/API2/HA/Resources.pm @@ -3,15 +3,20 @@ package PVE::API2::HA::Resources; use strict; use warnings; +use JSON qw(); + +use PVE::Exception qw(raise_param_exc); use PVE::SafeSyslog; use PVE::Tools qw(extract_param); use PVE::Cluster; use PVE::HA::Config; +use PVE::HA::Env::PVE2; # The resource plugins are registered here. use PVE::HA::Resources; use HTTP::Status qw(:constants); use Storable qw(dclone); use PVE::JSONSchema qw(get_standard_option); use PVE::RPCEnvironment; +use PVE::RESTEnvironment qw(log_warn); use PVE::RESTHandler; @@ -34,6 +39,49 @@ my $api_copy_config = sub { return $scfg; }; +my sub migrate_json_properties { + my ($type) = @_; + my $plugin = PVE::HA::Resources->lookup($type); + return $plugin->migrate_json_properties(); +} + +my sub json_crm_motion_command_is_supported { + PVE::Cluster::cfs_update(); + 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 = $@) { + # An updated cluster is the usual situation. An error here should not affect the usual + # situation. The unusual situation, where the master is too old and unable to report its + # version properly is rare. Submitting a JSON CRM to such a master will lead to warnings + # and the command being ignored. Note that migration without extra parameters also still + # works. + log_warn("failed check for migration options support - cannot parse version info for" + . " node '$node' - $err"); + } elsif (!PVE::Cluster::pvecfg_min_version($node_info->{version}, 9, 2, 23)) { + return 0; + } + } + + return 1; +} + +my sub json_crm_motion_command_is_applicable { + my ($sid, $req_node, $param) = @_; + + # FIXME: MAJOR VERSION: use JSON variant even if there are no extra params. + if (scalar(keys $param->%*) > 0) { + if (json_crm_motion_command_is_supported()) { + return 1; + } else { + log_warn("$sid migration to $req_node: ignoring additional migration options - not" + . " supported by CRM on all nodes"); + } + } + + return 0; +} + sub check_service_state { my ($sid, $req_state) = @_; @@ -336,20 +384,32 @@ __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 => $resource_type_enum, }, + oneOf => [ + { + 'instance-type' => 'ct', + additionalProperties => 0, + properties => migrate_json_properties('ct'), + }, + { + 'instance-type' => 'vm', + additionalProperties => 0, + properties => migrate_json_properties('vm'), + }, + ], + }, + 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 +463,34 @@ __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' => "value '$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_motion_command_is_applicable($sid, $req_node, $param)) { + $crm_command = JSON::to_json( + { + task => 'migrate', + sid => $sid, + node => $req_node, + options => $param, + }, + { canonical => 1 }, + ); + } else { + $crm_command = "migrate $sid $req_node"; + } + + PVE::HA::Config::queue_crm_commands($crm_command); $result->{sid} = $sid; $result->{'requested-node'} = $req_node; -- 2.47.3