use strict;
use warnings;
-use POSIX;
-use IO::Handle;
-use IO::Select;
-use IO::File;
-use IO::Dir;
-use IO::Socket::UNIX;
+use Cwd 'abs_path';
+use Digest::SHA;
+use Fcntl ':flock';
+use Fcntl;
use File::Basename;
+use File::Copy qw(copy);
use File::Path;
use File::stat;
use Getopt::Long;
-use Digest::SHA;
-use Fcntl ':flock';
-use Cwd 'abs_path';
+use IO::Dir;
+use IO::File;
+use IO::Handle;
+use IO::Select;
+use IO::Socket::UNIX;
use IPC::Open3;
use JSON;
-use Fcntl;
-use PVE::SafeSyslog;
-use Storable qw(dclone);
use MIME::Base64;
-use PVE::Exception qw(raise raise_param_exc);
-use PVE::Storage;
-use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach $IPV6RE);
-use PVE::JSONSchema qw(get_standard_option);
+use POSIX;
+use Storable qw(dclone);
+use Time::HiRes qw(gettimeofday);
+use URI::Escape;
+
use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Exception qw(raise raise_param_exc);
+use PVE::GuestHelpers;
use PVE::INotify;
+use PVE::JSONSchema qw(get_standard_option);
use PVE::ProcFSTools;
-use PVE::QemuConfig;
-use PVE::QMPClient;
use PVE::RPCEnvironment;
-use PVE::GuestHelpers;
-use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port);
-use PVE::QemuServer::Memory;
-use PVE::QemuServer::USB qw(parse_usb_device);
-use PVE::QemuServer::Cloudinit;
+use PVE::SafeSyslog;
+use PVE::Storage;
use PVE::SysFSTools;
use PVE::Systemd;
-use Time::HiRes qw(gettimeofday);
-use File::Copy qw(copy);
-use URI::Escape;
+use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach get_host_arch $IPV6RE);
+
+use PVE::QMPClient;
+use PVE::QemuConfig;
+use PVE::QemuServer::Cloudinit;
+use PVE::QemuServer::Memory;
+use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port);
+use PVE::QemuServer::USB qw(parse_usb_device);
my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
my $OVMF = {
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+)+(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\.pxe)?|virt(?:-\d+(\.\d+)+)?)',
maxLength => 40,
optional => 1,
});
$path = PVE::Storage::path($storecfg, $drive->{file});
}
- if($path =~ m/^iscsi\:\/\//){
+ # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
+ if ($path =~ m/^iscsi\:\/\// &&
+ !qemu_machine_feature_enabled($machine_type, undef, 4, 1)) {
$devicetype = 'generic';
}
}
return undef;
}
-sub split_flagged_list {
- my $text = shift || '';
- $text =~ s/[,;]/ /g;
- $text =~ s/^\s+//;
- return { map { /^(!?)(.*)$/ && ($2, $1) } ($text =~ /\S+/g) };
-}
-
-sub join_flagged_list {
- my ($how, $lst) = @_;
- join $how, map { $lst->{$_} . $_ } keys %$lst;
-}
-
-sub vmconfig_delete_pending_option {
- my ($conf, $key, $force) = @_;
-
- delete $conf->{pending}->{$key};
- my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
- $pending_delete_hash->{$key} = $force ? '!' : '';
- $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash);
-}
-
-sub vmconfig_undelete_pending_option {
- my ($conf, $key) = @_;
-
- my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
- delete $pending_delete_hash->{$key};
-
- if (%$pending_delete_hash) {
- $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash);
- } else {
- delete $conf->{pending}->{delete};
- }
-}
-
sub vmconfig_register_unused_drive {
my ($storecfg, $vmid, $conf, $drive) = @_;
}
}
-sub vmconfig_cleanup_pending {
- my ($conf) = @_;
-
- # remove pending changes when nothing changed
- my $changes;
- foreach my $opt (keys %{$conf->{pending}}) {
- if (defined($conf->{$opt}) && ($conf->{pending}->{$opt} eq $conf->{$opt})) {
- $changes = 1;
- delete $conf->{pending}->{$opt};
- }
- }
-
- my $current_delete_hash = split_flagged_list($conf->{pending}->{delete});
- my $pending_delete_hash = {};
- while (my ($opt, $force) = each %$current_delete_hash) {
- if (defined($conf->{$opt})) {
- $pending_delete_hash->{$opt} = $force;
- } else {
- $changes = 1;
- }
- }
-
- if (%$pending_delete_hash) {
- $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash);
- } else {
- delete $conf->{pending}->{delete};
- }
-
- return $changes;
-}
-
# smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
my $smbios1_fmt = {
uuid => {
}
}
-sub touch_config {
- my ($vmid) = @_;
-
- my $conf = PVE::QemuConfig->config_file($vmid);
- utime undef, undef, $conf;
-}
-
sub destroy_vm {
my ($storecfg, $vmid, $keep_empty_config, $skiplock) = @_;
- my $conffile = PVE::QemuConfig->config_file($vmid);
-
my $conf = PVE::QemuConfig->load_config($vmid);
PVE::QemuConfig->check_lock($conf) if !$skiplock;
});
- if ($keep_empty_config) {
- PVE::Tools::file_set_contents($conffile, "memory: 128\n");
- } else {
- PVE::QemuConfig->destroy_config($vmid);
- }
-
# also remove unused disk
eval {
my $dl = PVE::Storage::vdisk_list($storecfg, undef, $vmid);
};
warn $@ if $@;
+
+ if ($keep_empty_config) {
+ PVE::QemuConfig->write_config($vmid, { memory => 128 });
+ } else {
+ PVE::QemuConfig->destroy_config($vmid);
+ }
}
sub parse_vm_config {
foreach my $vmid (keys %$list) {
next if $opt_vmid && ($vmid ne $opt_vmid);
- my $cfspath = PVE::QemuConfig->cfs_config_path($vmid);
- my $conf = PVE::Cluster::cfs_read_file($cfspath) || {};
+ my $conf = PVE::QemuConfig->load_config($vmid);
my $d = { vmid => $vmid };
$d->{pid} = $list->{$vmid}->{pid};
return $1 || 1;
}
-my $host_arch; # FIXME: fix PVE::Tools::get_host_arch
-sub get_host_arch() {
- $host_arch = (POSIX::uname())[4] if !$host_arch;
- return $host_arch;
-}
-
sub is_native($) {
my ($arch) = @_;
return get_host_arch() eq $arch;
if (my $vmstate = $conf->{vmstate}) {
my $statepath = PVE::Storage::path($storecfg, $vmstate);
- push @$vollist, $statepath;
+ push @$vollist, $vmstate;
push @$cmd, '-loadstate', $statepath;
}
}
sub qmp_socket {
- my ($vmid, $qga, $name) = @_;
+ my ($vmid, $qga) = @_;
my $sockettype = $qga ? 'qga' : 'qmp';
- my $ext = $name ? '-'.$name : '';
- return "${var_run_tmpdir}/$vmid$ext.$sockettype";
+ return "${var_run_tmpdir}/$vmid.$sockettype";
}
sub pidfile_name {
my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
- my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
- while (my ($opt, $force) = each %$pending_delete_hash) {
+ my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
+ foreach my $opt (sort keys %$pending_delete_hash) {
next if $selection && !$selection->{$opt};
+ my $force = $pending_delete_hash->{$opt}->{force};
eval {
if ($opt eq 'hotplug') {
die "skip\n" if ($conf->{hotplug} =~ /memory/);
} else {
# save new config if hotplug was successful
delete $conf->{$opt};
- vmconfig_undelete_pending_option($conf, $opt);
+ PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
PVE::QemuConfig->write_config($vmid, $conf);
$conf = PVE::QemuConfig->load_config($vmid); # update/reload
}
}
}
+
+
sub vmconfig_apply_pending {
my ($vmid, $conf, $storecfg) = @_;
# cold plug
- my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
- while (my ($opt, $force) = each %$pending_delete_hash) {
+ my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
+ foreach my $opt (sort keys %$pending_delete_hash) {
die "internal error" if $opt =~ m/^unused/;
+ my $force = $pending_delete_hash->{$opt}->{force};
$conf = PVE::QemuConfig->load_config($vmid); # update/reload
if (!defined($conf->{$opt})) {
- vmconfig_undelete_pending_option($conf, $opt);
+ PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
PVE::QemuConfig->write_config($vmid, $conf);
} elsif (is_valid_drivename($opt)) {
vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
- vmconfig_undelete_pending_option($conf, $opt);
+ PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
delete $conf->{$opt};
PVE::QemuConfig->write_config($vmid, $conf);
} else {
- vmconfig_undelete_pending_option($conf, $opt);
+ PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
delete $conf->{$opt};
PVE::QemuConfig->write_config($vmid, $conf);
}
push @$cmd, '-loadstate', $statefile;
} else {
my $statepath = PVE::Storage::path($storecfg, $statefile);
- push @$vollist, $statepath;
+ push @$vollist, $statefile;
push @$cmd, '-loadstate', $statepath;
}
} elsif ($paused) {
});
}
-sub vm_destroy {
- my ($storecfg, $vmid, $skiplock) = @_;
-
- PVE::QemuConfig->lock_config($vmid, sub {
-
- my $conf = PVE::QemuConfig->load_config($vmid);
-
- if (!check_running($vmid)) {
- destroy_vm($storecfg, $vmid, undef, $skiplock);
- } else {
- die "VM $vmid is running - destroy failed\n";
- }
- });
-}
-
# vzdump restore implementaion
sub tar_archive_read_firstfile {
$current_minor = $2;
}
- return 1 if $current_major > $version_major ||
- ($current_major == $version_major &&
- $current_minor >= $version_minor);
+ 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 {