]> git.proxmox.com Git - qemu-server.git/commitdiff
migrate: allow arbitrary source->target storage maps
authorFabian Grünbichler <f.gruenbichler@proxmox.com>
Mon, 30 Mar 2020 11:41:33 +0000 (13:41 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Thu, 2 Apr 2020 15:47:14 +0000 (17:47 +0200)
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@proxmox.com>
PVE/API2/Qemu.pm
PVE/QemuMigrate.pm
PVE/QemuServer.pm

index 6fad9729f7595ce42c6198dfd7ab2dc4ba658382..1ce1ac871adc7052c1a8e01b15ec582c1bbe98ee 100644 (file)
@@ -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);
        }
index e954eca5a0ac29d78f7d0e481ee72d28c5fcd3a4..26d33a0f47462b13a7146c744d0345955db1c218 100644 (file)
@@ -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;
index 510a9958c302b51412fd615fea2e4b4f5b4829a5..0a4061198b06811a6cd20123864dc631ae34eacc 100644 (file)
@@ -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);
     });