use PVE::JSONSchema qw(get_standard_option);
use PVE::ProcFSTools;
use PVE::RPCEnvironment;
-use PVE::SafeSyslog;
use PVE::Storage;
use PVE::SysFSTools;
use PVE::Systemd;
use PVE::QMPClient;
use PVE::QemuConfig;
-use PVE::QemuServer::Helpers;
+use PVE::QemuServer::Helpers qw(min_version);
use PVE::QemuServer::Cloudinit;
+use PVE::QemuServer::Machine;
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);
use PVE::QemuServer::USB qw(parse_usb_device);
],
};
-my $qemu_snap_storage = { rbd => 1 };
-
my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
my $QEMU_FORMAT_RE = qr/raw|cow|qcow|qcow2|qed|vmdk|cloop/;
PVE::JSONSchema::register_standard_option('pve-qemu-machine', {
description => "Specifies the Qemu machine type.",
type => 'string',
- pattern => '(pc|pc(-i440fx)?-\d+(\.\d+)+(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\.pxe)?|virt(?:-\d+(\.\d+)+)?)',
+ pattern => '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
maxLength => 40,
optional => 1,
});
}
-my $nodename = PVE::INotify::nodename();
+my $nodename_cache;
+sub nodename {
+ $nodename_cache //= PVE::INotify::nodename();
+ return $nodename_cache;
+}
my $cpu_vendor_list = {
# Intel CPUs
description => "Configure additional enhancements for SPICE.",
optional => 1
},
+ tags => {
+ type => 'string', format => 'pve-tag-list',
+ description => 'Tags of the VM. This is only meta information.',
+ optional => 1,
+ },
};
my $cicustom_fmt = {
};
PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
-my $PCIRE = qr/[a-f0-9]{2}:[a-f0-9]{2}(?:\.[a-f0-9])?/;
+my $PCIRE = qr/([a-f0-9]{4}:)?[a-f0-9]{2}:[a-f0-9]{2}(?:\.[a-f0-9])?/;
my $hostpci_fmt = {
host => {
default_key => 1,
}
sub print_drive {
- my ($vmid, $drive) = @_;
+ my ($drive) = @_;
my $data = { %$drive };
delete $data->{$_} for qw(index interface);
return PVE::JSONSchema::print_property_string($data, $alldrive_fmt);
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';
}
# for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
+ my $version = PVE::QemuServer::Machine::extract_version($machine_type, kvm_user_version());
if ($path =~ m/^iscsi\:\/\// &&
- !qemu_machine_feature_enabled($machine_type, undef, 4, 1)) {
+ !min_version($version, 4, 1)) {
$devicetype = 'generic';
}
}
};
sub print_vga_device {
- my ($conf, $vga, $arch, $kvmver, $machine, $id, $qxlnum, $bridges) = @_;
+ my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
my $type = $vga_map->{$vga->{type}};
if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
if (!$conf->{ostype} || $conf->{ostype} =~ m/^(?:l\d\d)|(?:other)$/) {
# set max outputs so linux can have up to 4 qxl displays with one device
- if (qemu_machine_feature_enabled($machine, $kvmver, 4, 1)) {
+ if (min_version($machine_version, 4, 1)) {
$max_outputs = ",max_outputs=4";
}
}
$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;
my @idlist = split(/;/, $res->{host});
delete $res->{host};
foreach my $id (@idlist) {
- if ($id =~ m/\./) { # full id 00:00.1
- push @{$res->{pciid}}, {
- id => $id,
- };
- } else { # partial id 00:00
- $res->{pciid} = PVE::SysFSTools::lspci($id);
- }
+ my $devs = PVE::SysFSTools::lspci($id);
+ die "no PCI device found for '$id'\n" if !scalar(@$devs);
+ push @{$res->{pciid}}, @$devs;
}
return $res;
}
my $v = parse_drive($key, $value);
if (my $volid = filename_to_volume_id($vmid, $v->{file}, $v->{media})) {
$v->{file} = $volid;
- $value = print_drive($vmid, $v);
+ $value = print_drive($v);
} else {
warn "vm $vmid - unable to parse value of '$key'\n";
next;
&$cleanup_config($conf->{pending}, 1);
foreach my $snapname (keys %{$conf->{snapshots}}) {
- die "internal error" if $snapname eq 'pending';
+ die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
&$cleanup_config($conf->{snapshots}->{$snapname}, undef, $snapname);
}
my $res = {};
return $res if !$vmlist || !$vmlist->{ids};
my $ids = $vmlist->{ids};
+ my $nodename = nodename();
foreach my $vmid (keys %$ids) {
my $d = $ids->{$vmid};
my $nodelist = PVE::Cluster::get_nodelist();
my $nodehash = { map { $_ => 1 } @$nodelist };
- my $nodename = PVE::INotify::nodename();
+ my $nodename = nodename();
foreach_drive($conf, sub {
my ($ds, $drive) = @_;
return $nodehash
}
-sub check_cmdline {
- my ($pidfile, $pid) = @_;
-
- my $fh = IO::File->new("/proc/$pid/cmdline", "r");
- if (defined($fh)) {
- my $line = <$fh>;
- $fh->close;
- return undef if !$line;
- my @param = split(/\0/, $line);
-
- my $cmd = $param[0];
- return if !$cmd || ($cmd !~ m|kvm$| && $cmd !~ m@(?:^|/)qemu-system-[^/]+$@);
-
- for (my $i = 0; $i < scalar (@param); $i++) {
- my $p = $param[$i];
- next if !$p;
- if (($p eq '-pidfile') || ($p eq '--pidfile')) {
- my $p = $param[$i+1];
- return 1 if $p && ($p eq $pidfile);
- return undef;
- }
- }
- }
- return undef;
-}
-
+# Compat only, use assert_config_exists_on_node and vm_running_locally where possible
sub check_running {
my ($vmid, $nocheck, $node) = @_;
- my $filename = PVE::QemuConfig->config_file($vmid, $node);
-
- die "unable to find configuration file for VM $vmid - no such machine\n"
- if !$nocheck && ! -f $filename;
-
- my $pidfile = PVE::QemuServer::Helpers::pidfile_name($vmid);
-
- if (my $fd = IO::File->new("<$pidfile")) {
- my $st = stat($fd);
- my $line = <$fd>;
- close($fd);
-
- my $mtime = $st->mtime;
- if ($mtime > time()) {
- warn "file '$filename' modified in future\n";
- }
-
- if ($line =~ m/^(\d+)$/) {
- my $pid = $1;
- if (check_cmdline($pidfile, $pid)) {
- if (my $pinfo = PVE::ProcFSTools::check_process_running($pid)) {
- return $pid;
- }
- }
- }
- }
-
- return undef;
+ PVE::QemuConfig::assert_config_exists_on_node($vmid, $node) if !$nocheck;
+ return PVE::QemuServer::Helpers::vm_running_locally($vmid);
}
sub vzlist {
description => "The current config lock, if any.",
type => 'string',
optional => 1,
- }
+ },
+ tags => {
+ description => "The current configured tags, if any",
+ type => 'string',
+ optional => 1,
+ },
};
my $last_proc_pid_stat;
$d->{serial} = 1 if conf_has_serial($conf);
$d->{lock} = $conf->{lock} if $conf->{lock};
+ $d->{tags} = $conf->{tags} if defined($conf->{tags});
$res->{$vmid} = $d;
}
return get_host_arch() eq $arch;
}
+sub get_vm_arch {
+ my ($conf) = @_;
+ return $conf->{arch} // get_host_arch();
+}
+
my $default_machines = {
x86_64 => 'pc',
aarch64 => 'virt',
};
-sub get_basic_machine_info {
- my ($conf, $forcemachine) = @_;
+sub get_vm_machine {
+ my ($conf, $forcemachine, $arch, $add_pve_version) = @_;
- my $arch = $conf->{arch} // get_host_arch();
- my $machine = $forcemachine || $conf->{machine} || $default_machines->{$arch};
- return ($arch, $machine);
+ 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;
+ }
+
+ return $machine;
}
sub get_ovmf_files($) {
}
sub get_cpu_options {
- my ($conf, $arch, $kvm, $machine_type, $kvm_off, $kvmver, $winversion, $gpu_passthrough) = @_;
+ my ($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough) = @_;
my $cpuFlags = [];
my $ostype = $conf->{ostype};
push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
- if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3) && $arch eq 'x86_64') {
+ if (min_version($machine_version, 2, 3) && $arch eq 'x86_64') {
push @$cpuFlags , '+kvm_pv_unhalt' if $kvm;
push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
}
- add_hyperv_enlightenments($cpuFlags, $winversion, $machine_type, $kvmver, $conf->{bios}, $gpu_passthrough, $hv_vendor_id) if $kvm;
+ add_hyperv_enlightenments($cpuFlags, $winversion, $machine_version, $conf->{bios}, $gpu_passthrough, $hv_vendor_id) if $kvm;
push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm && $arch eq 'x86_64';
my $ostype = $conf->{ostype};
my $winversion = windows_version($ostype);
my $kvm = $conf->{kvm};
+ my $nodename = nodename();
- my ($arch, $machine_type) = get_basic_machine_info($conf, $forcemachine);
+ my $arch = get_vm_arch($conf);
my $kvm_binary = get_command_for_arch($arch);
my $kvmver = kvm_user_version($kvm_binary);
+
+ my $add_pve_version = min_version($kvmver, 4, 1);
+
+ my $machine_type = get_vm_machine($conf, $forcemachine, $arch, $add_pve_version);
+ my $machine_version = PVE::QemuServer::Machine::extract_version($machine_type, $kvmver);
$kvm //= 1 if is_native($arch);
+ $machine_version =~ m/(\d+)\.(\d+)/;
+ 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 ($kvm) {
die "KVM virtualisation configured, but not available. Either disable in VM configuration or enable in BIOS.\n"
if !defined kvm_version();
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);
push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait";
push @$cmd, '-mon', "chardev=qmp,mode=control";
- if (qemu_machine_feature_enabled($machine_type, $kvmver, 2, 12)) {
+ if (min_version($machine_version, 2, 12)) {
push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
push @$cmd, '-mon', "chardev=qmp-event,mode=control";
}
# load q35 config
if ($q35) {
# we use different pcie-port hardware for qemu >= 4.0 for passthrough
- if (qemu_machine_feature_enabled($machine_type, $kvmver, 4, 0)) {
+ if (min_version($machine_version, 4, 0)) {
push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
} else {
push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
if (!$vga->{type}) {
if ($arch eq 'aarch64') {
$vga->{type} = 'virtio';
- } elsif (qemu_machine_feature_enabled($machine_type, $kvmver, 2, 9)) {
+ } elsif (min_version($machine_version, 2, 9)) {
$vga->{type} = (!$winversion || $winversion >= 6) ? 'std' : 'cirrus';
} else {
$vga->{type} = ($winversion >= 6) ? 'std' : 'cirrus';
if ($d->{mdev} && scalar(@$pcidevices) == 1) {
my $pci_id = $pcidevices->[0]->{id};
my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
- $sysfspath = "/sys/bus/pci/devices/0000:$pci_id/$uuid";
+ $sysfspath = "/sys/bus/pci/devices/$pci_id/$uuid";
} elsif ($d->{mdev}) {
warn "ignoring mediated device '$id' with multifunction device\n";
}
# usb devices
my $usb_dev_features = {};
- $usb_dev_features->{spice_usb3} = 1 if qemu_machine_feature_enabled($machine_type, $kvmver, 4, 0);
+ $usb_dev_features->{spice_usb3} = 1 if min_version($machine_version, 4, 0);
my @usbdevices = PVE::QemuServer::USB::get_usb_devices($conf, $usbdesc->{format}, $MAX_USB_DEVICES, $usb_dev_features);
push @$devices, @usbdevices if @usbdevices;
die "MAX $allowed_vcpus vcpus allowed per VM on this node\n"
if ($allowed_vcpus < $maxcpus);
- if($hotplug_features->{cpu} && qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 7)) {
+ if($hotplug_features->{cpu} && min_version($machine_version, 2, 7)) {
push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
for (my $i = 2; $i <= $vcpus; $i++) {
push @$cmd, '-no-reboot' if defined($conf->{reboot}) && $conf->{reboot} == 0;
if ($vga->{type} && $vga->{type} !~ m/^serial\d+$/ && $vga->{type} ne 'none'){
- push @$devices, '-device', print_vga_device($conf, $vga, $arch, $kvmver, $machine_type, undef, $qxlnum, $bridges);
+ push @$devices, '-device', print_vga_device($conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
my $socket = PVE::QemuServer::Helpers::vnc_socket($vmid);
push @$cmd, '-vnc', "unix:$socket,password";
} else {
push @$rtcFlags, 'base=localtime';
}
- push @$cmd, get_cpu_options($conf, $arch, $kvm, $machine_type, $kvm_off, $kvmver, $winversion, $gpu_passthrough);
+ push @$cmd, get_cpu_options($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
PVE::QemuServer::Memory::config($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
if ($qxlnum > 1) {
if ($winversion){
for(my $i = 1; $i < $qxlnum; $i++){
- push @$devices, '-device', print_vga_device($conf, $vga, $arch, $kvmver, $machine_type, $i, $qxlnum, $bridges);
+ push @$devices, '-device', print_vga_device($conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
}
} else {
# assume other OS works like Linux
my $pciaddr = print_pci_addr("spice", $bridges, $arch, $machine_type);
- my $nodename = PVE::INotify::nodename();
my $pfamily = PVE::Tools::get_host_address_family($nodename);
my @nodeaddrs = PVE::Tools::getaddrinfo_all('localhost', family => $pfamily);
die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
if (!$q35) {
# add pci bridges
- if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
+ if (min_version($machine_version, 2, 3)) {
$bridges->{1} = 1;
$bridges->{2} = 1;
}
my $statepath = PVE::Storage::path($storecfg, $vmstate);
push @$vollist, $vmstate;
push @$cmd, '-loadstate', $statepath;
+ print "activating and using '$vmstate' as vmstate\n";
}
# add custom args
sub spice_port {
my ($vmid) = @_;
- my $res = vm_mon_cmd($vmid, 'query-spice');
+ my $res = mon_cmd($vmid, 'query-spice');
return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
}
sub vm_devices_list {
my ($vmid) = @_;
- my $res = vm_mon_cmd($vmid, 'query-pci');
+ my $res = mon_cmd($vmid, 'query-pci');
my $devices_to_check = [];
my $devices = {};
foreach my $pcibus (@$res) {
$devices_to_check = $to_check;
}
- my $resblock = vm_mon_cmd($vmid, 'query-block');
+ my $resblock = mon_cmd($vmid, 'query-block');
foreach my $block (@$resblock) {
if($block->{device} =~ m/^drive-(\S+)/){
$devices->{$1} = 1;
}
}
- my $resmice = vm_mon_cmd($vmid, 'query-mice');
+ my $resmice = mon_cmd($vmid, 'query-mice');
foreach my $mice (@$resmice) {
if ($mice->{name} eq 'QEMU HID Tablet') {
$devices->{tablet} = 1;
# for usb devices there is no query-usb
# but we can iterate over the entries in
# qom-list path=/machine/peripheral
- my $resperipheral = vm_mon_cmd($vmid, 'qom-list', path => '/machine/peripheral');
+ my $resperipheral = mon_cmd($vmid, 'qom-list', path => '/machine/peripheral');
foreach my $per (@$resperipheral) {
if ($per->{name} =~ m/^usb\d+$/) {
$devices->{$per->{name}} = 1;
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});
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);
$devicefull = "driver=".$devicefull;
my %options = split(/[=,]/, $devicefull);
- vm_mon_cmd($vmid, "device_add" , %options);
+ mon_cmd($vmid, "device_add" , %options);
}
sub qemu_devicedel {
my ($vmid, $deviceid) = @_;
- my $ret = vm_mon_cmd($vmid, "device_del", id => $deviceid);
+ my $ret = mon_cmd($vmid, "device_del", id => $deviceid);
}
sub qemu_iothread_add {
sub qemu_objectadd {
my($vmid, $objectid, $qomtype) = @_;
- vm_mon_cmd($vmid, "object-add", id => $objectid, "qom-type" => $qomtype);
+ mon_cmd($vmid, "object-add", id => $objectid, "qom-type" => $qomtype);
return 1;
}
sub qemu_objectdel {
my($vmid, $objectid) = @_;
- vm_mon_cmd($vmid, "object-del", id => $objectid);
+ mon_cmd($vmid, "object-del", id => $objectid);
return 1;
}
my $drive = print_drive_full($storecfg, $vmid, $device);
$drive =~ s/\\/\\\\/g;
- my $ret = vm_human_monitor_command($vmid, "drive_add auto \"$drive\"");
+ my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_add auto \"$drive\"");
# If the command succeeds qemu prints: "OK"
return 1 if $ret =~ m/OK/s;
sub qemu_drivedel {
my($vmid, $deviceid) = @_;
- my $ret = vm_human_monitor_command($vmid, "drive_del drive-$deviceid");
+ my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-$deviceid");
$ret =~ s/^\s+//;
return 1 if $ret eq "";
sub qemu_set_link_status {
my ($vmid, $device, $up) = @_;
- vm_mon_cmd($vmid, "set_link", name => $device,
+ mon_cmd($vmid, "set_link", name => $device,
up => $up ? JSON::true : JSON::false);
}
my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
my %options = split(/[=,]/, $netdev);
- vm_mon_cmd($vmid, "netdev_add", %options);
+ mon_cmd($vmid, "netdev_add", %options);
return 1;
}
sub qemu_netdevdel {
my ($vmid, $deviceid) = @_;
- vm_mon_cmd($vmid, "netdev_del", id => $deviceid);
+ mon_cmd($vmid, "netdev_del", id => $deviceid);
}
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
if ($vcpus < $currentvcpus) {
- if (qemu_machine_feature_enabled ($machine_type, undef, 2, 7)) {
+ if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
for (my $i = $currentvcpus; $i > $vcpus; $i--) {
qemu_devicedel($vmid, "cpu$i");
my $retry = 0;
my $currentrunningvcpus = undef;
while (1) {
- $currentrunningvcpus = vm_mon_cmd($vmid, "query-cpus");
+ $currentrunningvcpus = mon_cmd($vmid, "query-cpus");
last if scalar(@{$currentrunningvcpus}) == $i-1;
raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
$retry++;
return;
}
- my $currentrunningvcpus = vm_mon_cmd($vmid, "query-cpus");
+ my $currentrunningvcpus = mon_cmd($vmid, "query-cpus");
die "vcpus in running vm does not match its configuration\n"
if scalar(@{$currentrunningvcpus}) != $currentvcpus;
- if (qemu_machine_feature_enabled ($machine_type, undef, 2, 7)) {
+ if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
my $cpustr = print_cpu_device($conf, $i);
my $retry = 0;
my $currentrunningvcpus = undef;
while (1) {
- $currentrunningvcpus = vm_mon_cmd($vmid, "query-cpus");
+ $currentrunningvcpus = mon_cmd($vmid, "query-cpus");
last if scalar(@{$currentrunningvcpus}) == $i;
raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
sleep 1;
} else {
for (my $i = $currentvcpus; $i < $vcpus; $i++) {
- vm_mon_cmd($vmid, "cpu-add", id => int($i));
+ mon_cmd($vmid, "cpu-add", id => int($i));
}
}
}
return if !check_running($vmid) ;
- vm_mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
+ mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
bps => int($bps),
bps_rd => int($bps_rd),
bps_wr => int($bps_wr),
return if !$running;
- vm_mon_cmd($vmid, "block_resize", device => $deviceid, size => int($size));
+ mon_cmd($vmid, "block_resize", device => $deviceid, size => int($size));
}
my $running = check_running($vmid);
if ($running && do_snapshots_with_qemu($storecfg, $volid)){
- vm_mon_cmd($vmid, 'blockdev-snapshot-internal-sync', device => $deviceid, name => $snap);
+ mon_cmd($vmid, 'blockdev-snapshot-internal-sync', device => $deviceid, name => $snap);
} else {
PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
}
}
if ($running && do_snapshots_with_qemu($storecfg, $volid)){
- vm_mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync', device => $deviceid, name => $snap);
+ mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync', device => $deviceid, name => $snap);
} else {
PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
}
"compress" => 0
};
- my $supported_capabilities = vm_mon_cmd_nocheck($vmid, "query-migrate-capabilities");
+ my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
for my $supported_capability (@$supported_capabilities) {
push @$cap_ref, {
};
}
- vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
+ mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
}
my $fast_plug_option = {
'protection' => 1,
'vmstatestorage' => 1,
'hookscript' => 1,
+ 'tags' => 1,
};
# hotplug changes in [PENDING]
my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
my $defaults = load_defaults();
- my ($arch, $machine_type) = get_basic_machine_info($conf, undef);
+ my $arch = get_vm_arch($conf);
+ my $machine_type = get_vm_machine($conf, undef, $arch);
# commit values which do not have any impact on running VM first
# Note: those option cannot raise errors, we we do not care about
die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
# here we reset the ballooning value to memory
my $balloon = $conf->{memory} || $defaults->{memory};
- vm_mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
+ mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
} elsif ($fast_plug_option->{$opt}) {
# do nothing
} elsif ($opt =~ m/^net(\d+)$/) {
# allow manual ballooning if shares is set to zero
if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
- vm_mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
+ mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
}
} elsif ($opt =~ m/^net(\d+)$/) {
# some changes can be done without hotplug
vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
$vmid, $opt, $value, $arch, $machine_type);
} elsif (is_valid_drivename($opt)) {
+ die "skip\n" if $opt eq 'efidisk0';
# some changes can be done without hotplug
my $drive = parse_drive($opt, $value);
if (drive_is_cloudinit($drive)) {
} else { # cdrom
if ($drive->{file} eq 'none') {
- vm_mon_cmd($vmid, "eject",force => JSON::true,device => "drive-$opt");
+ mon_cmd($vmid, "eject",force => JSON::true,device => "drive-$opt");
if (drive_is_cloudinit($old_drive)) {
vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive);
}
} else {
my $path = get_iso_path($storecfg, $vmid, $drive->{file});
- vm_mon_cmd($vmid, "eject", force => JSON::true,device => "drive-$opt"); # force eject if locked
- vm_mon_cmd($vmid, "change", device => "drive-$opt",target => "$path") if $path;
+ mon_cmd($vmid, "eject", force => JSON::true,device => "drive-$opt"); # force eject if locked
+ mon_cmd($vmid, "change", device => "drive-$opt",target => "$path") if $path;
}
return 1;
my $newdrive = $drive;
$newdrive->{format} = $format;
$newdrive->{file} = $newvolid;
- my $drivestr = PVE::QemuServer::print_drive($vmid, $newdrive);
+ my $drivestr = print_drive($newdrive);
$local_volumes->{$opt} = $drivestr;
#pass drive to conf for command line
$conf->{$opt} = $drivestr;
if ($statefile eq 'tcp') {
my $localip = "localhost";
my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter.cfg');
- my $nodename = PVE::INotify::nodename();
+ my $nodename = nodename();
if (!defined($migration_type)) {
if (defined($datacenterconf->{migration}->{type})) {
foreach my $pcidevice (@$pcidevices) {
my $pciid = $pcidevice->{id};
- my $info = PVE::SysFSTools::pci_device_info("0000:$pciid");
+ my $info = PVE::SysFSTools::pci_device_info("$pciid");
die "IOMMU not present\n" if !PVE::SysFSTools::check_iommu_support();
die "no pci device info for device '$pciid'\n" if !$info;
print "migration listens on $migrate_uri\n" if $migrate_uri;
if ($statefile && $statefile ne 'tcp' && $statefile ne 'unix') {
- eval { vm_mon_cmd_nocheck($vmid, "cont"); };
+ eval { mon_cmd($vmid, "cont"); };
warn $@ if $@;
}
#start nbd server for storage migration
if ($targetstorage) {
- my $nodename = PVE::INotify::nodename();
+ my $nodename = nodename();
my $localip = $get_migration_ip->($migration_network, $nodename);
my $pfamily = PVE::Tools::get_host_address_family($nodename);
my $storage_migrate_port = PVE::Tools::next_migrate_port($pfamily);
- vm_mon_cmd_nocheck($vmid, "nbd-server-start", addr => { type => 'inet', data => { host => "${localip}", port => "${storage_migrate_port}" } } );
+ mon_cmd($vmid, "nbd-server-start", addr => { type => 'inet', data => { host => "${localip}", port => "${storage_migrate_port}" } } );
$localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
foreach my $opt (sort keys %$local_volumes) {
my $volid = $local_volumes->{$opt};
- vm_mon_cmd_nocheck($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
+ mon_cmd($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
my $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}:exportname=drive-$opt";
print "storage migration listens on $migrate_storage_uri volume:$volid\n";
}
if ($spice_port) {
print "spice listens on port $spice_port\n";
if ($spice_ticket) {
- vm_mon_cmd_nocheck($vmid, "set_password", protocol => 'spice', password => $spice_ticket);
- vm_mon_cmd_nocheck($vmid, "expire_password", protocol => 'spice', time => "+30");
+ mon_cmd($vmid, "set_password", protocol => 'spice', password => $spice_ticket);
+ mon_cmd($vmid, "expire_password", protocol => 'spice', time => "+30");
}
}
} else {
- vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024)
+ mon_cmd($vmid, "balloon", value => $conf->{balloon}*1024*1024)
if !$statefile && $conf->{balloon};
foreach my $opt (keys %$conf) {
}
}
- vm_mon_cmd_nocheck($vmid, 'qom-set',
+ mon_cmd($vmid, 'qom-set',
path => "machine/peripheral/balloon0",
property => "guest-stats-polling-interval",
value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
- if ($is_suspended && (my $vmstate = $conf->{vmstate})) {
+ if ($is_suspended) {
print "Resumed VM, removing state\n";
+ if (my $vmstate = $conf->{vmstate}) {
+ PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
+ PVE::Storage::vdisk_free($storecfg, $vmstate);
+ }
delete $conf->@{qw(lock vmstate runningmachine)};
- PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
- PVE::Storage::vdisk_free($storecfg, $vmstate);
PVE::QemuConfig->write_config($vmid, $conf);
}
});
}
-sub vm_mon_cmd {
- my ($vmid, $execute, %params) = @_;
-
- my $cmd = { execute => $execute, arguments => \%params };
- vm_qmp_command($vmid, $cmd);
-}
-
-sub vm_mon_cmd_nocheck {
- my ($vmid, $execute, %params) = @_;
-
- my $cmd = { execute => $execute, arguments => \%params };
- vm_qmp_command($vmid, $cmd, 1);
-}
-
-sub vm_qmp_command {
- my ($vmid, $cmd, $nocheck) = @_;
-
- my $res;
-
- my $timeout;
- if ($cmd->{arguments}) {
- $timeout = delete $cmd->{arguments}->{timeout};
- }
-
- eval {
- die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
- my $sname = PVE::QemuServer::Helpers::qmp_socket($vmid);
- if (-e $sname) { # test if VM is reasonambe new and supports qmp/qga
- my $qmpclient = PVE::QMPClient->new();
-
- $res = $qmpclient->cmd($vmid, $cmd, $timeout);
- } else {
- die "unable to open monitor socket\n";
- }
- };
- if (my $err = $@) {
- syslog("err", "VM $vmid qmp command failed - $err");
- die $err;
- }
-
- return $res;
-}
-
-sub vm_human_monitor_command {
- my ($vmid, $cmdline) = @_;
-
- my $cmd = {
- execute => 'human-monitor-command',
- arguments => { 'command-line' => $cmdline},
- };
-
- return vm_qmp_command($vmid, $cmd);
-}
-
sub vm_commandline {
my ($storecfg, $vmid, $snapname) = @_;
PVE::QemuConfig->check_lock($conf) if !$skiplock;
- vm_mon_cmd($vmid, "system_reset");
+ mon_cmd($vmid, "system_reset");
});
}
eval {
if ($shutdown) {
if (defined($conf) && parse_guest_agent($conf)->{enabled}) {
- vm_qmp_command($vmid, {
- execute => "guest-shutdown",
- arguments => { timeout => $timeout }
- }, $nocheck);
+ mon_cmd($vmid, "guest-shutdown", timeout => $timeout);
} else {
- vm_qmp_command($vmid, { execute => "system_powerdown" }, $nocheck);
+ mon_cmd($vmid, "system_powerdown");
}
} else {
- vm_qmp_command($vmid, { execute => "quit" }, $nocheck);
+ mon_cmd($vmid, "quit");
}
};
my $err = $@;
});
}
+# note: if using the statestorage parameter, the caller has to check privileges
sub vm_suspend {
my ($vmid, $skiplock, $includestate, $statestorage) = @_;
$conf->{lock} = 'suspending';
my $date = strftime("%Y-%m-%d", localtime(time()));
$storecfg = PVE::Storage::config();
+ if (!$statestorage) {
+ $statestorage = find_vmstate_storage($conf, $storecfg);
+ # check permissions for the storage
+ my $rpcenv = PVE::RPCEnvironment::get();
+ if ($rpcenv->{type} ne 'cli') {
+ my $authuser = $rpcenv->get_user();
+ $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
+ }
+ }
+
+
$vmstate = PVE::QemuConfig->__snapshot_save_vmstate($vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
$path = PVE::Storage::path($storecfg, $vmstate);
PVE::QemuConfig->write_config($vmid, $conf);
} else {
- vm_mon_cmd($vmid, "stop");
+ mon_cmd($vmid, "stop");
}
});
PVE::Storage::activate_volumes($storecfg, [$vmstate]);
eval {
- vm_mon_cmd($vmid, "savevm-start", statefile => $path);
+ mon_cmd($vmid, "savevm-start", statefile => $path);
for(;;) {
- my $state = vm_mon_cmd_nocheck($vmid, "query-savevm");
+ my $state = mon_cmd($vmid, "query-savevm");
if (!$state->{status}) {
die "savevm not active\n";
} elsif ($state->{status} eq 'active') {
if ($err) {
# cleanup, but leave suspending lock, to indicate something went wrong
eval {
- vm_mon_cmd($vmid, "savevm-end");
+ mon_cmd($vmid, "savevm-end");
PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
PVE::Storage::vdisk_free($storecfg, $vmstate);
delete $conf->@{qw(vmstate runningmachine)};
die "lock changed unexpectedly\n"
if !PVE::QemuConfig->has_lock($conf, 'suspending');
- vm_qmp_command($vmid, { execute => "quit" });
+ mon_cmd($vmid, "quit");
$conf->{lock} = 'suspended';
PVE::QemuConfig->write_config($vmid, $conf);
});
my ($vmid, $skiplock, $nocheck) = @_;
PVE::QemuConfig->lock_config($vmid, sub {
- my $vm_mon_cmd = $nocheck ? \&vm_mon_cmd_nocheck : \&vm_mon_cmd;
- my $res = $vm_mon_cmd->($vmid, 'query-status');
+ my $res = mon_cmd($vmid, 'query-status');
my $resume_cmd = 'cont';
if ($res->{status} && $res->{status} eq 'suspended') {
if !($skiplock || PVE::QemuConfig->has_lock($conf, 'backup'));
}
- $vm_mon_cmd->($vmid, $resume_cmd);
+ mon_cmd($vmid, $resume_cmd);
});
}
my $conf = PVE::QemuConfig->load_config($vmid);
# there is no qmp command, so we use the human monitor command
- my $res = vm_human_monitor_command($vmid, "sendkey $key");
+ my $res = PVE::QemuServer::Monitor::hmp_cmd($vmid, "sendkey $key");
die $res if $res ne '';
});
}
} elsif ($map->{$virtdev}) {
delete $di->{format}; # format can change on restore
$di->{file} = $map->{$virtdev};
- $value = print_drive($vmid, $di);
+ $value = print_drive($di);
print $outfd "$virtdev: $value\n";
} else {
print $outfd $line;
}
sub update_disksize {
+ my ($drive, $volid_hash) = @_;
+
+ my $volid = $drive->{file};
+ return undef if !defined($volid);
+
+ my $oldsize = $drive->{size};
+ my $newsize = $volid_hash->{$volid}->{size};
+
+ if (defined($newsize) && defined($oldsize) && $newsize != $oldsize) {
+ $drive->{size} = $newsize;
+
+ my $old_fmt = PVE::JSONSchema::format_size($oldsize);
+ my $new_fmt = PVE::JSONSchema::format_size($newsize);
+
+ return wantarray ? ($drive, $old_fmt, $new_fmt) : $drive;
+ }
+
+ return undef;
+}
+
+sub update_disk_config {
my ($vmid, $conf, $volid_hash) = @_;
my $changes;
my $volid = $drive->{file};
next if !$volid;
+ # mark volid as "in-use" for next step
$referenced->{$volid} = 1;
if ($volid_hash->{$volid} &&
(my $path = $volid_hash->{$volid}->{path})) {
next if drive_is_cdrom($drive);
next if !$volid_hash->{$volid};
- $drive->{size} = $volid_hash->{$volid}->{size};
- my $new = print_drive($vmid, $drive);
- if ($new ne $conf->{$opt}) {
+ my ($updated, $old_size, $new_size) = update_disksize($drive, $volid_hash);
+ if (defined($updated)) {
$changes = 1;
- $conf->{$opt} = $new;
- print "$prefix update disk '$opt' information.\n";
+ $conf->{$opt} = print_drive($updated);
+ print "$prefix size of disk '$volid' ($opt) updated from $old_size to $new_size\n";
}
}
}
my $volid = $conf->{$opt};
my $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid};
if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
- print "$prefix remove entry '$opt', its volume '$volid' is in use.\n";
+ print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
$changes = 1;
delete $conf->{$opt};
}
next if $referencedpath->{$path};
$changes = 1;
my $key = PVE::QemuConfig->add_unused_volume($conf, $volid);
- print "$prefix add unreferenced volume '$volid' as '$key' to config.\n";
+ print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
$referencedpath->{$path} = 1; # avoid to add more than once (aliases)
}
$vm_volids->{$volid} = $info if $info->{vmid} && $info->{vmid} == $vmid;
}
- my $changes = update_disksize($vmid, $conf, $vm_volids);
+ my $changes = update_disk_config($vmid, $conf, $vm_volids);
PVE::QemuConfig->write_config($vmid, $conf) if $changes && !$dryrun;
};
}
}
+my $qemu_snap_storage = {
+ rbd => 1,
+};
sub do_snapshots_with_qemu {
my ($storecfg, $volid) = @_;
sub qga_check_running {
my ($vmid, $nowarn) = @_;
- eval { vm_mon_cmd($vmid, "guest-ping", timeout => 3); };
+ eval { mon_cmd($vmid, "guest-ping", timeout => 3); };
if ($@) {
warn "Qemu Guest Agent is not running - $@" if !$nowarn;
return 0;
my $voliddst = PVE::Storage::vdisk_create_base($storecfg, $volid);
$drive->{file} = $voliddst;
- $conf->{$ds} = print_drive($vmid, $drive);
+ $conf->{$ds} = print_drive($drive);
PVE::QemuConfig->write_config($vmid, $conf);
});
}
my $cachemode;
my $src_path;
my $src_is_iscsi = 0;
- my $src_format = 'raw';
+ my $src_format;
if ($src_storeid) {
PVE::Storage::activate_volumes($storecfg, [$src_volid], $snapname);
my $cmd = [];
push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
- push @$cmd, '-l', "snapshot.name=$snapname" if($snapname && $src_format eq "qcow2");
+ push @$cmd, '-l', "snapshot.name=$snapname"
+ if $snapname && $src_format && $src_format eq "qcow2";
push @$cmd, '-t', 'none' if $dst_scfg->{type} eq 'zfspool';
push @$cmd, '-T', $cachemode if defined($cachemode);
if ($src_is_iscsi) {
push @$cmd, '--image-opts';
$src_path = convert_iscsi_path($src_path);
- } else {
+ } elsif ($src_format) {
push @$cmd, '-f', $src_format;
}
}
# if a job already runs for this device we get an error, catch it for cleanup
- eval { vm_mon_cmd($vmid, "drive-mirror", %$opts); };
+ eval { mon_cmd($vmid, "drive-mirror", %$opts); };
if (my $err = $@) {
eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
warn "$@\n" if $@;
while (1) {
die "storage migration timed out\n" if $err_complete > 300;
- my $stats = vm_mon_cmd($vmid, "query-block-jobs");
+ my $stats = mon_cmd($vmid, "query-block-jobs");
my $running_mirror_jobs = {};
foreach my $stat (@$stats) {
my $agent_running = $qga && qga_check_running($vmid);
if ($agent_running) {
print "freeze filesystem\n";
- eval { PVE::QemuServer::vm_mon_cmd($vmid, "guest-fsfreeze-freeze"); };
+ eval { mon_cmd($vmid, "guest-fsfreeze-freeze"); };
} else {
print "suspend vm\n";
eval { PVE::QemuServer::vm_suspend($vmid, 1); };
if ($agent_running) {
print "unfreeze filesystem\n";
- eval { PVE::QemuServer::vm_mon_cmd($vmid, "guest-fsfreeze-thaw"); };
+ eval { mon_cmd($vmid, "guest-fsfreeze-thaw"); };
} else {
print "resume vm\n";
eval { PVE::QemuServer::vm_resume($vmid, 1, 1); };
# try to switch the disk if source and destination are on the same guest
print "$job: Completing block job...\n";
- eval { vm_mon_cmd($vmid, "block-job-complete", device => $job) };
+ eval { mon_cmd($vmid, "block-job-complete", device => $job) };
if ($@ =~ m/cannot be completed/) {
print "$job: Block job cannot be completed, try again.\n";
$err_complete++;
foreach my $job (keys %$jobs) {
print "$job: Cancelling block job\n";
- eval { vm_mon_cmd($vmid, "block-job-cancel", device => $job); };
+ eval { mon_cmd($vmid, "block-job-cancel", device => $job); };
$jobs->{$job}->{cancel} = 1;
}
while (1) {
- my $stats = vm_mon_cmd($vmid, "query-block-jobs");
+ my $stats = mon_cmd($vmid, "query-block-jobs");
my $running_jobs = {};
foreach my $stat (@$stats) {
} else {
my $kvmver = get_running_qemu_version ($vmid);
- if (!qemu_machine_feature_enabled (undef, $kvmver, 2, 7)) {
+ if (!min_version($kvmver, 2, 7)) {
die "drive-mirror with iothread requires qemu version 2.7 or higher\n"
if $drive->{iothread};
}
return $disk;
}
-# this only works if VM is running
-sub get_current_qemu_machine {
- my ($vmid) = @_;
-
- my $cmd = { execute => 'query-machines', arguments => {} };
- my $res = vm_qmp_command($vmid, $cmd);
-
- 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 $cmd = { execute => 'query-version', arguments => {} };
- my $res = vm_qmp_command($vmid, $cmd);
+ 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 = vm_qmp_command($vmid, { execute => '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) = @_;
$machine_type = $1;
$use_old_bios_files = 1;
} else {
- my $kvmver = kvm_user_version();
+ my $version = PVE::QemuServer::Machine::extract_version($machine_type, kvm_user_version());
# Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
# load new efi bios files on migration. So this hack is required to allow
# live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
# updrading from proxmox-ve-3.X to proxmox-ve 4.0
- $use_old_bios_files = !qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 4);
+ $use_old_bios_files = !min_version($version, 2, 4);
}
return ($use_old_bios_files, $machine_type);
PVE::Storage::activate_volumes($storecfg, [$volid]);
qemu_img_convert($ovmf_vars, $volid, $vars_size_b, undef, 0);
+ my ($size) = PVE::Storage::volume_size_info($storecfg, $volid, 3);
- return ($volid, $vars_size);
+ return ($volid, $size/1024);
}
sub vm_iothreads_list {
my ($vmid) = @_;
- my $res = vm_mon_cmd($vmid, 'query-iothreads');
+ my $res = mon_cmd($vmid, 'query-iothreads');
my $iothreads = {};
foreach my $iothread (@$res) {
}
sub add_hyperv_enlightenments {
- my ($cpuFlags, $winversion, $machine_type, $kvmver, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
+ my ($cpuFlags, $winversion, $machine_version, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
return if $winversion < 6;
return if $bios && $bios eq 'ovmf' && $winversion < 8;
push @$cpuFlags , "hv_vendor_id=$hv_vendor_id";
}
- if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
+ if (min_version($machine_version, 2, 3)) {
push @$cpuFlags , 'hv_spinlocks=0x1fff';
push @$cpuFlags , 'hv_vapic';
push @$cpuFlags , 'hv_time';
push @$cpuFlags , 'hv_spinlocks=0xffff';
}
- if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 6)) {
+ if (min_version($machine_version, 2, 6)) {
push @$cpuFlags , 'hv_reset';
push @$cpuFlags , 'hv_vpindex';
push @$cpuFlags , 'hv_runtime';
if ($winversion >= 7) {
push @$cpuFlags , 'hv_relaxed';
- if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 12)) {
+ if (min_version($machine_version, 2, 12)) {
push @$cpuFlags , 'hv_synic';
push @$cpuFlags , 'hv_stimer';
}
- if (qemu_machine_feature_enabled ($machine_type, $kvmver, 3, 1)) {
+ if (min_version($machine_version, 3, 1)) {
push @$cpuFlags , 'hv_ipi';
}
}
return $firstdisk;
}
+# NOTE: if this logic changes, please update docs & possibly gui logic
+sub find_vmstate_storage {
+ my ($conf, $storecfg) = @_;
+
+ # first, return storage from conf if set
+ return $conf->{vmstatestorage} if $conf->{vmstatestorage};
+
+ my ($target, $shared, $local);
+
+ foreach_storage_used_by_vm($conf, sub {
+ my ($sid) = @_;
+ my $scfg = PVE::Storage::storage_config($storecfg, $sid);
+ my $dst = $scfg->{shared} ? \$shared : \$local;
+ $$dst = $sid if !$$dst || $scfg->{path}; # prefer file based storage
+ });
+
+ # second, use shared storage where VM has at least one disk
+ # third, use local storage where VM has at least one disk
+ # fall back to local storage
+ $target = $shared // $local // 'local';
+
+ return $target;
+}
+
sub generate_uuid {
my ($uuid, $uuid_str);
UUID::generate($uuid);
sub nbd_stop {
my ($vmid) = @_;
- vm_mon_cmd($vmid, 'nbd-server-stop');
+ mon_cmd($vmid, 'nbd-server-stop');
}
sub create_reboot_request {