From bf8fc5a3075285101be21507d2edc139ce2ceaf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= Date: Mon, 30 Mar 2020 13:41:33 +0200 Subject: [PATCH] migrate: allow arbitrary source->target storage maps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the syntax is backwards compatible, providing a single storage ID or '1' works like before. the new helper ensures consistent behaviour at all call sites. Signed-off-by: Fabian Grünbichler --- PVE/API2/Qemu.pm | 41 ++++++++++++++++++++++++++++------------- PVE/QemuMigrate.pm | 24 +++++++++++------------- PVE/QemuServer.pm | 34 ++++++++++++++++++++++++++++------ 3 files changed, 67 insertions(+), 32 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 6fad9729..1ce1ac87 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -2024,11 +2024,7 @@ __PACKAGE__->register_method({ optional => 1, }, machine => get_standard_option('pve-qemu-machine'), - targetstorage => { - description => "Target storage for the migration. (Can be '1' to use the same storage id as on the source node.)", - type => 'string', - optional => 1 - }, + targetstorage => get_standard_option('pve-targetstorage'), timeout => { description => "Wait maximal timeout seconds.", type => 'integer', @@ -2067,8 +2063,15 @@ __PACKAGE__->register_method({ my $migration_network = $get_root_param->('migration_network'); my $targetstorage = $get_root_param->('targetstorage'); - raise_param_exc({ targetstorage => "targetstorage can only by used with migratedfrom." }) - if $targetstorage && !$migratedfrom; + my $storagemap; + + if ($targetstorage) { + raise_param_exc({ targetstorage => "targetstorage can only by used with migratedfrom." }) + if !$migratedfrom; + $storagemap = eval { PVE::JSONSchema::parse_idmap($targetstorage, 'pve-storage-id') }; + raise_param_exc({ targetstorage => "failed to parse targetstorage map: $@" }) + if $@; + } # read spice ticket from STDIN my $spice_ticket; @@ -2119,7 +2122,7 @@ __PACKAGE__->register_method({ spice_ticket => $spice_ticket, network => $migration_network, type => $migration_type, - targetstorage => $targetstorage, + storagemap => $storagemap, nbd_proto_version => $nbd_protocol_version, replicated_volumes => $replicated_volumes, }; @@ -3385,9 +3388,7 @@ __PACKAGE__->register_method({ description => "Enable live storage migration for local disk", optional => 1, }, - targetstorage => get_standard_option('pve-storage-id', { - description => "Default target storage.", - optional => 1, + targetstorage => get_standard_option('pve-targetstorage', { completion => \&PVE::QemuServer::complete_migration_storage, }), bwlimit => { @@ -3451,8 +3452,22 @@ __PACKAGE__->register_method({ my $storecfg = PVE::Storage::config(); - if( $param->{targetstorage}) { - PVE::Storage::storage_check_node($storecfg, $param->{targetstorage}, $target); + if (my $targetstorage = $param->{targetstorage}) { + my $storagemap = eval { PVE::JSONSchema::parse_idmap($targetstorage, 'pve-storage-id') }; + raise_param_exc({ targetstorage => "failed to parse targetstorage map: $@" }) + if $@; + + foreach my $source (keys %{$storagemap->{entries}}) { + PVE::Storage::storage_check_node($storecfg, $storagemap->{entries}->{$source}, $target); + } + + PVE::Storage::storage_check_node($storecfg, $storagemap->{default}, $target) + if $storagemap->{default}; + + PVE::QemuServer::check_storage_availability($storecfg, $conf, $target) + if $storagemap->{identity}; + + $param->{storagemap} = $storagemap; } else { PVE::QemuServer::check_storage_availability($storecfg, $conf, $target); } diff --git a/PVE/QemuMigrate.pm b/PVE/QemuMigrate.pm index e954eca5..26d33a0f 100644 --- a/PVE/QemuMigrate.pm +++ b/PVE/QemuMigrate.pm @@ -244,7 +244,7 @@ sub prepare { my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1); # check if storage is available on both nodes - my $targetsid = $self->{opts}->{targetstorage} // $sid; + my $targetsid = PVE::QemuServer::map_storage($self->{opts}->{storagemap}, $sid); my $scfg = PVE::Storage::storage_check_node($self->{storecfg}, $sid); PVE::Storage::storage_check_node($self->{storecfg}, $targetsid, $self->{node}); @@ -281,14 +281,6 @@ sub sync_disks { $self->{volumes} = []; my $storecfg = $self->{storecfg}; - my $override_targetsid = $self->{opts}->{targetstorage}; - - if (defined($override_targetsid)) { - my $scfg = PVE::Storage::storage_config($storecfg, $override_targetsid); - die "content type 'images' is not available on storage '$override_targetsid'\n" - if !$scfg->{content}->{images}; - } - eval { # found local volumes and their origin @@ -319,11 +311,17 @@ sub sync_disks { next if @{$dl->{$storeid}} == 0; - my $targetsid = $override_targetsid // $storeid; - + my $targetsid = PVE::QemuServer::map_storage($self->{opts}->{storagemap}, $storeid); # check if storage is available on target node PVE::Storage::storage_check_node($storecfg, $targetsid, $self->{node}); + # grandfather in existing mismatches + if ($targetsid ne $storeid) { + my $target_scfg = PVE::Storage::storage_config($storecfg, $targetsid); + die "content type 'images' is not available on storage '$targetsid'\n" + if !$target_scfg->{content}->{images}; + } + PVE::Storage::foreach_volid($dl, sub { my ($volid, $sid, $volinfo) = @_; @@ -368,7 +366,7 @@ sub sync_disks { my ($sid, $volname) = PVE::Storage::parse_volume_id($volid); - my $targetsid = $override_targetsid // $sid; + my $targetsid = PVE::QemuServer::map_storage($self->{opts}->{storagemap}, $sid); # check if storage is available on both nodes my $scfg = PVE::Storage::storage_check_node($storecfg, $sid); PVE::Storage::storage_check_node($storecfg, $targetsid, $self->{node}); @@ -518,7 +516,7 @@ sub sync_disks { foreach my $volid (keys %$local_volumes) { my ($sid, $volname) = PVE::Storage::parse_volume_id($volid); - my $targetsid = $override_targetsid // $sid; + my $targetsid = PVE::QemuServer::map_storage($self->{opts}->{storagemap}, $sid); my $ref = $local_volumes->{$volid}->{ref}; if ($self->{running} && $ref eq 'config') { push @{$self->{online_local_volumes}}, $volid; diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 510a9958..0a406119 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -97,6 +97,28 @@ PVE::JSONSchema::register_standard_option('pve-qemu-machine', { optional => 1, }); + +sub map_storage { + my ($map, $source) = @_; + + return $source if !defined($map); + + return $map->{entries}->{$source} + if defined($map->{entries}) && $map->{entries}->{$source}; + + return $map->{default} if $map->{default}; + + # identity (fallback) + return $source; +} + +PVE::JSONSchema::register_standard_option('pve-targetstorage', { + description => "Mapping from source to target storages. Providing only a single storage ID maps all source storages to that storage. Providing the special value '1' will map each source storage to itself.", + type => 'string', + format => 'storagepair-list', + optional => 1, +}); + #no warnings 'redefine'; sub cgroups_write { @@ -4711,7 +4733,7 @@ sub vmconfig_update_disk { # called in locked context by incoming migration sub vm_migrate_alloc_nbd_disks { - my ($storecfg, $vmid, $conf, $targetstorage, $replicated_volumes) = @_; + my ($storecfg, $vmid, $conf, $storagemap, $replicated_volumes) = @_; my $local_volumes = {}; foreach_drive($conf, sub { @@ -4746,8 +4768,8 @@ sub vm_migrate_alloc_nbd_disks { # If a remote storage is specified and the format of the original # volume is not available there, fall back to the default format. # Otherwise use the same format as the original. - if ($targetstorage && $targetstorage ne "1") { - $storeid = $targetstorage; + if (!$storagemap->{identity}) { + $storeid = map_storage($storagemap, $storeid); my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid); my $scfg = PVE::Storage::storage_config($storecfg, $storeid); my $fileFormat = qemu_img_format($scfg, $volname); @@ -4772,7 +4794,7 @@ sub vm_migrate_alloc_nbd_disks { # see vm_start_nolock for parameters, additionally: # migrate_opts: -# targetstorage = storageid/'1' - target storage for disks migrated over NBD +# storagemap = parsed storage map for allocating NBD disks sub vm_start { my ($storecfg, $vmid, $params, $migrate_opts) = @_; @@ -4788,8 +4810,8 @@ sub vm_start { die "VM $vmid already running\n" if check_running($vmid, undef, $migrate_opts->{migratedfrom}); - $migrate_opts->{nbd} = vm_migrate_alloc_nbd_disks($storecfg, $vmid, $conf, $migrate_opts->{targetstorage}, $migrate_opts->{replicated_volumes}) - if $migrate_opts->{targetstorage}; + $migrate_opts->{nbd} = vm_migrate_alloc_nbd_disks($storecfg, $vmid, $conf, $migrate_opts->{storagemap}, $migrate_opts->{replicated_volumes}) + if $migrate_opts->{storagemap}; vm_start_nolock($storecfg, $vmid, $conf, $params, $migrate_opts); });