From 3392d6cacf9725a778d4a29d0c7ca08e4b5c2d5d Mon Sep 17 00:00:00 2001 From: Stefan Reiter Date: Tue, 19 Nov 2019 12:23:48 +0100 Subject: [PATCH] refactor: extract QEMU machine related helpers to package ...PVE::QemuServer::Machine. qemu_machine_feature_enabled is exported since it has a *lot* of users in PVE::QemuServer and a long enough name as it is. Signed-off-by: Stefan Reiter --- PVE/QemuConfig.pm | 3 +- PVE/QemuMigrate.pm | 3 +- PVE/QemuServer.pm | 103 +++----------------------------------- PVE/QemuServer/Machine.pm | 101 +++++++++++++++++++++++++++++++++++++ PVE/QemuServer/Makefile | 1 + PVE/VZDump/QemuServer.pm | 3 +- 6 files changed, 116 insertions(+), 98 deletions(-) create mode 100644 PVE/QemuServer/Machine.pm diff --git a/PVE/QemuConfig.pm b/PVE/QemuConfig.pm index c3b9ac5..f13c7b0 100644 --- a/PVE/QemuConfig.pm +++ b/PVE/QemuConfig.pm @@ -8,6 +8,7 @@ use PVE::INotify; use PVE::QemuServer::Helpers; use PVE::QemuServer::Monitor qw(mon_cmd); use PVE::QemuServer; +use PVE::QemuServer::Machine; use PVE::Storage; use PVE::Tools; @@ -165,7 +166,7 @@ sub __snapshot_save_vmstate { $name .= ".raw" if $scfg->{path}; # add filename extension for file base storage my $statefile = PVE::Storage::vdisk_alloc($storecfg, $target, $vmid, 'raw', $name, $size*1024); - my $runningmachine = PVE::QemuServer::get_current_qemu_machine($vmid); + my $runningmachine = PVE::QemuServer::Machine::get_current_qemu_machine($vmid); if ($suspend) { $conf->{vmstate} = $statefile; diff --git a/PVE/QemuMigrate.pm b/PVE/QemuMigrate.pm index 0251561..d3e434c 100644 --- a/PVE/QemuMigrate.pm +++ b/PVE/QemuMigrate.pm @@ -11,6 +11,7 @@ use PVE::Tools; use PVE::Cluster; use PVE::Storage; use PVE::QemuServer; +use PVE::QemuServer::Machine; use PVE::QemuServer::Monitor qw(mon_cmd); use Time::HiRes qw( usleep ); use PVE::RPCEnvironment; @@ -216,7 +217,7 @@ sub prepare { die "can't migrate running VM without --online\n" if !$online; $running = $pid; - $self->{forcemachine} = PVE::QemuServer::qemu_machine_pxe($vmid, $conf); + $self->{forcemachine} = PVE::QemuServer::Machine::qemu_machine_pxe($vmid, $conf); } my $loc_res = PVE::QemuServer::check_local_resources($conf, 1); diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 324be80..1d56810 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -43,6 +43,7 @@ use PVE::QMPClient; use PVE::QemuConfig; use PVE::QemuServer::Helpers; use PVE::QemuServer::Cloudinit; +use PVE::QemuServer::Machine qw(qemu_machine_feature_enabled); use PVE::QemuServer::Memory; use PVE::QemuServer::Monitor qw(mon_cmd); use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port); @@ -1814,20 +1815,14 @@ sub path_is_scsi { return $res; } -sub machine_type_is_q35 { - my ($conf) = @_; - - return $conf->{machine} && ($conf->{machine} =~ m/q35/) ? 1 : 0; -} - sub print_tabletdevice_full { my ($conf, $arch) = @_; - my $q35 = machine_type_is_q35($conf); + my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf); # we use uhci for old VMs because tablet driver was buggy in older qemu my $usbbus; - if (machine_type_is_q35($conf) || $arch eq 'aarch64') { + if (PVE::QemuServer::Machine::machine_type_is_q35($conf) || $arch eq 'aarch64') { $usbbus = 'ehci'; } else { $usbbus = 'uhci'; @@ -2186,7 +2181,7 @@ sub print_vga_device { $memory = ",ram_size=67108864,vram_size=33554432"; } - my $q35 = machine_type_is_q35($conf); + my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf); my $vgaid = "vga" . ($id // ''); my $pciaddr; @@ -3468,7 +3463,7 @@ sub config_to_command { die "detected old qemu-kvm binary ($kvmver)\n" if $vernum < 15000; - my $q35 = machine_type_is_q35($conf); + my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf); my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1'); my $use_old_bios_files = undef; ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type); @@ -4109,7 +4104,7 @@ sub vm_devices_list { sub vm_deviceplug { my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_; - my $q35 = machine_type_is_q35($conf); + my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf); my $devices_list = vm_devices_list($vmid); return 1 if defined($devices_list->{$deviceid}); @@ -4185,7 +4180,7 @@ sub vm_deviceplug { return undef if !qemu_netdevadd($vmid, $conf, $arch, $device, $deviceid); - my $machine_type = PVE::QemuServer::qemu_machine_pxe($vmid, $conf); + my $machine_type = PVE::QemuServer::Machine::qemu_machine_pxe($vmid, $conf); my $use_old_bios_files = undef; ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type); @@ -4499,7 +4494,7 @@ sub qemu_usb_hotplug { sub qemu_cpu_hotplug { my ($vmid, $conf, $vcpus) = @_; - my $machine_type = PVE::QemuServer::get_current_qemu_machine($vmid); + my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid); my $sockets = 1; $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused @@ -7007,94 +7002,12 @@ no_data_clone: return $disk; } -# this only works if VM is running -sub get_current_qemu_machine { - my ($vmid) = @_; - - my $res = mon_cmd($vmid, "query-machines"); - - my ($current, $default); - foreach my $e (@$res) { - $default = $e->{name} if $e->{'is-default'}; - $current = $e->{name} if $e->{'is-current'}; - } - - # fallback to the default machine if current is not supported by qemu - return $current || $default || 'pc'; -} - sub get_running_qemu_version { my ($vmid) = @_; my $res = mon_cmd($vmid, "query-version"); return "$res->{qemu}->{major}.$res->{qemu}->{minor}"; } -sub qemu_machine_feature_enabled { - my ($machine, $kvmver, $version_major, $version_minor) = @_; - - my $current_major; - my $current_minor; - - if ($machine && $machine =~ m/^((?:pc(-i440fx|-q35)?|virt)-(\d+)\.(\d+))/) { - - $current_major = $3; - $current_minor = $4; - - } elsif ($kvmver =~ m/^(\d+)\.(\d+)/) { - - $current_major = $1; - $current_minor = $2; - } - - return 1 if version_cmp($current_major, $version_major, $current_minor, $version_minor) >= 0; -} - -# gets in pairs the versions you want to compares, i.e.: -# ($a-major, $b-major, $a-minor, $b-minor, $a-extra, $b-extra, ...) -# returns 0 if same, -1 if $a is older than $b, +1 if $a is newer than $b -sub version_cmp { - my @versions = @_; - - my $size = scalar(@versions); - - return 0 if $size == 0; - die "cannot compare odd count of versions" if $size & 1; - - for (my $i = 0; $i < $size; $i += 2) { - my ($a, $b) = splice(@versions, 0, 2); - $a //= 0; - $b //= 0; - - return 1 if $a > $b; - return -1 if $a < $b; - } - 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 { - my ($vmid, $major, $minor, $extra) = @_; - - my $v = mon_cmd($vmid, "query-version"); - die "could not query currently running version for VM $vmid\n" if !defined($v); - $v = $v->{qemu}; - - return version_cmp($v->{major}, $major, $v->{minor}, $minor, $v->{micro}, $extra) >= 0; -} - -sub qemu_machine_pxe { - my ($vmid, $conf) = @_; - - my $machine = PVE::QemuServer::get_current_qemu_machine($vmid); - - if ($conf->{machine} && $conf->{machine} =~ m/\.pxe$/) { - $machine .= '.pxe'; - } - - return $machine; -} - sub qemu_use_old_bios_files { my ($machine_type) = @_; diff --git a/PVE/QemuServer/Machine.pm b/PVE/QemuServer/Machine.pm new file mode 100644 index 0000000..c3e0004 --- /dev/null +++ b/PVE/QemuServer/Machine.pm @@ -0,0 +1,101 @@ +package PVE::QemuServer::Machine; + +use strict; +use warnings; + +use PVE::QemuServer::Monitor; + +use base 'Exporter'; +our @EXPORT_OK = qw( +qemu_machine_feature_enabled +); + +sub machine_type_is_q35 { + my ($conf) = @_; + + return $conf->{machine} && ($conf->{machine} =~ m/q35/) ? 1 : 0; +} + +# this only works if VM is running +sub get_current_qemu_machine { + my ($vmid) = @_; + + my $res = PVE::QemuServer::Monitor::mon_cmd($vmid, 'query-machines'); + + my ($current, $default); + foreach my $e (@$res) { + $default = $e->{name} if $e->{'is-default'}; + $current = $e->{name} if $e->{'is-current'}; + } + + # fallback to the default machine if current is not supported by qemu + return $current || $default || 'pc'; +} + +sub qemu_machine_feature_enabled { + my ($machine, $kvmver, $version_major, $version_minor) = @_; + + my $current_major; + my $current_minor; + + if ($machine && $machine =~ m/^((?:pc(-i440fx|-q35)?|virt)-(\d+)\.(\d+))/) { + + $current_major = $3; + $current_minor = $4; + + } elsif ($kvmver =~ m/^(\d+)\.(\d+)/) { + + $current_major = $1; + $current_minor = $2; + } + + return 1 if version_cmp($current_major, $version_major, $current_minor, $version_minor) >= 0; +} + +# gets in pairs the versions you want to compares, i.e.: +# ($a-major, $b-major, $a-minor, $b-minor, $a-extra, $b-extra, ...) +# returns 0 if same, -1 if $a is older than $b, +1 if $a is newer than $b +sub version_cmp { + my @versions = @_; + + my $size = scalar(@versions); + + return 0 if $size == 0; + die "cannot compare odd count of versions" if $size & 1; + + for (my $i = 0; $i < $size; $i += 2) { + my ($a, $b) = splice(@versions, 0, 2); + $a //= 0; + $b //= 0; + + return 1 if $a > $b; + return -1 if $a < $b; + } + 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 { + my ($vmid, $major, $minor, $extra) = @_; + + my $v = PVE::QemuServer::Monitor::mon_cmd($vmid, 'query-version'); + die "could not query currently running version for VM $vmid\n" if !defined($v); + $v = $v->{qemu}; + + return version_cmp($v->{major}, $major, $v->{minor}, $minor, $v->{micro}, $extra) >= 0; +} + +sub qemu_machine_pxe { + my ($vmid, $conf) = @_; + + my $machine = get_current_qemu_machine($vmid); + + if ($conf->{machine} && $conf->{machine} =~ m/\.pxe$/) { + $machine .= '.pxe'; + } + + return $machine; +} + +1; diff --git a/PVE/QemuServer/Makefile b/PVE/QemuServer/Makefile index 02b0209..670105a 100644 --- a/PVE/QemuServer/Makefile +++ b/PVE/QemuServer/Makefile @@ -7,6 +7,7 @@ SOURCES=PCI.pm \ Agent.pm \ Helpers.pm \ Monitor.pm \ + Machine.pm \ .PHONY: install install: ${SOURCES} diff --git a/PVE/VZDump/QemuServer.pm b/PVE/VZDump/QemuServer.pm index ca93258..1826edb 100644 --- a/PVE/VZDump/QemuServer.pm +++ b/PVE/VZDump/QemuServer.pm @@ -19,6 +19,7 @@ use PVE::Tools; use PVE::VZDump; use PVE::QemuServer; +use PVE::QemuServer::Machine; use PVE::QemuServer::Monitor qw(mon_cmd); use base qw (PVE::VZDump::Plugin); @@ -79,7 +80,7 @@ sub prepare { $self->loginfo("exclude disk '$ds' '$volid' (backup=no)"); return; } elsif ($self->{vm_was_running} && $drive->{iothread}) { - if (!PVE::QemuServer::runs_at_least_qemu_version($vmid, 4, 0, 1)) { + if (!PVE::QemuServer::Machine::runs_at_least_qemu_version($vmid, 4, 0, 1)) { die "disk '$ds' '$volid' (iothread=on) can't use backup feature with running QEMU " . "version < 4.0.1! Either set backup=no for this drive or upgrade QEMU and restart VM\n"; } -- 2.39.2