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 EFAB91FF13B for ; Wed, 25 Feb 2026 15:34:35 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 20E06555C; Wed, 25 Feb 2026 15:35:25 +0100 (CET) From: Daniel Kral To: pve-devel@lists.proxmox.com Subject: [RFC qemu-server 1/1] fix #7053: api: migrate: save and restore migration params for HA managed VMs Date: Wed, 25 Feb 2026 15:35:05 +0100 Message-ID: <20260225143514.368884-2-d.kral@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260225143514.368884-1-d.kral@proxmox.com> References: <20260225143514.368884-1-d.kral@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1772030101185 X-SPAM-LEVEL: Spam detection results: 0 AWL -1.039 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 1.113 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.358 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.659 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: U23CJDX6UHPBQO72JM3PG2NZCYQVAWDV X-Message-ID-Hash: U23CJDX6UHPBQO72JM3PG2NZCYQVAWDV X-MailFrom: d.kral@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: If a HA-managed VM's migrate API endpoint is called from a web API or CLI environment, it is first relayed to the HA Manager by queueing a 'migrate' CRM command with `ha-manager migrate vm:$vmid $target_node`. This command doesn't take any additional migration parameters though. As soon as the HA Manager reads the CRM command in the next HA Manager round, it passes the migration request - if valid - to the HA resource state. This migration request is then picked up by the LRM, where the HA resource is assigned to and calls the migrate_vm API endpoint, which will then initial the VM migration. As the migrate_vm API request is proxied to the node, where the HA resource is assigned to, this allows the migrate parameters to stored locally while the migration request is passed through the HA stack. Signed-off-by: Daniel Kral --- src/PVE/API2/Qemu.pm | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/PVE/API2/Qemu.pm b/src/PVE/API2/Qemu.pm index 1f0864f5..027896a3 100644 --- a/src/PVE/API2/Qemu.pm +++ b/src/PVE/API2/Qemu.pm @@ -3,6 +3,7 @@ package PVE::API2::Qemu; use strict; use warnings; use Cwd 'abs_path'; +use File::stat qw(); use Net::SSLeay; use IO::Socket::IP; use IO::Socket::UNIX; @@ -5367,6 +5368,52 @@ __PACKAGE__->register_method({ }, }); +my sub migrate_params_filename { + my ($vmid) = @_; + return "/run/qemu-server/$vmid.migrate_params"; +} + +my sub save_migrate_params { + my ($vmid, $params) = @_; + + my $migrate_params_file = migrate_params_filename($vmid); + + warn "existing migration parameters file for '$vmid' will be overwritten\n" + if -f $migrate_params_file; + + PVE::Tools::file_set_contents($migrate_params_file, encode_json($params), 0640); +} + +my sub try_to_restore_migrate_params { + my ($vmid, $params) = @_; + + my $migrate_params_file = migrate_params_filename($vmid); + my @migrate_params_denylist = qw(node vmid target online force with-local-disks targetstorage); + + if (-f $migrate_params_file) { + my $stat = File::stat::lstat($migrate_params_file); + # prevent that non-root users could write the migrate parameters file + my $has_correct_perms = $stat->uid == 0 && ($stat->mode & 037) == 0; + + if (PVE::HA::Config::vm_is_ha_managed($vmid) && $has_correct_perms) { + my $migration_params = {}; + eval { + my $data = PVE::Tools::file_get_contents($migrate_params_file); + $migration_params = decode_json($data) // {}; + }; + for my $key (keys %$migration_params) { + next if grep { $key eq $_ } @migrate_params_denylist; + + $params->{$key} = $migration_params->{$key}; + } + } else { + warn "remove orphan migration parameters file\n"; + } + + unlink $migrate_params_file; + } +} + __PACKAGE__->register_method({ name => 'migrate_vm', path => '{vmid}/migrate', @@ -5464,6 +5511,13 @@ __PACKAGE__->register_method({ my $vmid = extract_param($param, 'vmid'); + if (PVE::HA::Config::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') { + save_migrate_params($vmid, $param); + } else { + # always try to restore to remove oprhaned migration parameters files + try_to_restore_migrate_params($vmid, $param); + } + raise_param_exc({ force => "Only root may use this option." }) if $param->{force} && $authuser ne 'root@pam'; -- 2.47.3