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 <d.csapak@proxmox.com>
 [TL: split out from unrelated changes and fix return schema]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2025-04-03 15:15:42 +02:00
parent 8c6b0569ea
commit 0f55e8be84
3 changed files with 30 additions and 15 deletions

View File

@ -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;

View File

@ -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");
}

View File

@ -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.