]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
Import OVF: Lock config with "lock" property
[qemu-server.git] / PVE / QemuServer.pm
index 3c5e6f7c86c39362abac82edb7b1bcd348d1b0ff..bfe6662dd18d57cdcc70486ebc59acfc89575f93 100644 (file)
@@ -36,7 +36,7 @@ use PVE::SafeSyslog;
 use PVE::Storage;
 use PVE::SysFSTools;
 use PVE::Systemd;
-use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach $IPV6RE);
+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;
@@ -91,7 +91,7 @@ PVE::JSONSchema::register_standard_option('pve-qm-image-format', {
 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,
 });
@@ -1898,7 +1898,9 @@ sub print_drivedevice_full {
                 $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';
            }
        }
@@ -2350,40 +2352,6 @@ sub vm_is_volid_owner {
     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) = @_;
 
@@ -2398,37 +2366,6 @@ sub vmconfig_register_unused_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 => {
@@ -2611,13 +2548,6 @@ sub check_type {
     }
 }
 
-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) = @_;
 
@@ -2662,12 +2592,6 @@ sub destroy_vm {
 
     });
 
-    if ($keep_empty_config) {
-       PVE::QemuConfig->write_config($vmid, { memory => 128 });
-    } else {
-       PVE::QemuConfig->destroy_config($vmid);
-    }
-
     # also remove unused disk
     eval {
        my $dl = PVE::Storage::vdisk_list($storecfg, undef, $vmid);
@@ -2682,6 +2606,12 @@ sub destroy_vm {
 
     };
     warn $@ if $@;
+
+    if ($keep_empty_config) {
+       PVE::QemuConfig->write_config($vmid, { memory => 128 });
+    } else {
+       PVE::QemuConfig->destroy_config($vmid);
+    }
 }
 
 sub parse_vm_config {
@@ -3202,8 +3132,7 @@ sub vmstatus {
     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};
@@ -3488,12 +3417,6 @@ sub vga_conf_has_spice {
     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;
@@ -4183,7 +4106,7 @@ sub config_to_command {
 
     if (my $vmstate = $conf->{vmstate}) {
        my $statepath = PVE::Storage::path($storecfg, $vmstate);
-       push @$vollist, $statepath;
+       push @$vollist, $vmstate;
        push @$cmd, '-loadstate', $statepath;
     }
 
@@ -4210,10 +4133,9 @@ sub spice_port {
 }
 
 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 {
@@ -4920,9 +4842,10 @@ sub vmconfig_hotplug_pending {
 
     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/);
@@ -4976,7 +4899,7 @@ sub vmconfig_hotplug_pending {
        } 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
        }
@@ -5111,25 +5034,28 @@ sub vmconfig_delete_or_detach_drive {
     }
 }
 
+
+
 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);
        }
@@ -5466,7 +5392,7 @@ sub vm_start {
                push @$cmd, '-loadstate', $statefile;
            } else {
                my $statepath = PVE::Storage::path($storecfg, $statefile);
-               push @$vollist, $statepath;
+               push @$vollist, $statefile;
                push @$cmd, '-loadstate', $statepath;
            }
        } elsif ($paused) {
@@ -6011,21 +5937,6 @@ sub vm_sendkey {
     });
 }
 
-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 {
@@ -7239,9 +7150,41 @@ sub qemu_machine_feature_enabled {
        $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 {