migrate: allow arbitrary source->target storage maps

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>
This commit is contained in:
Fabian Grünbichler 2020-03-30 13:41:33 +02:00 committed by Thomas Lamprecht
parent c05f1b33ea
commit bf8fc5a307
3 changed files with 67 additions and 32 deletions

View File

@ -2024,11 +2024,7 @@ __PACKAGE__->register_method({
optional => 1, optional => 1,
}, },
machine => get_standard_option('pve-qemu-machine'), machine => get_standard_option('pve-qemu-machine'),
targetstorage => { targetstorage => get_standard_option('pve-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
},
timeout => { timeout => {
description => "Wait maximal timeout seconds.", description => "Wait maximal timeout seconds.",
type => 'integer', type => 'integer',
@ -2067,8 +2063,15 @@ __PACKAGE__->register_method({
my $migration_network = $get_root_param->('migration_network'); my $migration_network = $get_root_param->('migration_network');
my $targetstorage = $get_root_param->('targetstorage'); my $targetstorage = $get_root_param->('targetstorage');
raise_param_exc({ targetstorage => "targetstorage can only by used with migratedfrom." }) my $storagemap;
if $targetstorage && !$migratedfrom;
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 # read spice ticket from STDIN
my $spice_ticket; my $spice_ticket;
@ -2119,7 +2122,7 @@ __PACKAGE__->register_method({
spice_ticket => $spice_ticket, spice_ticket => $spice_ticket,
network => $migration_network, network => $migration_network,
type => $migration_type, type => $migration_type,
targetstorage => $targetstorage, storagemap => $storagemap,
nbd_proto_version => $nbd_protocol_version, nbd_proto_version => $nbd_protocol_version,
replicated_volumes => $replicated_volumes, replicated_volumes => $replicated_volumes,
}; };
@ -3385,9 +3388,7 @@ __PACKAGE__->register_method({
description => "Enable live storage migration for local disk", description => "Enable live storage migration for local disk",
optional => 1, optional => 1,
}, },
targetstorage => get_standard_option('pve-storage-id', { targetstorage => get_standard_option('pve-targetstorage', {
description => "Default target storage.",
optional => 1,
completion => \&PVE::QemuServer::complete_migration_storage, completion => \&PVE::QemuServer::complete_migration_storage,
}), }),
bwlimit => { bwlimit => {
@ -3451,8 +3452,22 @@ __PACKAGE__->register_method({
my $storecfg = PVE::Storage::config(); my $storecfg = PVE::Storage::config();
if( $param->{targetstorage}) { if (my $targetstorage = $param->{targetstorage}) {
PVE::Storage::storage_check_node($storecfg, $param->{targetstorage}, $target); 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 { } else {
PVE::QemuServer::check_storage_availability($storecfg, $conf, $target); PVE::QemuServer::check_storage_availability($storecfg, $conf, $target);
} }

View File

@ -244,7 +244,7 @@ sub prepare {
my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1); my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
# check if storage is available on both nodes # 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); my $scfg = PVE::Storage::storage_check_node($self->{storecfg}, $sid);
PVE::Storage::storage_check_node($self->{storecfg}, $targetsid, $self->{node}); PVE::Storage::storage_check_node($self->{storecfg}, $targetsid, $self->{node});
@ -281,14 +281,6 @@ sub sync_disks {
$self->{volumes} = []; $self->{volumes} = [];
my $storecfg = $self->{storecfg}; 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 { eval {
# found local volumes and their origin # found local volumes and their origin
@ -319,11 +311,17 @@ sub sync_disks {
next if @{$dl->{$storeid}} == 0; 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 # check if storage is available on target node
PVE::Storage::storage_check_node($storecfg, $targetsid, $self->{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 { PVE::Storage::foreach_volid($dl, sub {
my ($volid, $sid, $volinfo) = @_; my ($volid, $sid, $volinfo) = @_;
@ -368,7 +366,7 @@ sub sync_disks {
my ($sid, $volname) = PVE::Storage::parse_volume_id($volid); 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 # check if storage is available on both nodes
my $scfg = PVE::Storage::storage_check_node($storecfg, $sid); my $scfg = PVE::Storage::storage_check_node($storecfg, $sid);
PVE::Storage::storage_check_node($storecfg, $targetsid, $self->{node}); PVE::Storage::storage_check_node($storecfg, $targetsid, $self->{node});
@ -518,7 +516,7 @@ sub sync_disks {
foreach my $volid (keys %$local_volumes) { foreach my $volid (keys %$local_volumes) {
my ($sid, $volname) = PVE::Storage::parse_volume_id($volid); 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}; my $ref = $local_volumes->{$volid}->{ref};
if ($self->{running} && $ref eq 'config') { if ($self->{running} && $ref eq 'config') {
push @{$self->{online_local_volumes}}, $volid; push @{$self->{online_local_volumes}}, $volid;

View File

@ -97,6 +97,28 @@ PVE::JSONSchema::register_standard_option('pve-qemu-machine', {
optional => 1, 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'; #no warnings 'redefine';
sub cgroups_write { sub cgroups_write {
@ -4711,7 +4733,7 @@ sub vmconfig_update_disk {
# called in locked context by incoming migration # called in locked context by incoming migration
sub vm_migrate_alloc_nbd_disks { sub vm_migrate_alloc_nbd_disks {
my ($storecfg, $vmid, $conf, $targetstorage, $replicated_volumes) = @_; my ($storecfg, $vmid, $conf, $storagemap, $replicated_volumes) = @_;
my $local_volumes = {}; my $local_volumes = {};
foreach_drive($conf, sub { 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 # If a remote storage is specified and the format of the original
# volume is not available there, fall back to the default format. # volume is not available there, fall back to the default format.
# Otherwise use the same format as the original. # Otherwise use the same format as the original.
if ($targetstorage && $targetstorage ne "1") { if (!$storagemap->{identity}) {
$storeid = $targetstorage; $storeid = map_storage($storagemap, $storeid);
my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid); my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
my $scfg = PVE::Storage::storage_config($storecfg, $storeid); my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
my $fileFormat = qemu_img_format($scfg, $volname); my $fileFormat = qemu_img_format($scfg, $volname);
@ -4772,7 +4794,7 @@ sub vm_migrate_alloc_nbd_disks {
# see vm_start_nolock for parameters, additionally: # see vm_start_nolock for parameters, additionally:
# migrate_opts: # migrate_opts:
# targetstorage = storageid/'1' - target storage for disks migrated over NBD # storagemap = parsed storage map for allocating NBD disks
sub vm_start { sub vm_start {
my ($storecfg, $vmid, $params, $migrate_opts) = @_; 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}); 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}) $migrate_opts->{nbd} = vm_migrate_alloc_nbd_disks($storecfg, $vmid, $conf, $migrate_opts->{storagemap}, $migrate_opts->{replicated_volumes})
if $migrate_opts->{targetstorage}; if $migrate_opts->{storagemap};
vm_start_nolock($storecfg, $vmid, $conf, $params, $migrate_opts); vm_start_nolock($storecfg, $vmid, $conf, $params, $migrate_opts);
}); });