mirror of
git://git.proxmox.com/git/qemu-server.git
synced 2024-12-23 17:34:19 +03:00
migrate: add live-migration of replicated disks
with incremental drive-mirror and dirty-bitmap tracking. 1.) get replicated disks that are currently referenced by running VM 2.) add a block-dirty-bitmap to each of them 3.) replicate ALL replicated disks 4.) pass bitmaps from 2) to drive-mirror for disks from 1) 5.) skip replicated disks when cleaning up volumes on either source or target added error handling is just removing the bitmaps if an error occurs at any point after 2, except when the handover to the target node has already happened, since the bitmaps are cleaned up together with the source VM in that case. Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com> Tested-by: Stefan Reiter <s.reiter@proxmox.com>
This commit is contained in:
parent
b9f44d2773
commit
9b6efe436d
@ -447,8 +447,30 @@ sub sync_disks {
|
||||
|
||||
my $rep_cfg = PVE::ReplicationConfig->new();
|
||||
if (my $jobcfg = $rep_cfg->find_local_replication_job($vmid, $self->{node})) {
|
||||
die "can't live migrate VM with replicated volumes\n" if $self->{running};
|
||||
if ($self->{running}) {
|
||||
my $live_replicatable_volumes = {};
|
||||
PVE::QemuServer::foreach_drive($conf, sub {
|
||||
my ($ds, $drive) = @_;
|
||||
|
||||
my $volid = $drive->{file};
|
||||
$live_replicatable_volumes->{$ds} = $volid
|
||||
if defined($replicatable_volumes->{$volid});
|
||||
});
|
||||
foreach my $drive (keys %$live_replicatable_volumes) {
|
||||
my $volid = $live_replicatable_volumes->{$drive};
|
||||
|
||||
my $bitmap = "repl_$drive";
|
||||
|
||||
# start tracking before replication to get full delta + a few duplicates
|
||||
$self->log('info', "$drive: start tracking writes using block-dirty-bitmap '$bitmap'");
|
||||
mon_cmd($vmid, 'block-dirty-bitmap-add', node => "drive-$drive", name => $bitmap);
|
||||
|
||||
# other info comes from target node in phase 2
|
||||
$self->{target_drive}->{$drive}->{bitmap} = $bitmap;
|
||||
}
|
||||
}
|
||||
$self->log('info', "replicating disk images");
|
||||
|
||||
my $start_time = time();
|
||||
my $logfunc = sub { $self->log('info', shift) };
|
||||
$self->{replicated_volumes} = PVE::Replication::run_replication(
|
||||
@ -503,6 +525,8 @@ sub cleanup_remotedisks {
|
||||
my ($self) = @_;
|
||||
|
||||
foreach my $target_drive (keys %{$self->{target_drive}}) {
|
||||
# don't clean up replicated disks!
|
||||
next if defined($self->{target_drive}->{$target_drive}->{bitmap});
|
||||
|
||||
my $drive = PVE::QemuServer::parse_drive($target_drive, $self->{target_drive}->{$target_drive}->{drivestr});
|
||||
my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
|
||||
@ -517,6 +541,16 @@ sub cleanup_remotedisks {
|
||||
}
|
||||
}
|
||||
|
||||
sub cleanup_bitmaps {
|
||||
my ($self) = @_;
|
||||
foreach my $drive (%{$self->{target_drive}}) {
|
||||
my $bitmap = $self->{target_drive}->{$drive}->{bitmap};
|
||||
next if !$bitmap;
|
||||
$self->log('info', "$drive: removing block-dirty-bitmap '$bitmap'");
|
||||
mon_cmd($self->{vmid}, 'block-dirty-bitmap-remove', node => "drive-$drive", name => $bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
sub phase1 {
|
||||
my ($self, $vmid) = @_;
|
||||
|
||||
@ -553,6 +587,12 @@ sub phase1_cleanup {
|
||||
# fixme: try to remove ?
|
||||
}
|
||||
}
|
||||
|
||||
eval { $self->cleanup_bitmaps() };
|
||||
if (my $err =$@) {
|
||||
$self->log('err', $err);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub phase2 {
|
||||
@ -737,9 +777,10 @@ sub phase2 {
|
||||
my $target_sid = PVE::Storage::Plugin::parse_volume_id($target_drive->{file});
|
||||
|
||||
my $bwlimit = PVE::Storage::get_bandwidth_limit('migration', [$source_sid, $target_sid], $opt_bwlimit);
|
||||
my $bitmap = $target->{bitmap};
|
||||
|
||||
$self->log('info', "$drive: start migration to $nbd_uri");
|
||||
PVE::QemuServer::qemu_drive_mirror($vmid, $drive, $nbd_uri, $vmid, undef, $self->{storage_migration_jobs}, 'skip', undef, $bwlimit);
|
||||
PVE::QemuServer::qemu_drive_mirror($vmid, $drive, $nbd_uri, $vmid, undef, $self->{storage_migration_jobs}, 'skip', undef, $bwlimit, $bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -957,6 +998,10 @@ sub phase2_cleanup {
|
||||
if (my $err = $@) {
|
||||
$self->log('err', $err);
|
||||
}
|
||||
eval { $self->cleanup_bitmaps() };
|
||||
if (my $err =$@) {
|
||||
$self->log('err', $err);
|
||||
}
|
||||
}
|
||||
|
||||
my $nodename = PVE::INotify::nodename();
|
||||
@ -1118,6 +1163,9 @@ sub phase3_cleanup {
|
||||
my $volids = $self->{online_local_volumes};
|
||||
|
||||
foreach my $volid (@$volids) {
|
||||
# keep replicated volumes!
|
||||
next if $self->{replicated_volumes}->{$volid};
|
||||
|
||||
eval { PVE::Storage::vdisk_free($self->{storecfg}, $volid); };
|
||||
if (my $err = $@) {
|
||||
$self->log('err', "removing local copy of '$volid' failed - $err");
|
||||
|
@ -4755,11 +4755,18 @@ sub vm_start {
|
||||
$local_volumes->{$ds} = [$volid, $storeid, $volname];
|
||||
});
|
||||
|
||||
my $replicatable_volumes = PVE::QemuConfig->get_replicatable_volumes($storecfg, $vmid, $conf);
|
||||
|
||||
my $format = undef;
|
||||
|
||||
foreach my $opt (sort keys %$local_volumes) {
|
||||
|
||||
my ($volid, $storeid, $volname) = @{$local_volumes->{$opt}};
|
||||
if ($replicatable_volumes->{$volid}) {
|
||||
# re-use existing, replicated volume with bitmap on source side
|
||||
$local_volumes->{$opt} = $conf->{${opt}};
|
||||
next;
|
||||
}
|
||||
my $drive = parse_drive($opt, $conf->{$opt});
|
||||
|
||||
# If a remote storage is specified and the format of the original
|
||||
|
Loading…
Reference in New Issue
Block a user