[pve-devel] [PATCH v2 qemu-server 2/2] add migration hooks to VM migration process
Stefan Hanreich
s.hanreich at proxmox.com
Thu Oct 6 14:44:47 CEST 2022
This calls the newly added commands in the previous commit to run the
migrate-hooks during the migration process. When a tunnel already
exists, the tunnel gets reused otherwise it creates a new ad-hoc tunnel
that is used for running the migration-hook.
Additionally I added some mock methods to the QemuMigrateMock class, so
the test class supports the newly added commands as well.
Signed-off-by: Stefan Hanreich <s.hanreich at proxmox.com>
---
PVE/QemuMigrate.pm | 108 ++++++++++++++++++++++++++
test/MigrationTest/QemuMigrateMock.pm | 11 ++-
2 files changed, 118 insertions(+), 1 deletion(-)
diff --git a/PVE/QemuMigrate.pm b/PVE/QemuMigrate.pm
index d52dc8d..42cf7d0 100644
--- a/PVE/QemuMigrate.pm
+++ b/PVE/QemuMigrate.pm
@@ -203,6 +203,8 @@ sub prepare {
eval { $self->cmd_quiet($cmd); };
die "Can't connect to destination address using public key\n" if $@;
+ $self->migration_hook($vmid, 'pre');
+
return $running;
}
@@ -1216,6 +1218,10 @@ sub phase3_cleanup {
}
}
+ if (!$self->{errors}) {
+ $self->migration_hook($vmid, 'post');
+ }
+
# close tunnel on successful migration, on error phase2_cleanup closed it
if ($tunnel) {
eval { PVE::Tunnel::finish_tunnel($tunnel); };
@@ -1284,4 +1290,106 @@ sub round_powerof2 {
return 2 << int(log($_[0]-1)/log(2));
}
+sub migration_hook {
+ my ($self, $vmid, $phase) = @_;
+
+ if (!$self->{vmconf}->{hookscript}) {
+ return;
+ }
+
+ my $stop_on_error = $phase eq 'pre';
+
+ PVE::GuestHelpers::exec_hookscript(
+ $self->{vmconf},
+ $vmid,
+ "$phase-migrate",
+ $stop_on_error,
+ );
+
+ my $tunnel;
+
+ eval {
+ $tunnel = $self->{tunnel} // $self->fork_tunnel();
+ };
+ die $@ if $@;
+
+ my $close_tunnel = sub {
+ if (!$self->{tunnel}) {
+ $self->log('info', "closing tunnel for migration hook");
+ PVE::Tunnel::finish_tunnel($tunnel);
+ }
+ };
+
+ my $source = PVE::INotify::nodename();
+ my $target = $self->{node};
+
+ eval {
+ $self->log('info', "running hook $phase-migrate on target");
+ PVE::Tunnel::write_tunnel($tunnel, 30, "migrate-hook $vmid $phase $source $target");
+ };
+ my $err = $@;
+
+ if ($err =~ /no reply to command/) {
+ eval {
+ $close_tunnel->();
+ };
+ if ($@) {
+ die $err;
+ } else {
+ $self->log('warn', 'Got timeout when trying to run migrate-hook. Target doesn\'t support migrate hooks (old version?). Still continuing with migration.');
+ return;
+ }
+ } elsif ($err) {
+ $close_tunnel->();
+ die $err;
+ }
+
+ $self->log('info', "successfully started hook $phase-migrate on target");
+
+ my $running = 1;
+
+ while ($running) {
+ eval {
+ PVE::Tunnel::write_tunnel($tunnel, 30, "query-migrate-hook");
+ my $status = PVE::Tunnel::read_tunnel($tunnel, 30);
+
+ if ($status eq 'running') {
+ sleep(5);
+ } elsif ($status eq 'finished') {
+ my $output = MIME::Base64::decode(
+ PVE::Tunnel::read_tunnel($tunnel, 30)
+ );
+
+ $self->log('info', "$phase-migrate hook ran successfully on target:\n" . $output);
+ } elsif ($status eq 'error') {
+ my $output = MIME::Base64::decode(
+ PVE::Tunnel::read_tunnel($tunnel, 30)
+ );
+
+ my $msg = "An error occured during running the hookscript:\n" . $output;
+
+ if ($stop_on_error) {
+ die $msg;
+ } else {
+ $self->log('warn', $msg)
+ }
+ } else {
+ die "Invalid response!"
+ }
+
+ $running = $status eq 'running';
+ };
+ if ($@) {
+ $err = $@;
+ last;
+ }
+ }
+
+ eval {
+ $close_tunnel->();
+ };
+ die $err if $err; # use the initial error if it exists
+ die $@ if $@;
+}
+
1;
diff --git a/test/MigrationTest/QemuMigrateMock.pm b/test/MigrationTest/QemuMigrateMock.pm
index f2c0281..e33a284 100644
--- a/test/MigrationTest/QemuMigrateMock.pm
+++ b/test/MigrationTest/QemuMigrateMock.pm
@@ -64,6 +64,8 @@ $tunnel_module->mock(
my $vmid = $1;
die "resuming wrong VM '$vmid'\n" if $vmid ne $test_vmid;
return;
+ } elsif ($command =~ /^migrate-hook.*/) {
+ return;
}
die "write_tunnel (mocked) - implement me: $command\n";
},
@@ -72,7 +74,12 @@ $tunnel_module->mock(
my $qemu_migrate_module = Test::MockModule->new("PVE::QemuMigrate");
$qemu_migrate_module->mock(
fork_tunnel => sub {
- die "fork_tunnel (mocked) - implement me\n"; # currently no call should lead here
+ return {
+ writer => "mocked",
+ reader => "mocked",
+ pid => 123456,
+ version => 1,
+ };
},
read_tunnel => sub {
die "read_tunnel (mocked) - implement me\n"; # currently no call should lead here
@@ -298,6 +305,8 @@ $MigrationTest::Shared::tools_module->mock(
return 0;
} elsif ($cmd eq 'stop') {
return 0;
+ } elsif ($cmd eq 'mtunnel') {
+ return 0;
}
die "run_command (mocked) ssh qm command - implement me: ${cmd_msg}";
} elsif ($cmd eq 'pvesm') {
--
2.30.2
More information about the pve-devel
mailing list