5
0
mirror of git://git.proxmox.com/git/qemu-server.git synced 2025-01-11 05:17:57 +03:00

Use 'QEMU version' -> '+pve-version' mapping for machine types

The previously introduced approach can fail for pinned versions when a
new QEMU release is introduced. The saner approach is to use a mapping
that gives one pve-version for each QEMU release.

Fortunately, the old system has not been bumped yet, so we can still
change it without too much effort.

QEMU versions without a mapping are assumed to be pve0, 4.1 is mapped to
pve1 since thats what we had as our default previously.

Pinned machine versions (i.e. pc-i440fx-4.1) are always assumed to be
pve0, for specific pve-versions they'd have to be pinned as well (i.e.
pc-i440fx-4.1+pve1).

The new logic also makes the pve-version dynamic, and starts VMs with
the lowest possible 'feature-level', i.e. if a feature is only available
with 4.1+pve2, but the VM isn't using it, we still start it with
4.1+pve0.

We die if we don't support a version that is requested from us. This
allows us to use the pve-version as live-migration blocks (i.e. bumping
the version and then live-migrating a VM which uses the new feature (so
is running with the bumped version) to an outdated node will present the
user with a helpful error message and fail instead of silently modifying
the config and only failing *after* the migration).

$version_guard is introduced in config_to_command to use for features
that need to check pve-version, it automatically handles selecting the
newest necessary pve-version for the VM.

Tests have to be adjusted, since all of them now resolve to pve0 instead
of pve1. EXPECT_ERROR matching is changed to use 'eq' instead of regex
to allow special characters in error messages.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
This commit is contained in:
Stefan Reiter 2020-02-10 16:05:35 +01:00 committed by Thomas Lamprecht
parent 8541f8cddb
commit ac0077cc33
12 changed files with 92 additions and 22 deletions

View File

@ -3241,14 +3241,24 @@ my $default_machines = {
};
sub get_vm_machine {
my ($conf, $forcemachine, $arch, $add_pve_version) = @_;
my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
my $machine = $forcemachine || $conf->{machine};
if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
$arch //= 'x86_64';
$machine ||= $default_machines->{$arch};
$machine .= "+pve$PVE::QemuServer::Machine::PVE_MACHINE_VERSION" if $add_pve_version;
if ($add_pve_version) {
$kvmversion //= kvm_user_version();
my $pvever = PVE::QemuServer::Machine::get_pve_version($kvmversion);
$machine .= "+pve$pvever";
}
}
if ($add_pve_version && $machine !~ m/\+pve\d+$/) {
# for version-pinned machines that do not include a pve-version (e.g.
# pc-q35-4.1), we assume 0 to keep them stable in case we bump
$machine .= '+pve0';
}
return $machine;
@ -3422,8 +3432,26 @@ sub config_to_command {
$kvm //= 1 if is_native($arch);
$machine_version =~ m/(\d+)\.(\d+)/;
my ($machine_major, $machine_minor) = ($1, $2);
die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type', please upgrade node '$nodename'\n"
if !PVE::QemuServer::min_version($kvmver, $1, $2);
if !PVE::QemuServer::min_version($kvmver, $machine_major, $machine_minor);
if (!PVE::QemuServer::Machine::can_run_pve_machine_version($machine_version, $kvmver)) {
my $max_pve_version = PVE::QemuServer::Machine::get_pve_version($machine_version);
die "Installed qemu-server (max feature level for $machine_major.$machine_minor is pve$max_pve_version)"
. " is too old to run machine type '$machine_type', please upgrade node '$nodename'\n";
}
# if a specific +pve version is required for a feature, use $version_guard
# instead of min_version to allow machines to be run with the minimum
# required version
my $required_pve_version = 0;
my $version_guard = sub {
my ($major, $minor, $pve) = @_;
return 0 if !min_version($machine_version, $major, $minor, $pve);
$required_pve_version = $pve if $pve && $pve > $required_pve_version;
return 1;
};
if ($kvm) {
die "KVM virtualisation configured, but not available. Either disable in VM configuration or enable in BIOS.\n"
@ -3774,14 +3802,6 @@ sub config_to_command {
push @$rtcFlags, 'driftfix=slew' if $tdf;
if (!$kvm) {
push @$machineFlags, 'accel=tcg';
}
if ($machine_type) {
push @$machineFlags, "type=${machine_type}";
}
if (($conf->{startdate}) && ($conf->{startdate} ne 'now')) {
push @$rtcFlags, "base=$conf->{startdate}";
} elsif ($useLocaltime) {
@ -4005,6 +4025,17 @@ sub config_to_command {
}
}
if (!$kvm) {
push @$machineFlags, 'accel=tcg';
}
my $machine_type_min = $machine_type;
if ($add_pve_version) {
$machine_type_min =~ s/\+pve\d+$//;
$machine_type_min .= "+pve$required_pve_version";
}
push @$machineFlags, "type=${machine_type_min}";
push @$cmd, @$devices;
push @$cmd, '-rtc', join(',', @$rtcFlags)
if scalar(@$rtcFlags);

View File

@ -8,7 +8,9 @@ use PVE::QemuServer::Monitor;
# Bump this for VM HW layout changes during a release (where the QEMU machine
# version stays the same)
our $PVE_MACHINE_VERSION = 1;
our $PVE_MACHINE_VERSION = {
'4.1' => 1,
};
sub machine_type_is_q35 {
my ($conf) = @_;
@ -47,7 +49,8 @@ sub extract_version {
return $versionstr;
} elsif (defined($kvmversion)) {
if ($kvmversion =~ m/^(\d+)\.(\d+)/) {
return "$1.$2+pve$PVE_MACHINE_VERSION";
my $pvever = get_pve_version($kvmversion);
return "$1.$2+pve$pvever";
}
}
@ -61,6 +64,37 @@ sub machine_version {
extract_version($machine_type), $major, $minor, $pve);
}
sub get_pve_version {
my ($verstr) = @_;
if ($verstr =~ m/^(\d+\.\d+)/) {
return $PVE_MACHINE_VERSION->{$1} // 0;
}
die "internal error: cannot get pve version for invalid string '$verstr'";
}
sub can_run_pve_machine_version {
my ($machine_version, $kvmversion) = @_;
$machine_version =~ m/^(\d+)\.(\d+)(?:\+pve(\d+))$/;
my $major = $1;
my $minor = $2;
my $pvever = $3;
$kvmversion =~ m/(\d+)\.(\d+)/;
return 0 if PVE::QemuServer::Helpers::version_cmp($1, $major, $2, $minor) < 0;
# if $pvever is missing or 0, we definitely support it as long as we didn't
# fail the QEMU version check above
return 1 if !$pvever;
my $max_supported = get_pve_version("$major.$minor");
return 1 if $max_supported >= $pvever;
return 0;
}
# dies if a) VM not running or not exisiting b) Version query failed
# So, any defined return value is valid, any invalid state can be caught by eval
sub runs_at_least_qemu_version {

View File

@ -33,5 +33,5 @@
-netdev 'type=tap,id=net0,ifname=tap8006i0,script=/var/lib/qemu-server/pve-bridge,downscript=/var/lib/qemu-server/pve-bridgedown,vhost=on' \
-device 'virtio-net-pci,mac=2E:01:68:F9:9C:87,netdev=net0,bus=pci.0,addr=0x12,id=net0,bootindex=300' \
-rtc 'driftfix=slew,base=localtime' \
-machine 'type=pc+pve1' \
-machine 'type=pc+pve0' \
-global 'kvm-pit.lost_tick_policy=discard'

View File

@ -1,5 +1,5 @@
# TEST: newer machine verison than QEMU version installed on node
# QEMU_VERSION: 4.1.1
# EXPECT_ERROR: Installed QEMU version '4.1.1' is too old to run machine type 'pc-q35-42.9', please upgrade node 'localhost'
# EXPECT_ERROR: Installed QEMU version '4.1.1' is too old to run machine type 'pc-q35-42.9+pve0', please upgrade node 'localhost'
smbios1: uuid=6cf17dc3-8341-4ecc-aebd-7503f2583fb3
machine: pc-q35-42.9

View File

@ -0,0 +1,5 @@
# TEST: newer machine verison than QEMU version installed on node
# QEMU_VERSION: 4.1.1
# EXPECT_ERROR: Installed qemu-server (max feature level for 4.0 is pve0) is too old to run machine type 'pc-q35-4.0+pve77', please upgrade node 'localhost'
smbios1: uuid=6cf17dc3-8341-4ecc-aebd-7503f2583fb3
machine: pc-q35-4.0+pve77

View File

@ -21,4 +21,4 @@
-device 'VGA,id=vga,bus=pci.0,addr=0x2' \
-device 'virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3' \
-iscsi 'initiator-name=iqn.1993-08.org.debian:01:aabbccddeeff' \
-machine 'type=pc+pve1'
-machine 'type=pc+pve0'

View File

@ -27,4 +27,4 @@
-device 'scsi-hd,bus=scsihw0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0,id=scsi0,bootindex=100' \
-netdev 'type=tap,id=net0,ifname=tap8006i0,script=/var/lib/qemu-server/pve-bridge,downscript=/var/lib/qemu-server/pve-bridgedown,vhost=on' \
-device 'virtio-net-pci,mac=A2:C0:43:77:08:A1,netdev=net0,bus=pci.0,addr=0x12,id=net0,bootindex=300' \
-machine 'type=pc-q35-3.1'
-machine 'type=pc-q35-3.1+pve0'

View File

@ -31,4 +31,4 @@
-iscsi 'initiator-name=iqn.1993-08.org.debian:01:aabbccddeeff' \
-netdev 'type=tap,id=net0,ifname=tap8006i0,script=/var/lib/qemu-server/pve-bridge,downscript=/var/lib/qemu-server/pve-bridgedown,vhost=on' \
-device 'virtio-net-pci,mac=2E:01:68:F9:9C:87,netdev=net0,bus=pci.0,addr=0x12,id=net0,bootindex=300' \
-machine 'type=q35+pve1'
-machine 'type=q35+pve0'

View File

@ -36,4 +36,4 @@
-iscsi 'initiator-name=iqn.1993-08.org.debian:01:aabbccddeeff' \
-netdev 'type=tap,id=net0,ifname=tap8006i0,script=/var/lib/qemu-server/pve-bridge,downscript=/var/lib/qemu-server/pve-bridgedown,vhost=on' \
-device 'virtio-net-pci,mac=2E:01:68:F9:9C:87,netdev=net0,bus=pci.0,addr=0x12,id=net0,bootindex=300' \
-machine 'type=q35+pve1'
-machine 'type=q35+pve0'

View File

@ -34,5 +34,5 @@
-netdev 'type=tap,id=net0,ifname=tap8006i0,script=/var/lib/qemu-server/pve-bridge,downscript=/var/lib/qemu-server/pve-bridgedown,vhost=on' \
-device 'virtio-net-pci,mac=2E:01:68:F9:9C:87,netdev=net0,bus=pci.0,addr=0x12,id=net0,bootindex=300' \
-rtc 'driftfix=slew,base=localtime' \
-machine 'type=q35+pve1' \
-machine 'type=q35+pve0' \
-global 'kvm-pit.lost_tick_policy=discard'

View File

@ -27,4 +27,4 @@
-iscsi 'initiator-name=iqn.1993-08.org.debian:01:aabbccddeeff' \
-netdev 'type=tap,id=net0,ifname=tap8006i0,script=/var/lib/qemu-server/pve-bridge,downscript=/var/lib/qemu-server/pve-bridgedown,vhost=on' \
-device 'virtio-net-pci,mac=A2:C0:43:67:08:A1,netdev=net0,bus=pci.0,addr=0x12,id=net0,bootindex=300' \
-machine 'type=pc+pve1'
-machine 'type=pc+pve0'

View File

@ -292,7 +292,7 @@ sub do_test($) {
return;
}
chomp $err;
if ($err !~ /^\s*$err_expect\s*$/) {
if ($err ne $err_expect) {
fail("$testname");
note("error does not match expected error: '$err' !~ '$err_expect'");
} else {