From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 0F7DD9144D for ; Thu, 6 Oct 2022 14:45:57 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 712901C6D6 for ; Thu, 6 Oct 2022 14:44:59 +0200 (CEST) Received: from lana.proxmox.com (unknown [94.136.29.99]) by firstgate.proxmox.com (Proxmox) with ESMTP for ; Thu, 6 Oct 2022 14:44:57 +0200 (CEST) Received: by lana.proxmox.com (Postfix, from userid 10043) id CDB4B2C2331; Thu, 6 Oct 2022 14:44:48 +0200 (CEST) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Date: Thu, 6 Oct 2022 14:44:47 +0200 Message-Id: <20221006124447.120701-9-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20221006124447.120701-1-s.hanreich@proxmox.com> References: <20221006124447.120701-1-s.hanreich@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.345 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods NO_DNS_FOR_FROM 0.001 Envelope sender has no MX or A DNS records RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [qemumigrate.pm, qemumigratemock.pm] Subject: [pve-devel] [PATCH v2 qemu-server 2/2] add migration hooks to VM migration process X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 06 Oct 2022 12:45:57 -0000 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 --- 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