]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
bump version to 3.1-1
[qemu-server.git] / PVE / QemuServer.pm
index 2a5c13c0abeb91f3baa23899d5cba280b735c742..3c9c54dc7125e38c535848019f29f1efeb9cdf25 100644 (file)
@@ -335,8 +335,8 @@ EODESC
     vga => {
        optional => 1,
        type => 'string',
-       description => "Select VGA type. If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and 'cirrur' for other OS types",
-       enum => [qw(std cirrus vmware)],
+       description => "Select VGA type. If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and 'cirrur' for other OS types. Option 'qxl' enables the SPICE display sever. You can also run without any graphic card using a serial devive as terminal.",
+       enum => [qw(std cirrus vmware qxl serial0 serial1 serial2 serial3)],
     },
     watchdog => {
        optional => 1,
@@ -377,7 +377,7 @@ EODESCR
        optional => 1,
        type => 'boolean',
        default => 1,
-       description => "Enable/disable the usb tablet device. This device is usually needed to allow absolute mouse positioning. Else the mouse runs out of sync with normal vnc clients. If you're running lots of console-only guests on one host, you may consider disabling this to save some context switches.",
+       description => "Enable/disable the usb tablet device. This device is usually needed to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with normal VNC clients. If you're running lots of console-only guests on one host, you may consider disabling this to save some context switches. This is turned of by default if you use spice (vga=qxl).",
     },
     migrate_speed => {
        optional => 1,
@@ -421,6 +421,13 @@ EODESCR
        type => 'string', format => 'pve-volume-id',
        description => "Reference to a volume which stores the VM state. This is used internally for snapshots.",
     },
+    machine => {
+       description => "Specific the Qemu machine type.",
+       type => 'string',
+       pattern => '(pc|pc(-i440fx)?-\d+\.\d+|q35|pc-q35-\d+\.\d+)',
+       maxLength => 40,
+       optional => 1,
+    },
 };
 
 # what about other qemu settings ?
@@ -528,7 +535,7 @@ PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
 my $usbdesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-usb-device',
-    typetext => 'host=HOSTUSBDEVICE',
+    typetext => 'host=HOSTUSBDEVICE|spice',
     description => <<EODESCR,
 Configure an USB device (n is 0 to 4). This can be used to
 pass-through usb devices to the guest. HOSTUSBDEVICE syntax is:
@@ -540,6 +547,8 @@ You can use the 'lsusb -t' command to list existing usb devices.
 
 Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
 
+The value 'spice' can be used to add a usb redirection devices for spice.
+
 EODESCR
 };
 PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
@@ -565,9 +574,9 @@ PVE::JSONSchema::register_standard_option("pve-qm-hostpci", $hostpcidesc);
 my $serialdesc = {
        optional => 1,
        type => 'string',
-       pattern => '/dev/ttyS\d+',
+       pattern => '(/dev/ttyS\d+|socket)',
        description =>  <<EODESCR,
-Map host serial devices (n is 0 to 3).
+Create a serial device inside the VM (n is 0 to 3), and pass through a host serial device, or create a unix socket on the host side (use 'qm terminal' to open a terminal connection).
 
 Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
 
@@ -578,7 +587,7 @@ EODESCR
 my $paralleldesc= {
        optional => 1,
        type => 'string',
-       pattern => '/dev/parport\d+',
+        pattern => '/dev/parport\d+|/dev/usb/lp\d+',
        description =>  <<EODESCR,
 Map host parallel devices (n is 0 to 2).
 
@@ -918,7 +927,7 @@ sub parse_drive {
     return undef if $res->{backup} && $res->{backup} !~ m/^(yes|no)$/;
     return undef if $res->{aio} && $res->{aio} !~ m/^(native|threads)$/;
 
-    
+
     return undef if $res->{mbps_rd} && $res->{mbps};
     return undef if $res->{mbps_wr} && $res->{mbps};
 
@@ -934,7 +943,7 @@ sub parse_drive {
 
 
     if ($res->{size}) {
-       return undef if !defined($res->{size} = &$parse_size($res->{size})); 
+       return undef if !defined($res->{size} = &$parse_size($res->{size}));
     }
 
     if ($res->{media} && ($res->{media} eq 'cdrom')) {
@@ -988,7 +997,7 @@ sub scsi_inquiry {
 
     my $buf = "\x00" x 36;
     my $sensebuf = "\x00" x 8;
-    my $cmd = pack("C x3 C x11", 0x12, 36);
+    my $cmd = pack("C x3 C x1", 0x12, 36);
 
     # see /usr/include/scsi/sg.h
     my $sg_io_hdr_t = "i i C C s I P P P I I i P C C C C S S i I I";
@@ -1010,9 +1019,12 @@ sub scsi_inquiry {
     }
 
     my $res = {};
-    ($res->{device}, $res->{removable}, $res->{venodor},
+    (my $byte0, my $byte1, $res->{vendor},
      $res->{product}, $res->{revision}) = unpack("C C x6 A8 A16 A4", $buf);
 
+    $res->{removable} = $byte1 & 128 ? 1 : 0;
+    $res->{type} = $byte0 & 31;
+
     return $res;
 }
 
@@ -1043,7 +1055,7 @@ sub print_drivedevice_full {
         my $path = '';
         if (drive_is_cdrom($drive)) {
               $devicetype = 'cd';
-          } else {
+       } else {
               if ($drive->{file} =~ m|^/|) {
                   $path = $drive->{file};
               } else {
@@ -1051,10 +1063,15 @@ sub print_drivedevice_full {
               }
 
              if($path =~ m/^iscsi\:\/\//){
-                $devicetype = 'generic';
-             }
-             else {
-                $devicetype = 'block' if path_is_scsi($path);
+                 $devicetype = 'generic';
+             } else {
+                 if (my $info = path_is_scsi($path)) {
+                     if ($info->{type} == 0) {
+                         $devicetype = 'block';
+                     } elsif ($info->{type} == 1) { # tape
+                         $devicetype = 'generic';
+                     }
+                 }
              }
          }
 
@@ -1396,6 +1413,9 @@ sub parse_usb_device {
            $found = 1;
            $res->{hostbus} = $1;
            $res->{hostport} = $2;
+       } elsif ($v =~ m/^spice$/) {
+           $found = 1;
+           $res->{spice} = 1;
        } else {
            return undef;
        }
@@ -1617,12 +1637,12 @@ sub parse_vm_config {
     my @lines = split(/\n/, $raw);
     foreach my $line (@lines) {
        next if $line =~ m/^\s*$/;
-       
+
        if ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
            my $snapname = $1;
            $conf->{description} = $descr if $descr;
            $descr = '';
-           $conf = $res->{snapshots}->{$snapname} = {}; 
+           $conf = $res->{snapshots}->{$snapname} = {};
            next;
        }
 
@@ -1697,7 +1717,7 @@ sub write_vm_config {
     my $used_volids = {};
 
     my $cleanup_config = sub {
-       my ($cref) = @_;
+       my ($cref, $snapname) = @_;
 
        foreach my $key (keys %$cref) {
            next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
@@ -1708,7 +1728,7 @@ sub write_vm_config {
 
            $cref->{$key} = $value;
 
-           if (valid_drivename($key)) {
+           if (!$snapname && valid_drivename($key)) {
                my $drive = parse_drive($key, $value);
                $used_volids->{$drive->{file}} = 1 if $drive && $drive->{file};
            }
@@ -1717,7 +1737,7 @@ sub write_vm_config {
 
     &$cleanup_config($conf);
     foreach my $snapname (keys %{$conf->{snapshots}}) {
-       &$cleanup_config($conf->{snapshots}->{$snapname});
+       &$cleanup_config($conf->{snapshots}->{$snapname}, $snapname);
     }
 
     # remove 'unusedX' settings if we re-add a volume
@@ -1727,7 +1747,7 @@ sub write_vm_config {
            delete $conf->{$key};
        }
     }
-  
+
     my $generate_raw_config = sub {
        my ($conf) = @_;
 
@@ -1821,7 +1841,7 @@ sub check_local_resources {
     return $loc_res;
 }
 
-# check is used storages are available on all nodes (use by migrate)
+# check if used storages are available on all nodes (use by migrate)
 sub check_storage_availability {
     my ($storecfg, $conf, $node) = @_;
 
@@ -1840,6 +1860,40 @@ sub check_storage_availability {
    });
 }
 
+# list nodes where all VM images are available (used by has_feature API)
+sub shared_nodes {
+    my ($conf, $storecfg) = @_;
+
+    my $nodelist = PVE::Cluster::get_nodelist();
+    my $nodehash = { map { $_ => 1 } @$nodelist };
+    my $nodename = PVE::INotify::nodename();
+
+    foreach_drive($conf, sub {
+       my ($ds, $drive) = @_;
+
+       my $volid = $drive->{file};
+       return if !$volid;
+
+       my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
+       if ($storeid) {
+           my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+           if ($scfg->{disable}) {
+               $nodehash = {};
+           } elsif (my $avail = $scfg->{nodes}) {
+               foreach my $node (keys %$nodehash) {
+                   delete $nodehash->{$node} if !$avail->{$node};
+               }
+           } elsif (!$scfg->{shared}) {
+               foreach my $node (keys %$nodehash) {
+                   delete $nodehash->{$node} if $node ne $nodename
+               }
+           }
+       }
+    });
+
+    return $nodehash
+}
+
 sub check_lock {
     my ($conf) = @_;
 
@@ -2072,13 +2126,13 @@ sub vmstatus {
 
        my $info = $resp->{'return'};
        return if !$info->{max_mem};
-       
+
        my $d = $res->{$vmid};
 
        # use memory assigned to VM
        $d->{maxmem} = $info->{max_mem};
        $d->{balloon} = $info->{actual};
-       
+
        if (defined($info->{total_mem}) && defined($info->{free_mem})) {
            $d->{mem} = $info->{total_mem} - $info->{free_mem};
            $d->{freemem} = $info->{free_mem};
@@ -2147,14 +2201,14 @@ sub foreach_drive {
 
 sub foreach_volid {
     my ($conf, $func) = @_;
-    
+
     my $volhash = {};
 
     my $test_volid = sub {
        my ($volid, $is_cdrom) = @_;
 
        return if !$volid;
-       
+
        $volhash->{$volid} = $is_cdrom || 0;
     };
 
@@ -2173,17 +2227,24 @@ sub foreach_volid {
     }
 
     foreach my $volid (keys %$volhash) {
-       &$func($volid, $volhash->{$volid});     
+       &$func($volid, $volhash->{$volid});
     }
 }
 
+sub vga_conf_has_spice {
+    my ($vga) = @_;
+
+    return $vga && ($vga eq 'qxl');
+}
+
 sub config_to_command {
-    my ($storecfg, $vmid, $conf, $defaults) = @_;
+    my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_;
 
     my $cmd = [];
     my $globalFlags = [];
     my $machineFlags = [];
     my $rtcFlags = [];
+    my $cpuFlags = [];
     my $devices = [];
     my $pciaddr = '';
     my $bridges = {};
@@ -2227,8 +2288,27 @@ sub config_to_command {
     # include usb device config
     push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
 
+    my $vga = $conf->{vga};
+    if (!$vga) {
+       if ($conf->{ostype} && ($conf->{ostype} eq 'win8' || 
+                               $conf->{ostype} eq 'win7' || 
+                               $conf->{ostype} eq 'w2k8')) {
+           $vga = 'std';
+       } else {
+           $vga = 'cirrus';
+       }
+    }
+
     # enable absolute mouse coordinates (needed by vnc)
-    my $tablet = defined($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
+    my $tablet;
+    if (defined($conf->{tablet})) {
+       $tablet = $conf->{tablet};
+    } else {
+       $tablet = $defaults->{tablet};
+       $tablet = 0 if vga_conf_has_spice($vga); # disable for spice because it is not needed
+       $tablet = 0 if $vga =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
+    }
+
     push @$devices, '-device', 'usb-tablet,id=tablet,bus=uhci.0,port=1' if $tablet;
 
     # host pci devices
@@ -2247,15 +2327,25 @@ sub config_to_command {
            push @$devices, '-device', "usb-host,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
        } elsif (defined($d->{hostbus}) && defined($d->{hostport})) {
            push @$devices, '-device', "usb-host,hostbus=$d->{hostbus},hostport=$d->{hostport}";
+       } elsif ($d->{spice}) {
+           # usb redir support for spice
+           push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir";
+           push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=ehci.0";
        }
     }
 
     # serial devices
     for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++)  {
        if (my $path = $conf->{"serial$i"}) {
-           die "no such serial device\n" if ! -c $path;
-           push @$devices, '-chardev', "tty,id=serial$i,path=$path";
-           push @$devices, '-device', "isa-serial,chardev=serial$i";
+           if ($path eq 'socket') {
+               my $socket = "/var/run/qemu-server/${vmid}.serial$i";
+               push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server,nowait";
+               push @$devices, '-device', "isa-serial,chardev=serial$i";
+           } else {
+               die "no such serial device\n" if ! -c $path;
+               push @$devices, '-chardev', "tty,id=serial$i,path=$path";
+               push @$devices, '-device', "isa-serial,chardev=serial$i";
+           }
        }
     }
 
@@ -2277,11 +2367,8 @@ sub config_to_command {
     $sockets = $conf->{sockets} if  $conf->{sockets};
 
     my $cores = $conf->{cores} || 1;
-
     push @$cmd, '-smp', "sockets=$sockets,cores=$cores";
 
-    push @$cmd, '-cpu', $conf->{cpu} if $conf->{cpu};
-
     push @$cmd, '-nodefaults';
 
     my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
@@ -2299,16 +2386,7 @@ sub config_to_command {
 
     push @$cmd, '-no-reboot' if  defined($conf->{reboot}) && $conf->{reboot} == 0;
 
-    my $vga = $conf->{vga};
-    if (!$vga) {
-       if ($conf->{ostype} && ($conf->{ostype} eq 'win8' || $conf->{ostype} eq 'win7' || $conf->{ostype} eq 'w2k8')) {
-           $vga = 'std';
-       } else {
-           $vga = 'cirrus';
-       }
-    }
-
-    push @$cmd, '-vga', $vga if $vga; # for kvm 77 and later
+    push @$cmd, '-vga', $vga if $vga && $vga !~ m/^serial\d+$/; # for kvm 77 and later
 
     # time drift fix
     my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
@@ -2328,10 +2406,16 @@ sub config_to_command {
            }
        }
 
-       if ($ost eq 'win7' || $ost eq 'win8' || $ost eq 'w2k8' || 
+       if ($ost eq 'win7' || $ost eq 'win8' || $ost eq 'w2k8' ||
            $ost eq 'wvista') {
            push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
            push @$cmd, '-no-hpet';
+           #push @$cpuFlags , 'hv_vapic" if !$nokvm;  #fixme, my win2008R2 hang at boot with this
+           push @$cpuFlags , 'hv_spinlocks=0xffff' if !$nokvm;
+       }
+
+       if ($ost eq 'win7' || $ost eq 'win8') {
+           push @$cpuFlags , 'hv_relaxed' if !$nokvm;
        }
     }
 
@@ -2343,12 +2427,28 @@ sub config_to_command {
        die "No accelerator found!\n" if !$cpuinfo->{hvm};
     }
 
+    my $machine_type = $forcemachine || $conf->{machine};
+    if ($machine_type) {
+       push @$machineFlags, "type=${machine_type}";
+    }
+
     if ($conf->{startdate}) {
        push @$rtcFlags, "base=$conf->{startdate}";
     } elsif ($useLocaltime) {
        push @$rtcFlags, 'base=localtime';
     }
 
+    my $cpu = $nokvm ? "qemu64" : "kvm64";
+    $cpu = $conf->{cpu} if $conf->{cpu};
+
+    push @$cpuFlags , '+x2apic' if !$nokvm;
+
+    push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
+
+    $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
+
+    push @$cmd, '-cpu', $cpu;
+
     push @$cmd, '-S' if $conf->{freeze};
 
     # set keyboard layout
@@ -2368,6 +2468,19 @@ sub config_to_command {
        push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
     }
 
+    my $spice_port;
+    if (vga_conf_has_spice($vga)) {
+       my $pciaddr = print_pci_addr("spice", $bridges);
+
+       $spice_port = PVE::Tools::next_unused_port(61000, 61099);
+
+       push @$cmd, '-spice', "tls-port=${spice_port},addr=127.0.0.1,tls-ciphers=DES-CBC3-SHA,seamless-migration=on";
+
+       push @$cmd, '-device', "virtio-serial,id=spice$pciaddr";
+       push @$cmd, '-chardev', "spicevmc,id=vdagent,name=vdagent";
+       push @$cmd, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
+    }
+
     # enable balloon by default, unless explicitly disabled
     if (!defined($conf->{balloon}) || $conf->{balloon}) {
        $pciaddr = print_pci_addr("balloon0", $bridges);
@@ -2476,14 +2589,14 @@ sub config_to_command {
     }
 
     push @$cmd, @$devices;
-    push @$cmd, '-rtc', join(',', @$rtcFlags) 
+    push @$cmd, '-rtc', join(',', @$rtcFlags)
        if scalar(@$rtcFlags);
-    push @$cmd, '-machine', join(',', @$machineFlags) 
+    push @$cmd, '-machine', join(',', @$machineFlags)
        if scalar(@$machineFlags);
     push @$cmd, '-global', join(',', @$globalFlags)
        if scalar(@$globalFlags);
 
-    return wantarray ? ($cmd, $vollist) : $cmd;
+    return wantarray ? ($cmd, $vollist, $spice_port) : $cmd;
 }
 
 sub vnc_socket {
@@ -2491,6 +2604,14 @@ sub vnc_socket {
     return "${var_run_tmpdir}/$vmid.vnc";
 }
 
+sub spice_port {
+    my ($vmid) = @_;
+
+    my $res = vm_mon_cmd($vmid, 'query-spice');
+
+    return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
+}
+
 sub qmp_socket {
     my ($vmid) = @_;
     return "${var_run_tmpdir}/$vmid.qmp";
@@ -2506,25 +2627,6 @@ sub pidfile_name {
     return "${var_run_tmpdir}/$vmid.pid";
 }
 
-sub next_migrate_port {
-
-    for (my $p = 60000; $p < 60010; $p++) {
-
-       my $sock = IO::Socket::INET->new(Listen => 5,
-                                        LocalAddr => 'localhost',
-                                        LocalPort => $p,
-                                        ReuseAddr => 1,
-                                        Proto     => 0);
-
-       if ($sock) {
-           close($sock);
-           return $p;
-       }
-    }
-
-    die "unable to find free migration port";
-}
-
 sub vm_devices_list {
     my ($vmid) = @_;
 
@@ -2744,7 +2846,7 @@ sub qemu_bridgeadd {
     while (my ($k, $v) = each %$bridges) {
        $bridgeid = $k;
     }
-    return if $bridgeid < 1;
+    return if !$bridgeid || $bridgeid < 1;
     my $bridge = "pci.$bridgeid";
     my $devices_list = vm_devices_list($vmid);
 
@@ -2776,13 +2878,6 @@ sub qemu_block_set_io_throttle {
 
     return if !check_running($vmid) ;
 
-    $bps = 0 if !$bps;
-    $bps_rd = 0 if !$bps_rd;
-    $bps_wr = 0 if !$bps_wr;
-    $iops = 0 if !$iops;
-    $iops_rd = 0 if !$iops_rd;
-    $iops_wr = 0 if !$iops_wr;
-
     vm_mon_cmd($vmid, "block_set_io_throttle", device => $deviceid, bps => int($bps), bps_rd => int($bps_rd), bps_wr => int($bps_wr), iops => int($iops), iops_rd => int($iops_rd), iops_wr => int($iops_wr));
 
 }
@@ -2946,7 +3041,7 @@ sub qga_unfreezefs {
 }
 
 sub vm_start {
-    my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused) = @_;
+    my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused, $forcemachine, $spice_ticket) = @_;
 
     lock_config($vmid, sub {
        my $conf = load_config($vmid, $migratedfrom);
@@ -2962,14 +3057,20 @@ sub vm_start {
        # set environment variable useful inside network script
        $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
 
-       my ($cmd, $vollist) = config_to_command($storecfg, $vmid, $conf, $defaults);
+       my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
 
        my $migrate_port = 0;
-
+       my $migrate_uri;
        if ($statefile) {
            if ($statefile eq 'tcp') {
-               $migrate_port = next_migrate_port();
-               my $migrate_uri = "tcp:localhost:${migrate_port}";
+               my $localip = "localhost";
+               my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter.cfg');
+               if ($datacenterconf->{migration_unsecure}) {
+                       my $nodename = PVE::INotify::nodename();
+                       $localip = PVE::Cluster::remote_node_ip($nodename, 1);
+               }
+               $migrate_port = PVE::Tools::next_migrate_port();
+               $migrate_uri = "tcp:${localip}:${migrate_port}";
                push @$cmd, '-incoming', $migrate_uri;
                push @$cmd, '-S';
            } else {
@@ -2997,27 +3098,36 @@ sub vm_start {
        my $err = $@;
        die "start failed: $err" if $err;
 
-       print "migration listens on port $migrate_port\n" if $migrate_port;
+       print "migration listens on $migrate_uri\n" if $migrate_uri;
 
        if ($statefile && $statefile ne 'tcp')  {
            eval { vm_mon_cmd_nocheck($vmid, "cont"); };
            warn $@ if $@;
        }
 
-       if($migratedfrom) {
+       if ($migratedfrom) {
            my $capabilities = {};
            $capabilities->{capability} =  "xbzrle";
            $capabilities->{state} = JSON::true;
            eval { vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); };
-       }
-       else{
+           warn $@ if $@;
+           
+           if ($spice_port) {
+               print "spice listens on port $spice_port\n";
+               if ($spice_ticket) {
+                   PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "set_password", protocol => 'spice', password => $spice_ticket);
+                   PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "expire_password", protocol => 'spice', time => "+30");
+               }
+           }
+
+       } else {
 
            if (!$statefile && (!defined($conf->{balloon}) || $conf->{balloon})) {
-               vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024) 
+               vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024)
                    if $conf->{balloon};
-               vm_mon_cmd_nocheck($vmid, 'qom-set', 
-                           path => "machine/peripheral/balloon0", 
-                           property => "guest-stats-polling-interval", 
+               vm_mon_cmd_nocheck($vmid, 'qom-set',
+                           path => "machine/peripheral/balloon0",
+                           property => "guest-stats-polling-interval",
                            value => 2);
            }
        }
@@ -3048,7 +3158,7 @@ sub vm_qmp_command {
        $timeout = $cmd->{arguments}->{timeout};
        delete $cmd->{arguments}->{timeout};
     }
+
     eval {
        die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
        my $sname = qmp_socket($vmid);
@@ -3385,6 +3495,7 @@ sub print_pci_addr {
        scsihw1 => { bus => 0, addr => 6 },
        ahci0 => { bus => 0, addr => 7 },
        qga0 => { bus => 0, addr => 8 },
+       spice => { bus => 0, addr => 9 },
        virtio0 => { bus => 0, addr => 10 },
        virtio1 => { bus => 0, addr => 11 },
        virtio2 => { bus => 0, addr => 12 },
@@ -3538,6 +3649,7 @@ sub restore_update_config_line {
     return if $line =~ m/^lock:/;
     return if $line =~ m/^unused\d+:/;
     return if $line =~ m/^parent:/;
+    return if $line =~ m/^template:/; # restored VM is never a template
 
     if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
        # try to convert old 1.X settings
@@ -3568,6 +3680,7 @@ sub restore_update_config_line {
            print $outfd "#$line";
        } elsif ($virtdev && $map->{$virtdev}) {
            my $di = parse_drive($virtdev, $value);
+           delete $di->{format}; # format can change on restore
            $di->{file} = $map->{$virtdev};
            $value = print_drive($vmid, $di);
            print $outfd "$virtdev: $value\n";
@@ -3588,6 +3701,7 @@ sub scan_volids {
     foreach my $storeid (keys %$info) {
        foreach my $item (@{$info->{$storeid}}) {
            next if !($item->{volid} && $item->{size});
+           $item->{path} = PVE::Storage::path($cfg, $item->{volid});
            $volid_hash->{$item->{volid}} = $item;
        }
     }
@@ -3595,13 +3709,60 @@ sub scan_volids {
     return $volid_hash;
 }
 
+sub get_used_paths {
+    my ($vmid, $storecfg, $conf, $scan_snapshots, $skip_drive) = @_;
+
+    my $used_path = {};
+
+    my $scan_config = sub {
+       my ($cref, $snapname) = @_;
+
+       foreach my $key (keys %$cref) {
+           my $value = $cref->{$key};
+           if (valid_drivename($key)) {
+               next if $skip_drive && $key eq $skip_drive;
+               my $drive = parse_drive($key, $value);
+               next if !$drive || !$drive->{file} || drive_is_cdrom($drive);
+               if ($drive->{file} =~ m!^/!) {
+                   $used_path->{$drive->{file}}++; # = 1;
+               } else {
+                   my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1);
+                   next if !$storeid;
+                   my $scfg = PVE::Storage::storage_config($storecfg, $storeid, 1);
+                   next if !$scfg;
+                   my $path = PVE::Storage::path($storecfg, $drive->{file}, $snapname);
+                   $used_path->{$path}++; # = 1;
+               }
+           }
+       }
+    };
+
+    &$scan_config($conf);
+
+    undef $skip_drive;
+
+    if ($scan_snapshots) {
+       foreach my $snapname (keys %{$conf->{snapshots}}) {
+           &$scan_config($conf->{snapshots}->{$snapname}, $snapname);
+       }
+    }
+
+    return $used_path;
+}
+
 sub update_disksize {
     my ($vmid, $conf, $volid_hash) = @_;
+
     my $changes;
 
     my $used = {};
 
+    # Note: it is allowed to define multiple storages with same path (alias), so
+    # we need to check both 'volid' and real 'path' (two different volid can point
+    # to the same path).
+
+    my $usedpath = {};
+
     # update size info
     foreach my $opt (keys %$conf) {
        if (valid_drivename($opt)) {
@@ -3610,21 +3771,43 @@ sub update_disksize {
            next if !$volid;
 
            $used->{$volid} = 1;
+           if ($volid_hash->{$volid} &&
+               (my $path = $volid_hash->{$volid}->{path})) {
+               $usedpath->{$path} = 1;
+           }
 
            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}) {
+               $changes = 1;
+               $conf->{$opt} = $new;
+           }
+       }
+    }
+
+    # remove 'unusedX' entry if volume is used
+    foreach my $opt (keys %$conf) {
+       next if $opt !~ m/^unused\d+$/;
+       my $volid = $conf->{$opt};
+       my $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid};
+       if ($used->{$volid} || ($path && $usedpath->{$path})) {
            $changes = 1;
-           $conf->{$opt} = print_drive($vmid, $drive);
+           delete $conf->{$opt};
        }
     }
 
     foreach my $volid (sort keys %$volid_hash) {
        next if $volid =~ m/vm-$vmid-state-/;
        next if $used->{$volid};
+       my $path = $volid_hash->{$volid}->{path};
+       next if !$path; # just to be sure
+       next if $usedpath->{$path};
        $changes = 1;
        add_unused_volume($conf, $volid);
+       $usedpath->{$path} = 1; # avoid to add more than once (aliases)
     }
 
     return $changes;
@@ -3641,7 +3824,7 @@ sub rescan {
        my ($vmid) = @_;
 
        my $conf = load_config($vmid);
-           
+
        check_lock($conf);
 
        my $vm_volids = {};
@@ -3668,7 +3851,7 @@ sub rescan {
                &$updatefn($vmid);
            } else {
                lock_config($vmid, $updatefn, $vmid);
-           }    
+           }
        }
     }
 }
@@ -3690,7 +3873,7 @@ sub restore_vma_archive {
        } else {
            die "unknown compression method '$comp'\n";
        }
-       
+
     }
 
     my $tmpdir = "/var/tmp/vzdumptmp$$";
@@ -3749,7 +3932,7 @@ sub restore_vma_archive {
                $devinfo->{$devname}->{format} = $format;
                $devinfo->{$devname}->{storeid} = $storeid;
 
-               # check permission on storage 
+               # check permission on storage
                my $pool = $opts->{pool}; # todo: do we need that?
                if ($user ne 'root@pam') {
                    $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
@@ -3760,14 +3943,14 @@ sub restore_vma_archive {
        }
 
        foreach my $devname (keys %$devinfo) {
-           die "found no device mapping information for device '$devname'\n" 
-               if !$devinfo->{$devname}->{virtdev};        
+           die "found no device mapping information for device '$devname'\n"
+               if !$devinfo->{$devname}->{virtdev};
        }
 
        my $cfg = cfs_read_file('storage.cfg');
 
        # create empty/temp config
-       if ($oldconf) { 
+       if ($oldconf) {
            PVE::Tools::file_set_contents($conffile, "memory: 128\n");
            foreach_drive($oldconf, sub {
                my ($ds, $drive) = @_;
@@ -3794,6 +3977,12 @@ sub restore_vma_archive {
            my $d = $virtdev_hash->{$virtdev};
            my $alloc_size = int(($d->{size} + 1024 - 1)/1024);
            my $scfg = PVE::Storage::storage_config($cfg, $d->{storeid});
+
+           # test if requested format is supported
+           my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($cfg, $d->{storeid});
+           my $supported = grep { $_ eq $d->{format} } @$validFormats;
+           $d->{format} = $defFormat if !$supported;
+
            my $volid = PVE::Storage::vdisk_alloc($cfg, $d->{storeid}, $vmid,
                                                  $d->{format}, undef, $alloc_size);
            print STDERR "new volume ID is '$volid'\n";
@@ -3802,7 +3991,7 @@ sub restore_vma_archive {
 
            my $write_zeros = 1;
            # fixme: what other storages types initialize volumes with zero?
-           if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' || 
+           if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' ||
                $scfg->{type} eq 'sheepdog' || $scfg->{type} eq 'rbd') {
                $write_zeros = 0;
            }
@@ -3820,7 +4009,7 @@ sub restore_vma_archive {
 
        my $cookie = { netcount => 0 };
        while (defined(my $line = <$fh>)) {
-           restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});  
+           restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});
        }
 
        $fh->close();
@@ -3853,7 +4042,7 @@ sub restore_vma_archive {
                close($fifofh);
            }
        };
+
        print "restore vma archive: $cmd\n";
        run_command($cmd, input => $input, outfunc => $parser, afterfork => $openfifo);
     };
@@ -3977,7 +4166,7 @@ sub restore_tar_archive {
 
        my $cookie = { netcount => 0 };
        while (defined (my $line = <$srcfd>)) {
-           restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});  
+           restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});
        }
 
        $srcfd->close();
@@ -4025,7 +4214,7 @@ my $snapshot_copy_config = sub {
        next if $k eq 'digest';
        next if $k eq 'description';
        next if $k =~ m/^unused\d+$/;
-               
+
        $dest->{$k} = $source->{$k};
     }
 };
@@ -4064,7 +4253,7 @@ sub foreach_writable_storage {
        my $volid = $drive->{file};
 
        my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
-       $sidhash->{$sid} = $sid if $sid;        
+       $sidhash->{$sid} = $sid if $sid;
     }
 
     foreach my $sid (sort keys %$sidhash) {
@@ -4074,7 +4263,7 @@ sub foreach_writable_storage {
 
 my $alloc_vmstate_volid = sub {
     my ($storecfg, $vmid, $conf, $snapname) = @_;
-    
+
     # Note: we try to be smart when selecting a $target storage
 
     my $target;
@@ -4122,15 +4311,15 @@ my $snapshot_prepare = sub {
 
        my $conf = load_config($vmid);
 
-       die "you can't take a snapshot if it's a template\n" 
+       die "you can't take a snapshot if it's a template\n"
            if is_template($conf);
 
        check_lock($conf);
 
        $conf->{lock} = 'snapshot';
 
-       die "snapshot name '$snapname' already used\n" 
-           if defined($conf->{snapshots}->{$snapname}); 
+       die "snapshot name '$snapname' already used\n"
+           if defined($conf->{snapshots}->{$snapname});
 
        my $storecfg = PVE::Storage::config();
        die "snapshot feature is not available" if !has_feature('snapshot', $conf, $storecfg);
@@ -4147,6 +4336,10 @@ my $snapshot_prepare = sub {
        $snap->{snaptime} = time();
        $snap->{description} = $comment if $comment;
 
+       # always overwrite machine if we save vmstate. This makes sure we
+       # can restore it later using correct machine type
+       $snap->{machine} = get_current_qemu_machine($vmid) if $snap->{vmstate};
+
        update_config_nolock($vmid, $conf, 1);
     };
 
@@ -4162,16 +4355,16 @@ my $snapshot_commit = sub {
 
        my $conf = load_config($vmid);
 
-       die "missing snapshot lock\n" 
-           if !($conf->{lock} && $conf->{lock} eq 'snapshot'); 
+       die "missing snapshot lock\n"
+           if !($conf->{lock} && $conf->{lock} eq 'snapshot');
 
        my $snap = $conf->{snapshots}->{$snapname};
 
-       die "snapshot '$snapname' does not exist\n" if !defined($snap); 
+       die "snapshot '$snapname' does not exist\n" if !defined($snap);
+
+       die "wrong snapshot state\n"
+           if !($snap->{snapstate} && $snap->{snapstate} eq "prepare");
 
-       die "wrong snapshot state\n" 
-           if !($snap->{snapstate} && $snap->{snapstate} eq "prepare"); 
-       
        delete $snap->{snapstate};
        delete $conf->{lock};
 
@@ -4193,7 +4386,7 @@ sub snapshot_rollback {
     my $prepare = 1;
 
     my $storecfg = PVE::Storage::config();
+
     my $updatefn = sub {
 
        my $conf = load_config($vmid);
@@ -4202,9 +4395,9 @@ sub snapshot_rollback {
 
        $snap = $conf->{snapshots}->{$snapname};
 
-       die "snapshot '$snapname' does not exist\n" if !defined($snap); 
+       die "snapshot '$snapname' does not exist\n" if !defined($snap);
 
-       die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n" 
+       die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
            if $snap->{snapstate};
 
        if ($prepare) {
@@ -4222,22 +4415,33 @@ sub snapshot_rollback {
            delete $conf->{lock};
        }
 
+       my $forcemachine;
+
        if (!$prepare) {
+           my $has_machine_config = defined($conf->{machine});
+
            # copy snapshot config to current config
            $conf = &$snapshot_apply_config($conf, $snap);
            $conf->{parent} = $snapname;
+
+           # Note: old code did not store 'machine', so we try to be smart
+           # and guess the snapshot was generated with kvm 1.4 (pc-i440fx-1.4).
+           $forcemachine = $conf->{machine} || 'pc-i440fx-1.4';
+           # we remove the 'machine' configuration if not explicitly specified
+           # in the original config.
+           delete $conf->{machine} if $snap->{vmstate} && !$has_machine_config;
        }
 
        update_config_nolock($vmid, $conf, 1);
 
        if (!$prepare && $snap->{vmstate}) {
            my $statefile = PVE::Storage::path($storecfg, $snap->{vmstate});
-           vm_start($storecfg, $vmid, $statefile);
+           vm_start($storecfg, $vmid, $statefile, undef, undef, undef, $forcemachine);
        }
     };
 
     lock_config($vmid, $updatefn);
-    
+
     foreach_drive($snap, sub {
        my ($ds, $drive) = @_;
 
@@ -4289,7 +4493,7 @@ sub snapshot_create {
 
        if ($running) {
            if ($snap->{vmstate}) {
-               my $path = PVE::Storage::path($storecfg, $snap->{vmstate});     
+               my $path = PVE::Storage::path($storecfg, $snap->{vmstate});
                vm_mon_cmd($vmid, "savevm-start", statefile => $path);
                &$savevm_wait($vmid);
            } else {
@@ -4298,7 +4502,7 @@ sub snapshot_create {
        };
 
        qga_freezefs($vmid) if $running && $freezefs;
+
        foreach_drive($snap, sub {
            my ($ds, $drive) = @_;
 
@@ -4313,7 +4517,7 @@ sub snapshot_create {
     };
     my $err = $@;
 
-    eval { gqa_unfreezefs($vmid) if $running && $freezefs; };
+    eval { qga_unfreezefs($vmid) if $running && $freezefs; };
     warn $@ if $@;
 
     eval { vm_mon_cmd($vmid, "savevm-end") if $running; };
@@ -4349,7 +4553,7 @@ sub snapshot_delete {
            }
        }
     };
+
     my $updatefn =  sub {
        my ($remove_drive) = @_;
 
@@ -4357,13 +4561,13 @@ sub snapshot_delete {
 
        if (!$drivehash) {
            check_lock($conf);
-           die "you can't delete a snapshot if vm is a template\n" 
+           die "you can't delete a snapshot if vm is a template\n"
                if is_template($conf);
        }
 
        $snap = $conf->{snapshots}->{$snapname};
 
-       die "snapshot '$snapname' does not exist\n" if !defined($snap); 
+       die "snapshot '$snapname' does not exist\n" if !defined($snap);
 
        # remove parent refs
        &$unlink_parent($conf, $snap->{parent});
@@ -4442,7 +4646,7 @@ sub snapshot_delete {
 sub has_feature {
     my ($feature, $conf, $storecfg, $snapname, $running) = @_;
 
-    my $err = undef;
+    my $err;
     foreach_drive($conf, sub {
        my ($ds, $drive) = @_;
 
@@ -4451,7 +4655,7 @@ sub has_feature {
        $err = 1 if !PVE::Storage::volume_has_feature($storecfg, $feature, $volid, $snapname, $running);
     });
 
-    return 1 if !$err;
+    return $err ? 0 : 1;
 }
 
 sub template_create {
@@ -4470,8 +4674,8 @@ sub template_create {
 
        my $voliddst = PVE::Storage::vdisk_create_base($storecfg, $volid);
        $drive->{file} = $voliddst;
-       $conf->{$ds} = PVE::QemuServer::print_drive($vmid, $drive);
-       PVE::QemuServer::update_config_nolock($vmid, $conf, 1);
+       $conf->{$ds} = print_drive($vmid, $drive);
+       update_config_nolock($vmid, $conf, 1);
     });
 }
 
@@ -4524,22 +4728,13 @@ sub qemu_img_convert {
 sub qemu_img_format {
     my ($scfg, $volname) = @_;
 
-    if ($scfg->{path} && $volname =~ m/\.(raw|qcow2|qed|vmdk)$/){
+    if ($scfg->{path} && $volname =~ m/\.(raw|qcow2|qed|vmdk)$/) {
        return $1;
-    }
-    elsif ($scfg->{type} eq 'nexenta' || $scfg->{type} eq 'iscsidirect'){
-       return "iscsi";
-    }
-    elsif ($scfg->{type} eq 'lvm' || $scfg->{type} eq 'iscsi'){
+    } elsif ($scfg->{type} eq 'iscsi') {
        return "host_device";
-    }
-    elsif ($scfg->{type} eq 'rbd'){
+    } else {
        return "raw";
     }
-    #sheepdog other qemu block driver
-    else{
-       return $scfg->{type};
-    }
 }
 
 sub qemu_drive_mirror {
@@ -4555,28 +4750,34 @@ sub qemu_drive_mirror {
     if ($dst_storeid) {
        my $dst_scfg = PVE::Storage::storage_config($storecfg, $dst_storeid);
 
-       my $format = undef;
+       my $format;
         if ($dst_volname =~ m/\.(raw|qcow2)$/){
            $format = $1;
        }
 
        my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
 
-       if($format){
-           #fixme : sometime drive-mirror timeout, but works fine after. (I have see the problem with big volume > 200GB), so we need to eval
-           eval{ PVE::QemuServer::vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing", sync => "full", target => $dst_path, format => $format); };
-       }else{
-           eval{ PVE::QemuServer::vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing", sync => "full", target => $dst_path); };
+       if ($format) {
+           #fixme : sometime drive-mirror timeout, but works fine after.
+           # (I have see the problem with big volume > 200GB), so we need to eval
+           eval { vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing",
+                             sync => "full", target => $dst_path, format => $format); };
+       } else {
+           eval { vm_mon_cmd($vmid, "drive-mirror", timeout => 10, device => "drive-$drive", mode => "existing",
+                             sync => "full", target => $dst_path); };
        }
-       eval{
+
+       eval {
            while (1) {
-               my $stats = PVE::QemuServer::vm_mon_cmd($vmid, "query-block-jobs");
+               my $stats = vm_mon_cmd($vmid, "query-block-jobs");
                my $stat = @$stats[0];
+               die "mirroring job seem to have die. Maybe do you have bad sectors?" if !$stat;
+               die "error job is not mirroring" if $stat->{type} ne "mirror";
+
                my $transferred = $stat->{offset};
                my $total = $stat->{len};
                my $remaining = $total - $transferred;
                my $percent = sprintf "%.2f", ($transferred * 100 / $total);
-               die "error job is not mirroring" if $stat->{type} ne "mirror";
 
                 print "transferred: $transferred bytes remaining: $remaining bytes total: $total bytes progression: $percent %\n";
 
@@ -4588,31 +4789,112 @@ sub qemu_drive_mirror {
                        vm_suspend($vmid,1);
                        $count = 0;
                        $frozen = 1;
-                   }else {
+                   } else {
                        $count++ unless $frozen;
                    }
-               }
-               elsif ($frozen) {
-                       vm_resume($vmid,1);
-                       $count = 0;
+               } elsif ($frozen) {
+                   vm_resume($vmid,1);
+                   $count = 0;
                }
                $old_len = $stat->{offset};
                sleep 1;
            }
+
+           if ($vmiddst == $vmid) {
+               # switch the disk if source and destination are on the same guest
+               vm_mon_cmd($vmid, "block-job-complete", device => "drive-$drive");
+           }
        };
-       if ($@) {
-           eval{ PVE::QemuServer::vm_mon_cmd($vmid, "block-job-cancel", device => "drive-$drive"); };
-           die "mirroring error: $@";
+       if (my $err = $@) {
+           eval { vm_mon_cmd($vmid, "block-job-cancel", device => "drive-$drive"); };
+           die "mirroring error: $err";
        }
 
-       if($vmiddst != $vmid){
-           #if we clone a disk for a new target vm, we don't switch the disk
-           PVE::QemuServer::vm_mon_cmd($vmid, "block-job-cancel", device => "drive-$drive");
-       }else{
-           #if source and destination are on the same guest
-           PVE::QemuServer::vm_mon_cmd($vmid, "block-job-complete", device => "drive-$drive");
+       if ($vmiddst != $vmid) {
+           # if we clone a disk for a new target vm, we don't switch the disk
+           vm_mon_cmd($vmid, "block-job-cancel", device => "drive-$drive");
        }
     }
 }
 
+sub clone_disk {
+    my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
+       $newvmid, $storage, $format, $full, $newvollist) = @_;
+
+    my $newvolid;
+
+    if (!$full) {
+       print "create linked clone of drive $drivename ($drive->{file})\n";
+       $newvolid = PVE::Storage::vdisk_clone($storecfg,  $drive->{file}, $newvmid);
+       push @$newvollist, $newvolid;
+    } else {
+       my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
+       $storeid = $storage if $storage;
+
+       my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
+       if (!$format) {
+           $format = $drive->{format} || $defFormat;
+       }
+
+       # test if requested format is supported - else use default
+       my $supported = grep { $_ eq $format } @$validFormats;
+       $format = $defFormat if !$supported;
+
+       my ($size) = PVE::Storage::volume_size_info($storecfg, $drive->{file}, 3);
+
+       print "create full clone of drive $drivename ($drive->{file})\n";
+       $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $format, undef, ($size/1024));
+       push @$newvollist, $newvolid;
+
+       if (!$running || $snapname) {
+           qemu_img_convert($drive->{file}, $newvolid, $size, $snapname);
+       } else {
+           qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid);
+       }
+    }
+
+    my ($size) = PVE::Storage::volume_size_info($storecfg, $newvolid, 3);
+
+    my $disk = $drive;
+    $disk->{format} = undef;
+    $disk->{file} = $newvolid;
+    $disk->{size} = $size;
+
+    return $disk;
+}
+
+# this only works if VM is running
+sub get_current_qemu_machine {
+    my ($vmid) = @_;
+
+    my $cmd = { execute => 'query-machines', arguments => {} };
+    my $res = PVE::QemuServer::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 read_x509_subject_spice {
+    my ($filename) = @_;
+
+    # read x509 subject
+    my $bio = Net::SSLeay::BIO_new_file($filename, 'r');
+    my $x509 = Net::SSLeay::PEM_read_bio_X509($bio);
+    Net::SSLeay::BIO_free($bio);
+    my $nameobj = Net::SSLeay::X509_get_subject_name($x509);
+    my $subject = Net::SSLeay::X509_NAME_oneline($nameobj);
+    Net::SSLeay::X509_free($x509);
+  
+    # remote-viewer wants comma as seperator (not '/')
+    $subject =~ s!^/!!;
+    $subject =~ s!/(\w+=)!,$1!g;
+
+    return $subject;
+}
 1;