5
0
mirror of git://git.proxmox.com/git/qemu-server.git synced 2025-01-08 21:18:03 +03:00

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,
},
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);
}

View 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;

View 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);
});