[pve-devel] [PATCH qemu-server v2 1/6] migration: only migrate disks used by the guest
Fiona Ebner
f.ebner at proxmox.com
Mon May 22 13:59:20 CEST 2023
Am 12.05.23 um 14:40 schrieb Aaron Lauterer:
> When scanning all configured storages for disk images belonging to the
> VM, the migration could easily fail if a storage is not available, but
> enabled. That storage might not even be used by the VM at all.
>
> By not doing that and only looking at the disk images referenced in the
> VM config, we can avoid that.
> Extra handling is needed for disk images currently in the 'pending'
> section of the VM config. These disk images used to be detected by
> scanning all storages before.
> It is also necessary to fetch some information (size, format) about the
> disk images explicitly that used to be provided by the initial scan of
> all storages.
>
> The big change regarding behavior is that disk images not referenced in
> the VM config file will be ignored. They are already orphans that used
> to be migrated as well, but are now left where they are. The tests have
> been adapted to that changed behavior.
>
> Signed-off-by: Aaron Lauterer <a.lauterer at proxmox.com>
> ---
> PVE/QemuMigrate.pm | 71 +++++++++++----------------
> test/MigrationTest/QemuMigrateMock.pm | 10 ++++
> test/run_qemu_migrate_tests.pl | 12 ++---
> 3 files changed, 44 insertions(+), 49 deletions(-)
>
> diff --git a/PVE/QemuMigrate.pm b/PVE/QemuMigrate.pm
> index 09cc1d8..1d21250 100644
> --- a/PVE/QemuMigrate.pm
> +++ b/PVE/QemuMigrate.pm
> @@ -312,49 +312,6 @@ sub scan_local_volumes {
> $abort = 1;
> };
>
> - my @sids = PVE::Storage::storage_ids($storecfg);
> - foreach my $storeid (@sids) {
> - my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
> - next if $scfg->{shared} && !$self->{opts}->{remote};
> - next if !PVE::Storage::storage_check_enabled($storecfg, $storeid, undef, 1);
> -
> - # get list from PVE::Storage (for unused volumes)
> - my $dl = PVE::Storage::vdisk_list($storecfg, $storeid, $vmid, undef, 'images');
> -
> - next if @{$dl->{$storeid}} == 0;
> -
> - my $targetsid = PVE::JSONSchema::map_id($self->{opts}->{storagemap}, $storeid);
> - if (!$self->{opts}->{remote}) {
> - # check if storage is available on target node
> - my $target_scfg = PVE::Storage::storage_check_enabled(
> - $storecfg,
> - $targetsid,
> - $self->{node},
> - );
> -
> - die "content type 'images' is not available on storage '$targetsid'\n"
> - if !$target_scfg->{content}->{images};
This one might be worth re-adding after the storage enabled check
further below. The same check is already done in prepare() for the
result of get_vm_volumes(), but that does not (currently ;)) include
pending ones (a bit of foreshadowing here :P)
Or actually, let's use the improved vtype-aware version from prepare():
> my ($vtype) = PVE::Storage::parse_volname($storecfg, $volid);
>
> die "$volid: content type '$vtype' is not available on storage '$targetsid'\n"
> if !$target_scfg->{content}->{$vtype};
Might even be worth factoring out the whole block including the
if (!$self->{opts}->{remote}) {
...
}
into a mini-helper? But it's only used twice, so do as you like :)
(...)
> @@ -405,8 +362,23 @@ sub scan_local_volumes {
>
> $local_volumes->{$volid}->{ref} = $attr->{referenced_in_config} ? 'config' : 'snapshot';
> $local_volumes->{$volid}->{ref} = 'storage' if $attr->{is_unused};
> + $local_volumes->{$volid}->{ref} = 'storage' if $attr->{is_pending};
> $local_volumes->{$volid}->{ref} = 'generated' if $attr->{is_tpmstate};
>
> + my $bwlimit = $self->get_bwlimit($sid, $targetsid);
> + $local_volumes->{$volid}->{targetsid} = $targetsid;
> + $local_volumes->{$volid}->{bwlimit} = $bwlimit;
> +
> + my $volume_list = PVE::Storage::volume_list($storecfg, $sid, $vmid, 'images');
> + # TODO could probably be done better than just iterating
volume_size_info() to the rescue :) Would avoid the loop and quite a bit
of overhead from calling volume_list() for each individual volume.
> + for my $volume (@$volume_list) {
> + if ($volume->{volid} eq $volid) {
> + $local_volumes->{$volid}->{size} = $volume->{size};
> + $local_volumes->{$volid}->{format} = $volume->{format};
> + last;
> + }
> + }
> +
> $local_volumes->{$volid}->{is_vmstate} = $attr->{is_vmstate} ? 1 : 0;
>
> $local_volumes->{$volid}->{drivename} = $attr->{drivename}
> @@ -450,6 +422,19 @@ sub scan_local_volumes {
> if PVE::Storage::volume_is_base_and_used($storecfg, $volid);
> };
>
> + # add pending disks first
> + if (defined $conf->{pending} && %{$conf->{pending}}) {
Style nit: please use parentheses for defined. And $conf->{pending}->%*
is slightly nicer, because it can be read from left to right, rather
than needing to look at the inner bit first and then remember the % on
the outside ;)
> + PVE::QemuServer::foreach_volid($conf->{pending}, sub {
Should we rather expand foreach_volid() instead? It handles snapshots
already, so handling pending too doesn't seem fundamentally wrong.
Let's check the existing callers and whether they are fine with the change:
1. this one right here: wants pending
2. API2/Qemu.pm: check_vm_disks_local() for migration precondition:
related to migration, so more consistent with pending
3. QemuConfig.pm: get_replicatable_volumes(): can be fine with pending,
but it's a change of course!
4. QemuServer.pm: get_vm_volumes(): is used multiple times by:
4a. vm_stop_cleanup() to deactivate/unmap: should also be fine with
including pending
4b. QemuMigrate.pm: in prepare(): part of migration, so more consistent
with pending
4c. QemuMigrate.pm: in phase3_cleanup() for deactivation: part of
migration, so more consistent with pending
So the only issue is with replication, where we can decide between:
1. Also include pending volumes for replication (would be fine by me).
2. Keep behavior as-is. But then we need to adapt. Some possibilities:
2a. Add a new attribute like 'pending-only' in the result of
foreach_volid(), so that get_replicatable_volumes() can filter them out.
2b. Switch to a different function like foreach_volume() and get the two
attributes that are used there (cdrom and replicate) manually.
2c. Add a switch to foreach_volid() whether it should include volumes
that are only in pending.
> + my ($volid, $attr) = @_;
> + $attr->{is_pending} = 1;
> + eval { $test_volid->($volid, $attr); };
> + if (my $err = $@) {
> + &$log_error($err, $volid);
> + }
> + });
> + }
> +
> + # add non-pending referenced disks
> PVE::QemuServer::foreach_volid($conf, sub {
> my ($volid, $attr) = @_;
> eval { $test_volid->($volid, $attr); };
More information about the pve-devel
mailing list