From 0f55e8be8491e530e10799db88e64d82a563db5a Mon Sep 17 00:00:00 2001 From: Thomas Lamprecht Date: Thu, 3 Apr 2025 15:15:42 +0200 Subject: [PATCH] api: migration preconditions: return detailed info for mapped-resources Switch the underlying check_local_resource method to return a hash and adapt call sites to that change. Return that new hash in the API as new return property for the frontend to have more info to check. This is in preparation for enabling live-migration for VMs with mappings that declare being capable of them. Originally-by: Dominik Csapak [TL: split out from unrelated changes and fix return schema] Signed-off-by: Thomas Lamprecht --- PVE/API2/Qemu.pm | 10 ++++++++-- PVE/QemuMigrate.pm | 20 ++++++++++++++------ PVE/QemuServer.pm | 15 ++++++++------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index e7b684d7..7aa0b8f8 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -4667,13 +4667,18 @@ __PACKAGE__->register_method({ }, description => "List local resources (e.g. pci, usb) that block migration." }, + # FIXME: remove with 9.0 'mapped-resources' => { type => 'array', items => { type => 'string', description => "A mapped resource", }, - description => "List of mapped resources e.g. pci, usb" + description => "List of mapped resources e.g. pci, usb. Deprecated, use 'mapped-resource-info' instead." + }, + 'mapped-resource-info' => { + type => 'object', + description => "Object of mapped resources with additional information such if they're live migratable.", }, }, }, @@ -4738,7 +4743,8 @@ __PACKAGE__->register_method({ $res->{local_disks} = [ values %$local_disks ];; $res->{local_resources} = $local_resources; - $res->{'mapped-resources'} = $mapped_resources; + $res->{'mapped-resources'} = [ sort keys $mapped_resources->%* ]; + $res->{'mapped-resource-info'} = $mapped_resources; return $res; diff --git a/PVE/QemuMigrate.pm b/PVE/QemuMigrate.pm index c2e36334..6909fc82 100644 --- a/PVE/QemuMigrate.pm +++ b/PVE/QemuMigrate.pm @@ -229,7 +229,7 @@ sub prepare { my ($loc_res, $mapped_res, $missing_mappings_by_node) = PVE::QemuServer::check_local_resources($conf, $running, 1); my $blocking_resources = []; for my $res ($loc_res->@*) { - if (!grep($res, $mapped_res->@*)) { + if (!defined($mapped_res->{$res})) { push $blocking_resources->@*, $res; } } @@ -241,12 +241,20 @@ sub prepare { } } - if (scalar($mapped_res->@*)) { + if (scalar(keys $mapped_res->%*)) { my $missing_mappings = $missing_mappings_by_node->{$self->{node}}; - if ($running) { - die "can't migrate running VM which uses mapped devices: " . join(", ", $mapped_res->@*) . "\n"; - } elsif (scalar($missing_mappings->@*)) { - die "can't migrate to '$self->{node}': missing mapped devices " . join(", ", $missing_mappings->@*) . "\n"; + my $missing_live_mappings = []; + for my $key (sort keys $mapped_res->%*) { + my $res = $mapped_res->{$key}; + my $name = "$key:$res->{name}"; + push $missing_live_mappings->@*, $name if !$res->{'live-migration'}; + } + if (scalar($missing_mappings->@*)) { + my $missing = join(", ", $missing_mappings->@*); + die "can't migrate to '$self->{node}': missing mapped devices $missing\n"; + } elsif ($running && scalar($missing_live_mappings->@*)) { + my $missing = join(", ", $missing_live_mappings->@*); + die "can't live migrate running VM which uses following mapped devices: $missing\n"; } else { $self->log('info', "migrating VM which uses mapped local devices"); } diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 155432d3..2e72e2d6 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -2472,7 +2472,7 @@ sub check_local_resources { my ($conf, $state, $noerr) = @_; my @loc_res = (); - my $mapped_res = []; + my $mapped_res = {}; my @non_migratable_resources = check_non_migratable_resources($conf, $state, $noerr); push(@loc_res, @non_migratable_resources); @@ -2507,16 +2507,17 @@ sub check_local_resources { if ($k =~ m/^usb/) { my $entry = parse_property_string('pve-qm-usb', $conf->{$k}); next if $entry->{host} && $entry->{host} =~ m/^spice$/i; - if ($entry->{mapping}) { - $add_missing_mapping->('usb', $k, $entry->{mapping}); - push @$mapped_res, $k; + if (my $name = $entry->{mapping}) { + $add_missing_mapping->('usb', $k, $name); + $mapped_res->{$k} = { name => $name }; } } if ($k =~ m/^hostpci/) { my $entry = parse_property_string('pve-qm-hostpci', $conf->{$k}); - if ($entry->{mapping}) { - $add_missing_mapping->('pci', $k, $entry->{mapping}); - push @$mapped_res, $k; + if (my $name = $entry->{mapping}) { + $add_missing_mapping->('pci', $k, $name); + my $mapped_device = { name => $name }; + $mapped_res->{$k} = $mapped_device; # don't add mapped devices as blocker for offline migration but still iterate over # all mappings above to collect on which nodes they are available.