[pve-devel] [PATCH qemu-server 5/8] migrate: allow arbitrary source->target storage maps

Fabian Grünbichler f.gruenbichler at proxmox.com
Mon Mar 30 13:41:33 CEST 2020


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 <f.gruenbichler at proxmox.com>
---

Notes:
    needs a versioned dep on pve-common with the new format and parse_idmap

 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 788db93..6eba8d0 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 579be0e..1546d87 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 1ac8643..cd534f4 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);
     });
-- 
2.20.1





More information about the pve-devel mailing list