public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu
@ 2023-10-26  8:57 Alexandre Derumier
  2023-10-26  8:57 ` [pve-devel] [PATCH v5 qemu-server 1/3] migration: move livemigration code in a dedicated sub Alexandre Derumier
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Alexandre Derumier @ 2023-10-26  8:57 UTC (permalink / raw)
  To: pve-devel

This patch series allow remote migration between cluster with different cpu model.

2 new params are introduced: "target-cpu" && "restart"

If target-cpu is defined, this will replace the cpu model of the target vm.

If vm is online/running, an extra "target-reboot" safeguard option is needed.
Indeed, as the target cpu is different, the live migration with memory transfert
is skipped (as anyway, the target will die with a different cpu).

Then, after the storage copy, we switch source vm disk to the targetvm nbd export,
then shutdown the source vm and restart the target vm.
(Like a virtual reboot between source/target)



We have redone a lot of migration this summer( maybe another 4000vm),
0 corruption, windows or linux guest vms.


Changelog v2:

The first version was simply shuting down the target vm,
wihout doing the block-job-complete.

After doing production migration with around 400vms, I had
some fs corruption, like some datas was still in buffer.

This v2 has been tested with another 400vms batch, without
any corruption.


Changelog v3:

v2 was not perfect, still have some 1 or 2 fs corruption with vms doing
a lot of write.

This v3 retake idea of the v1 but in a cleaner way

- we migrate disk to target vm
- source vm is switching disk to the nbd of the target vm.
  (with a block-job-complete, and not a block-job-cancel with standard disk migration).
  We are 100% sure it that no pending write is still pending in the migration job.
- source vm is shutdown
- target with is restart


Changelog v4:
 - bugfix: no not override cpu with empty config if targetcpu is not defined
 - small cleanups with params

Changelov V5:
 - Fix fiona comments
 - use "restart" param instead "target-reboot"
 - split target-cpu param in separated patch



Alexandre Derumier (3):
  migration: move livemigration code in a dedicated sub
  remote-migration: add restart param
  add target-cpu param

 PVE/API2/Qemu.pm   |  26 +++
 PVE/CLI/qm.pm      |  12 ++
 PVE/QemuMigrate.pm | 452 ++++++++++++++++++++++++---------------------
 3 files changed, 281 insertions(+), 209 deletions(-)

-- 
2.39.2




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH v5 qemu-server 1/3] migration: move livemigration code in a dedicated sub
  2023-10-26  8:57 [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu Alexandre Derumier
@ 2023-10-26  8:57 ` Alexandre Derumier
  2023-10-26  8:57 ` [pve-devel] [PATCH v5 qemu-server 2/3] remote-migration: add restart param Alexandre Derumier
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Alexandre Derumier @ 2023-10-26  8:57 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/QemuMigrate.pm | 420 +++++++++++++++++++++++----------------------
 1 file changed, 214 insertions(+), 206 deletions(-)

diff --git a/PVE/QemuMigrate.pm b/PVE/QemuMigrate.pm
index 111eeb0..7dd3455 100644
--- a/PVE/QemuMigrate.pm
+++ b/PVE/QemuMigrate.pm
@@ -728,6 +728,219 @@ sub cleanup_bitmaps {
     }
 }
 
+sub live_migration {
+    my ($self, $vmid, $migrate_uri, $spice_port) = @_;
+
+    my $conf = $self->{vmconf};
+
+    $self->log('info', "starting online/live migration on $migrate_uri");
+    $self->{livemigration} = 1;
+
+    # load_defaults
+    my $defaults = PVE::QemuServer::load_defaults();
+
+    $self->log('info', "set migration capabilities");
+    eval { PVE::QemuServer::set_migration_caps($vmid) };
+    warn $@ if $@;
+
+    my $qemu_migrate_params = {};
+
+    # migrate speed can be set via bwlimit (datacenter.cfg and API) and via the
+    # migrate_speed parameter in qm.conf - take the lower of the two.
+    my $bwlimit = $self->get_bwlimit();
+
+    my $migrate_speed = $conf->{migrate_speed} // 0;
+    $migrate_speed *= 1024; # migrate_speed is in MB/s, bwlimit in KB/s
+
+    if ($bwlimit && $migrate_speed) {
+	$migrate_speed = ($bwlimit < $migrate_speed) ? $bwlimit : $migrate_speed;
+    } else {
+	$migrate_speed ||= $bwlimit;
+    }
+    $migrate_speed ||= ($defaults->{migrate_speed} || 0) * 1024;
+
+    if ($migrate_speed) {
+	$migrate_speed *= 1024; # qmp takes migrate_speed in B/s.
+	$self->log('info', "migration speed limit: ". render_bytes($migrate_speed, 1) ."/s");
+    } else {
+	# always set migrate speed as QEMU default to 128 MiBps == 1 Gbps, use 16 GiBps == 128 Gbps
+	$migrate_speed = (16 << 30);
+    }
+    $qemu_migrate_params->{'max-bandwidth'} = int($migrate_speed);
+
+    my $migrate_downtime = $defaults->{migrate_downtime};
+    $migrate_downtime = $conf->{migrate_downtime} if defined($conf->{migrate_downtime});
+    # migrate-set-parameters expects limit in ms
+    $migrate_downtime *= 1000;
+    $self->log('info', "migration downtime limit: $migrate_downtime ms");
+    $qemu_migrate_params->{'downtime-limit'} = int($migrate_downtime);
+
+    # set cachesize to 10% of the total memory
+    my $memory = get_current_memory($conf->{memory});
+    my $cachesize = int($memory * 1048576 / 10);
+    $cachesize = round_powerof2($cachesize);
+
+    $self->log('info', "migration cachesize: " . render_bytes($cachesize, 1));
+    $qemu_migrate_params->{'xbzrle-cache-size'} = int($cachesize);
+
+    $self->log('info', "set migration parameters");
+    eval {
+	mon_cmd($vmid, "migrate-set-parameters", %{$qemu_migrate_params});
+    };
+    $self->log('info', "migrate-set-parameters error: $@") if $@;
+
+    if (PVE::QemuServer::vga_conf_has_spice($conf->{vga}) && !$self->{opts}->{remote}) {
+	my $rpcenv = PVE::RPCEnvironment::get();
+	my $authuser = $rpcenv->get_user();
+
+	my (undef, $proxyticket) = PVE::AccessControl::assemble_spice_ticket($authuser, $vmid, $self->{node});
+
+	my $filename = "/etc/pve/nodes/$self->{node}/pve-ssl.pem";
+	my $subject =  PVE::AccessControl::read_x509_subject_spice($filename);
+
+	$self->log('info', "spice client_migrate_info");
+
+	eval {
+	    mon_cmd($vmid, "client_migrate_info", protocol => 'spice',
+						hostname => $proxyticket, 'port' => 0, 'tls-port' => $spice_port,
+						'cert-subject' => $subject);
+	};
+	$self->log('info', "client_migrate_info error: $@") if $@;
+
+    }
+
+    my $start = time();
+
+    $self->log('info', "start migrate command to $migrate_uri");
+    eval {
+	mon_cmd($vmid, "migrate", uri => $migrate_uri);
+    };
+    my $merr = $@;
+    $self->log('info', "migrate uri => $migrate_uri failed: $merr") if $merr;
+
+    my $last_mem_transferred = 0;
+    my $usleep = 1000000;
+    my $i = 0;
+    my $err_count = 0;
+    my $lastrem = undef;
+    my $downtimecounter = 0;
+    while (1) {
+	$i++;
+	my $avglstat = $last_mem_transferred ? $last_mem_transferred / $i : 0;
+
+	usleep($usleep);
+
+	my $stat = eval { mon_cmd($vmid, "query-migrate") };
+	if (my $err = $@) {
+	    $err_count++;
+	    warn "query migrate failed: $err\n";
+	    $self->log('info', "query migrate failed: $err");
+	    if ($err_count <= 5) {
+		usleep(1_000_000);
+		next;
+	    }
+	    die "too many query migrate failures - aborting\n";
+	}
+
+	my $status = $stat->{status};
+	if (defined($status) && $status =~ m/^(setup)$/im) {
+	    sleep(1);
+	    next;
+	}
+
+	if (!defined($status) || $status !~ m/^(active|completed|failed|cancelled)$/im) {
+	    die $merr if $merr;
+	    die "unable to parse migration status '$status' - aborting\n";
+	}
+	$merr = undef;
+	$err_count = 0;
+
+	my $memstat = $stat->{ram};
+
+	if ($status eq 'completed') {
+	    my $delay = time() - $start;
+	    if ($delay > 0) {
+		my $total = $memstat->{total} || 0;
+		my $avg_speed = render_bytes($total / $delay, 1);
+		my $downtime = $stat->{downtime} || 0;
+		$self->log('info', "average migration speed: $avg_speed/s - downtime $downtime ms");
+	    }
+	}
+
+	if ($status eq 'failed' || $status eq 'cancelled') {
+	    my $message = $stat->{'error-desc'} ? "$status - $stat->{'error-desc'}" : $status;
+	    $self->log('info', "migration status error: $message");
+	    die "aborting\n"
+	}
+
+	if ($status ne 'active') {
+	    $self->log('info', "migration status: $status");
+	    last;
+	}
+
+	if ($memstat->{transferred} ne $last_mem_transferred) {
+	    my $trans = $memstat->{transferred} || 0;
+	    my $rem = $memstat->{remaining} || 0;
+	    my $total = $memstat->{total} || 0;
+	    my $speed = ($memstat->{'pages-per-second'} // 0) * ($memstat->{'page-size'} // 0);
+	    my $dirty_rate = ($memstat->{'dirty-pages-rate'} // 0) * ($memstat->{'page-size'} // 0);
+
+	    # reduce sleep if remainig memory is lower than the average transfer speed
+	    $usleep = 100_000 if $avglstat && $rem < $avglstat;
+
+	    # also reduce loggin if we poll more frequent
+	    my $should_log = $usleep > 100_000 ? 1 : ($i % 10) == 0;
+
+	    my $total_h = render_bytes($total, 1);
+	    my $transferred_h = render_bytes($trans, 1);
+	    my $speed_h = render_bytes($speed, 1);
+
+	    my $progress = "transferred $transferred_h of $total_h VM-state, ${speed_h}/s";
+
+	    if ($dirty_rate > $speed) {
+		my $dirty_rate_h = render_bytes($dirty_rate, 1);
+		$progress .= ", VM dirties lots of memory: $dirty_rate_h/s";
+	    }
+
+	    $self->log('info', "migration $status, $progress") if $should_log;
+
+	    my $xbzrle = $stat->{"xbzrle-cache"} || {};
+	    my ($xbzrlebytes, $xbzrlepages) = $xbzrle->@{'bytes', 'pages'};
+	    if ($xbzrlebytes || $xbzrlepages) {
+		my $bytes_h = render_bytes($xbzrlebytes, 1);
+
+		my $msg = "send updates to $xbzrlepages pages in $bytes_h encoded memory";
+
+		$msg .= sprintf(", cache-miss %.2f%%", $xbzrle->{'cache-miss-rate'} * 100)
+		    if $xbzrle->{'cache-miss-rate'};
+
+		$msg .= ", overflow $xbzrle->{overflow}" if $xbzrle->{overflow};
+
+		$self->log('info', "xbzrle: $msg") if $should_log;
+	    }
+
+	    if (($lastrem && $rem > $lastrem) || ($rem == 0)) {
+		$downtimecounter++;
+	    }
+	    $lastrem = $rem;
+
+	    if ($downtimecounter > 5) {
+		$downtimecounter = 0;
+		$migrate_downtime *= 2;
+		$self->log('info', "auto-increased downtime to continue migration: $migrate_downtime ms");
+		eval {
+		    # migrate-set-parameters does not touch values not
+		    # specified, so this only changes downtime-limit
+		    mon_cmd($vmid, "migrate-set-parameters", 'downtime-limit' => int($migrate_downtime));
+		};
+		$self->log('info', "migrate-set-parameters error: $@") if $@;
+	    }
+	}
+
+	$last_mem_transferred = $memstat->{transferred};
+    }
+}
+
 sub phase1 {
     my ($self, $vmid) = @_;
 
@@ -1139,212 +1352,7 @@ sub phase2 {
 	}
     }
 
-    $self->log('info', "starting online/live migration on $migrate_uri");
-    $self->{livemigration} = 1;
-
-    # load_defaults
-    my $defaults = PVE::QemuServer::load_defaults();
-
-    $self->log('info', "set migration capabilities");
-    eval { PVE::QemuServer::set_migration_caps($vmid) };
-    warn $@ if $@;
-
-    my $qemu_migrate_params = {};
-
-    # migrate speed can be set via bwlimit (datacenter.cfg and API) and via the
-    # migrate_speed parameter in qm.conf - take the lower of the two.
-    my $bwlimit = $self->get_bwlimit();
-
-    my $migrate_speed = $conf->{migrate_speed} // 0;
-    $migrate_speed *= 1024; # migrate_speed is in MB/s, bwlimit in KB/s
-
-    if ($bwlimit && $migrate_speed) {
-	$migrate_speed = ($bwlimit < $migrate_speed) ? $bwlimit : $migrate_speed;
-    } else {
-	$migrate_speed ||= $bwlimit;
-    }
-    $migrate_speed ||= ($defaults->{migrate_speed} || 0) * 1024;
-
-    if ($migrate_speed) {
-	$migrate_speed *= 1024; # qmp takes migrate_speed in B/s.
-	$self->log('info', "migration speed limit: ". render_bytes($migrate_speed, 1) ."/s");
-    } else {
-	# always set migrate speed as QEMU default to 128 MiBps == 1 Gbps, use 16 GiBps == 128 Gbps
-	$migrate_speed = (16 << 30);
-    }
-    $qemu_migrate_params->{'max-bandwidth'} = int($migrate_speed);
-
-    my $migrate_downtime = $defaults->{migrate_downtime};
-    $migrate_downtime = $conf->{migrate_downtime} if defined($conf->{migrate_downtime});
-    # migrate-set-parameters expects limit in ms
-    $migrate_downtime *= 1000;
-    $self->log('info', "migration downtime limit: $migrate_downtime ms");
-    $qemu_migrate_params->{'downtime-limit'} = int($migrate_downtime);
-
-    # set cachesize to 10% of the total memory
-    my $memory = get_current_memory($conf->{memory});
-    my $cachesize = int($memory * 1048576 / 10);
-    $cachesize = round_powerof2($cachesize);
-
-    $self->log('info', "migration cachesize: " . render_bytes($cachesize, 1));
-    $qemu_migrate_params->{'xbzrle-cache-size'} = int($cachesize);
-
-    $self->log('info', "set migration parameters");
-    eval {
-	mon_cmd($vmid, "migrate-set-parameters", %{$qemu_migrate_params});
-    };
-    $self->log('info', "migrate-set-parameters error: $@") if $@;
-
-    if (PVE::QemuServer::vga_conf_has_spice($conf->{vga}) && !$self->{opts}->{remote}) {
-	my $rpcenv = PVE::RPCEnvironment::get();
-	my $authuser = $rpcenv->get_user();
-
-	my (undef, $proxyticket) = PVE::AccessControl::assemble_spice_ticket($authuser, $vmid, $self->{node});
-
-	my $filename = "/etc/pve/nodes/$self->{node}/pve-ssl.pem";
-	my $subject =  PVE::AccessControl::read_x509_subject_spice($filename);
-
-	$self->log('info', "spice client_migrate_info");
-
-	eval {
-	    mon_cmd($vmid, "client_migrate_info", protocol => 'spice',
-						hostname => $proxyticket, 'port' => 0, 'tls-port' => $spice_port,
-						'cert-subject' => $subject);
-	};
-	$self->log('info', "client_migrate_info error: $@") if $@;
-
-    }
-
-    my $start = time();
-
-    $self->log('info', "start migrate command to $migrate_uri");
-    eval {
-	mon_cmd($vmid, "migrate", uri => $migrate_uri);
-    };
-    my $merr = $@;
-    $self->log('info', "migrate uri => $migrate_uri failed: $merr") if $merr;
-
-    my $last_mem_transferred = 0;
-    my $usleep = 1000000;
-    my $i = 0;
-    my $err_count = 0;
-    my $lastrem = undef;
-    my $downtimecounter = 0;
-    while (1) {
-	$i++;
-	my $avglstat = $last_mem_transferred ? $last_mem_transferred / $i : 0;
-
-	usleep($usleep);
-
-	my $stat = eval { mon_cmd($vmid, "query-migrate") };
-	if (my $err = $@) {
-	    $err_count++;
-	    warn "query migrate failed: $err\n";
-	    $self->log('info', "query migrate failed: $err");
-	    if ($err_count <= 5) {
-		usleep(1_000_000);
-		next;
-	    }
-	    die "too many query migrate failures - aborting\n";
-	}
-
-	my $status = $stat->{status};
-	if (defined($status) && $status =~ m/^(setup)$/im) {
-	    sleep(1);
-	    next;
-	}
-
-	if (!defined($status) || $status !~ m/^(active|completed|failed|cancelled)$/im) {
-	    die $merr if $merr;
-	    die "unable to parse migration status '$status' - aborting\n";
-	}
-	$merr = undef;
-	$err_count = 0;
-
-	my $memstat = $stat->{ram};
-
-	if ($status eq 'completed') {
-	    my $delay = time() - $start;
-	    if ($delay > 0) {
-		my $total = $memstat->{total} || 0;
-		my $avg_speed = render_bytes($total / $delay, 1);
-		my $downtime = $stat->{downtime} || 0;
-		$self->log('info', "average migration speed: $avg_speed/s - downtime $downtime ms");
-	    }
-	}
-
-	if ($status eq 'failed' || $status eq 'cancelled') {
-	    my $message = $stat->{'error-desc'} ? "$status - $stat->{'error-desc'}" : $status;
-	    $self->log('info', "migration status error: $message");
-	    die "aborting\n"
-	}
-
-	if ($status ne 'active') {
-	    $self->log('info', "migration status: $status");
-	    last;
-	}
-
-	if ($memstat->{transferred} ne $last_mem_transferred) {
-	    my $trans = $memstat->{transferred} || 0;
-	    my $rem = $memstat->{remaining} || 0;
-	    my $total = $memstat->{total} || 0;
-	    my $speed = ($memstat->{'pages-per-second'} // 0) * ($memstat->{'page-size'} // 0);
-	    my $dirty_rate = ($memstat->{'dirty-pages-rate'} // 0) * ($memstat->{'page-size'} // 0);
-
-	    # reduce sleep if remainig memory is lower than the average transfer speed
-	    $usleep = 100_000 if $avglstat && $rem < $avglstat;
-
-	    # also reduce loggin if we poll more frequent
-	    my $should_log = $usleep > 100_000 ? 1 : ($i % 10) == 0;
-
-	    my $total_h = render_bytes($total, 1);
-	    my $transferred_h = render_bytes($trans, 1);
-	    my $speed_h = render_bytes($speed, 1);
-
-	    my $progress = "transferred $transferred_h of $total_h VM-state, ${speed_h}/s";
-
-	    if ($dirty_rate > $speed) {
-		my $dirty_rate_h = render_bytes($dirty_rate, 1);
-		$progress .= ", VM dirties lots of memory: $dirty_rate_h/s";
-	    }
-
-	    $self->log('info', "migration $status, $progress") if $should_log;
-
-	    my $xbzrle = $stat->{"xbzrle-cache"} || {};
-	    my ($xbzrlebytes, $xbzrlepages) = $xbzrle->@{'bytes', 'pages'};
-	    if ($xbzrlebytes || $xbzrlepages) {
-		my $bytes_h = render_bytes($xbzrlebytes, 1);
-
-		my $msg = "send updates to $xbzrlepages pages in $bytes_h encoded memory";
-
-		$msg .= sprintf(", cache-miss %.2f%%", $xbzrle->{'cache-miss-rate'} * 100)
-		    if $xbzrle->{'cache-miss-rate'};
-
-		$msg .= ", overflow $xbzrle->{overflow}" if $xbzrle->{overflow};
-
-		$self->log('info', "xbzrle: $msg") if $should_log;
-	    }
-
-	    if (($lastrem && $rem > $lastrem) || ($rem == 0)) {
-		$downtimecounter++;
-	    }
-	    $lastrem = $rem;
-
-	    if ($downtimecounter > 5) {
-		$downtimecounter = 0;
-		$migrate_downtime *= 2;
-		$self->log('info', "auto-increased downtime to continue migration: $migrate_downtime ms");
-		eval {
-		    # migrate-set-parameters does not touch values not
-		    # specified, so this only changes downtime-limit
-		    mon_cmd($vmid, "migrate-set-parameters", 'downtime-limit' => int($migrate_downtime));
-		};
-		$self->log('info', "migrate-set-parameters error: $@") if $@;
-	    }
-	}
-
-	$last_mem_transferred = $memstat->{transferred};
-    }
+    live_migration($self, $vmid, $migrate_uri, $spice_port);
 
     if ($self->{storage_migration}) {
 	# finish block-job with block-job-cancel, to disconnect source VM from NBD
-- 
2.39.2




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH v5 qemu-server 2/3] remote-migration: add restart param
  2023-10-26  8:57 [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu Alexandre Derumier
  2023-10-26  8:57 ` [pve-devel] [PATCH v5 qemu-server 1/3] migration: move livemigration code in a dedicated sub Alexandre Derumier
@ 2023-10-26  8:57 ` Alexandre Derumier
  2023-10-26  8:57 ` [pve-devel] [PATCH v5 qemu-server 3/3] add target-cpu param Alexandre Derumier
  2023-11-17  8:52 ` [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu DERUMIER, Alexandre
  3 siblings, 0 replies; 7+ messages in thread
From: Alexandre Derumier @ 2023-10-26  8:57 UTC (permalink / raw)
  To: pve-devel

This patch add support for migration without memory transfert.

After the optionnal storage migration, we cleanly shutdown source vm
and restart the target vm. (like a virtual restart between source/dest)

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Qemu.pm   | 19 +++++++++++++++++++
 PVE/CLI/qm.pm      |  5 +++++
 PVE/QemuMigrate.pm | 31 ++++++++++++++++++++++++++++---
 3 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 38bdaab..c0ae516 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -4583,6 +4583,11 @@ __PACKAGE__->register_method({
 		optional => 1,
 		default => 0,
 	    },
+	    'restart' => {
+		type => 'boolean',
+		description => "For online migration, skip memory migration and restart the vm.",
+		optional => 1,
+	    },
 	    'target-storage' => get_standard_option('pve-targetstorage', {
 		completion => \&PVE::QemuServer::complete_migration_storage,
 		optional => 0,
@@ -5729,6 +5734,20 @@ __PACKAGE__->register_method({
 		    PVE::QemuServer::nbd_stop($state->{vmid});
 		    return;
 		},
+		'restart' => sub {
+		    my $nocheck = 1;
+		    my $timeout = 1;
+		    my $shutdown = undef;
+		    my $force = undef;
+		    my $keepactive = 1;
+		    PVE::QemuServer::vm_stop($storecfg, $state->{vmid}, $nocheck, $timeout, undef, undef, $keepactive);
+		    my $info = PVE::QemuServer::vm_start_nolock(
+			$state->{storecfg},
+			$state->{vmid},
+			$state->{conf},
+		    );
+		    return;
+		},
 		'resume' => sub {
 		    if (PVE::QemuServer::Helpers::vm_running_locally($state->{vmid})) {
 			PVE::QemuServer::vm_resume($state->{vmid}, 1, 1);
diff --git a/PVE/CLI/qm.pm b/PVE/CLI/qm.pm
index b17b4fe..12c5291 100755
--- a/PVE/CLI/qm.pm
+++ b/PVE/CLI/qm.pm
@@ -189,6 +189,11 @@ __PACKAGE__->register_method({
 		optional => 1,
 		default => 0,
 	    },
+	    'restart' => {
+		type => 'boolean',
+		description => "For online migration , skip memory migration and restart the vm.",
+		optional => 1,
+	    },
 	    'target-storage' => get_standard_option('pve-targetstorage', {
 		completion => \&PVE::QemuServer::complete_migration_storage,
 		optional => 0,
diff --git a/PVE/QemuMigrate.pm b/PVE/QemuMigrate.pm
index 7dd3455..c801362 100644
--- a/PVE/QemuMigrate.pm
+++ b/PVE/QemuMigrate.pm
@@ -731,6 +731,11 @@ sub cleanup_bitmaps {
 sub live_migration {
     my ($self, $vmid, $migrate_uri, $spice_port) = @_;
 
+    if($self->{opts}->{'restart'}){
+	$self->log('info', "using restart migration - skipping live migration.");
+	return;
+    }
+
     my $conf = $self->{vmconf};
 
     $self->log('info', "starting online/live migration on $migrate_uri");
@@ -1358,7 +1363,14 @@ sub phase2 {
 	# finish block-job with block-job-cancel, to disconnect source VM from NBD
 	# to avoid it trying to re-establish it. We are in blockjob ready state,
 	# thus, this command changes to it to blockjob complete (see qapi docs)
-	eval { PVE::QemuServer::qemu_drive_mirror_monitor($vmid, undef, $self->{storage_migration_jobs}, 'cancel'); };
+	my $finish_cmd = "cancel";
+	if ($self->{opts}->{'restart'}) {
+	    # no live migration.
+	    # finish block-job with block-job-complete, the source will switch to remote NDB
+	    # then we cleanly stop the source vm during phase3
+	    $finish_cmd = "complete";
+	}
+	eval { PVE::QemuServer::qemu_drive_mirror_monitor($vmid, undef, $self->{storage_migration_jobs}, $finish_cmd); };
 	if (my $err = $@) {
 	    die "Failed to complete storage migration: $err\n";
 	}
@@ -1575,7 +1587,17 @@ sub phase3_cleanup {
     };
 
     # always stop local VM with nocheck, since config is moved already
-    eval { PVE::QemuServer::vm_stop($self->{storecfg}, $vmid, 1, 1); };
+    my $shutdown_timeout = undef;
+    my $shutdown = undef;
+    my $force_stop = undef;
+    if ($self->{opts}->{'restart'}) {
+	$shutdown_timeout = 180;
+	$shutdown = 1;
+	$force_stop = 1;
+	$self->log('info', "shutting down source vm");
+    }
+
+    eval { PVE::QemuServer::vm_stop($self->{storecfg}, $vmid, 1, 1, $shutdown_timeout, $shutdown, $force_stop); };
     if (my $err = $@) {
 	$self->log('err', "stopping vm failed - $err");
 	$self->{errors} = 1;
@@ -1609,7 +1631,10 @@ sub phase3_cleanup {
     # clear migrate lock
     if ($tunnel && $tunnel->{version} >= 2) {
 	PVE::Tunnel::write_tunnel($tunnel, 10, "unlock");
-
+	if ($self->{opts}->{'restart'}) {
+	    $self->log('info', "restart target vm");
+	    PVE::Tunnel::write_tunnel($tunnel, 10, 'restart');
+	}
 	PVE::Tunnel::finish_tunnel($tunnel);
     } else {
 	my $cmd = [ @{$self->{rem_ssh}}, 'qm', 'unlock', $vmid ];
-- 
2.39.2




^ permalink raw reply	[flat|nested] 7+ messages in thread

* [pve-devel] [PATCH v5 qemu-server 3/3] add target-cpu param
  2023-10-26  8:57 [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu Alexandre Derumier
  2023-10-26  8:57 ` [pve-devel] [PATCH v5 qemu-server 1/3] migration: move livemigration code in a dedicated sub Alexandre Derumier
  2023-10-26  8:57 ` [pve-devel] [PATCH v5 qemu-server 2/3] remote-migration: add restart param Alexandre Derumier
@ 2023-10-26  8:57 ` Alexandre Derumier
  2023-11-17  8:52 ` [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu DERUMIER, Alexandre
  3 siblings, 0 replies; 7+ messages in thread
From: Alexandre Derumier @ 2023-10-26  8:57 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Qemu.pm   | 7 +++++++
 PVE/CLI/qm.pm      | 7 +++++++
 PVE/QemuMigrate.pm | 1 +
 3 files changed, 15 insertions(+)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index c0ae516..291eb2b 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -4597,6 +4597,13 @@ __PACKAGE__->register_method({
 		description => "Mapping from source to target bridges. Providing only a single bridge ID maps all source bridges to that bridge. Providing the special value '1' will map each source bridge to itself.",
 		format => 'bridge-pair-list',
 	    },
+	    'target-cpu' => {
+		optional => 1,
+		description => "Target Emulated CPU model. For online migration, this require restart option",
+		type => 'string',
+		requires => 'restart',
+		format => 'pve-vm-cpu-conf',
+	    },
 	    bwlimit => {
 		description => "Override I/O bandwidth limit (in KiB/s).",
 		optional => 1,
diff --git a/PVE/CLI/qm.pm b/PVE/CLI/qm.pm
index 12c5291..358ace6 100755
--- a/PVE/CLI/qm.pm
+++ b/PVE/CLI/qm.pm
@@ -194,6 +194,13 @@ __PACKAGE__->register_method({
 		description => "For online migration , skip memory migration and restart the vm.",
 		optional => 1,
 	    },
+	    'target-cpu' => {
+		optional => 1,
+		description => "Target Emulated CPU model. For online migration, this require restart option",
+		type => 'string',
+		requires => 'restart',
+		format => 'pve-vm-cpu-conf',
+	    },
 	    'target-storage' => get_standard_option('pve-targetstorage', {
 		completion => \&PVE::QemuServer::complete_migration_storage,
 		optional => 0,
diff --git a/PVE/QemuMigrate.pm b/PVE/QemuMigrate.pm
index c801362..a0e3d04 100644
--- a/PVE/QemuMigrate.pm
+++ b/PVE/QemuMigrate.pm
@@ -998,6 +998,7 @@ sub phase1_remote {
     my ($self, $vmid) = @_;
 
     my $remote_conf = PVE::QemuConfig->load_config($vmid);
+    $remote_conf->{cpu} = $self->{opts}->{'target-cpu'} if $self->{opts}->{'target-cpu'};
     PVE::QemuConfig->update_volume_ids($remote_conf, $self->{volume_map});
 
     my $bridges = map_bridges($remote_conf, $self->{opts}->{bridgemap});
-- 
2.39.2




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu
  2023-10-26  8:57 [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu Alexandre Derumier
                   ` (2 preceding siblings ...)
  2023-10-26  8:57 ` [pve-devel] [PATCH v5 qemu-server 3/3] add target-cpu param Alexandre Derumier
@ 2023-11-17  8:52 ` DERUMIER, Alexandre
  2023-11-17  9:04   ` Thomas Lamprecht
  3 siblings, 1 reply; 7+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-17  8:52 UTC (permalink / raw)
  To: pve-devel

Hi,

Any chance to have it one merged for 8.1 ?


-------- Message initial --------
De: Alexandre Derumier <aderumier@odiso.com>
Répondre à: Proxmox VE development discussion <pve-
devel@lists.proxmox.com>
À: pve-devel@lists.proxmox.com
Objet: [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration:
migration with different cpu
Date: 26/10/2023 10:57:07

This patch series allow remote migration between cluster with different
cpu model.

2 new params are introduced: "target-cpu" && "restart"

If target-cpu is defined, this will replace the cpu model of the target
vm.

If vm is online/running, an extra "target-reboot" safeguard option is
needed.
Indeed, as the target cpu is different, the live migration with memory
transfert
is skipped (as anyway, the target will die with a different cpu).

Then, after the storage copy, we switch source vm disk to the targetvm
nbd export,
then shutdown the source vm and restart the target vm.
(Like a virtual reboot between source/target)



We have redone a lot of migration this summer( maybe another 4000vm),
0 corruption, windows or linux guest vms.


Changelog v2:

The first version was simply shuting down the target vm,
wihout doing the block-job-complete.

After doing production migration with around 400vms, I had
some fs corruption, like some datas was still in buffer.

This v2 has been tested with another 400vms batch, without
any corruption.


Changelog v3:

v2 was not perfect, still have some 1 or 2 fs corruption with vms doing
a lot of write.

This v3 retake idea of the v1 but in a cleaner way

- we migrate disk to target vm
- source vm is switching disk to the nbd of the target vm.
  (with a block-job-complete, and not a block-job-cancel with standard
disk migration).
  We are 100% sure it that no pending write is still pending in the
migration job.
- source vm is shutdown
- target with is restart


Changelog v4:
 - bugfix: no not override cpu with empty config if targetcpu is not
defined
 - small cleanups with params

Changelov V5:
 - Fix fiona comments
 - use "restart" param instead "target-reboot"
 - split target-cpu param in separated patch



Alexandre Derumier (3):
  migration: move livemigration code in a dedicated sub
  remote-migration: add restart param
  add target-cpu param

 PVE/API2/Qemu.pm   |  26 +++
 PVE/CLI/qm.pm      |  12 ++
 PVE/QemuMigrate.pm | 452 ++++++++++++++++++++++++---------------------
 3 files changed, 281 insertions(+), 209 deletions(-)



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu
  2023-11-17  8:52 ` [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu DERUMIER, Alexandre
@ 2023-11-17  9:04   ` Thomas Lamprecht
  2023-11-17  9:11     ` DERUMIER, Alexandre
  0 siblings, 1 reply; 7+ messages in thread
From: Thomas Lamprecht @ 2023-11-17  9:04 UTC (permalink / raw)
  To: Proxmox VE development discussion, DERUMIER, Alexandre

Am 17/11/2023 um 09:52 schrieb DERUMIER, Alexandre:
> Any chance to have it one merged for 8.1 ?

rather unlikely, this is very core stuff and could add regressions to one
of our most important use case, would like to avoid rushing it now, sorry.




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu
  2023-11-17  9:04   ` Thomas Lamprecht
@ 2023-11-17  9:11     ` DERUMIER, Alexandre
  0 siblings, 0 replies; 7+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-17  9:11 UTC (permalink / raw)
  To: pve-devel, t.lamprecht



Am 17/11/2023 um 09:52 schrieb DERUMIER, Alexandre:
> Any chance to have it one merged for 8.1 ?

>>rather unlikely, this is very core stuff and could add regressions to
>>one
>>of our most important use case, would like to avoid rushing it now,
>>sorry.


Ok, no problem.
I'll still maintain it in my own branch for now .

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-11-17  9:11 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-26  8:57 [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu Alexandre Derumier
2023-10-26  8:57 ` [pve-devel] [PATCH v5 qemu-server 1/3] migration: move livemigration code in a dedicated sub Alexandre Derumier
2023-10-26  8:57 ` [pve-devel] [PATCH v5 qemu-server 2/3] remote-migration: add restart param Alexandre Derumier
2023-10-26  8:57 ` [pve-devel] [PATCH v5 qemu-server 3/3] add target-cpu param Alexandre Derumier
2023-11-17  8:52 ` [pve-devel] [PATCH v5 qemu-server 0/3] remote-migration: migration with different cpu DERUMIER, Alexandre
2023-11-17  9:04   ` Thomas Lamprecht
2023-11-17  9:11     ` DERUMIER, Alexandre

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal