]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
use vm_mon_cmd_nocheck at startup
[qemu-server.git] / PVE / QemuServer.pm
index e41f8ddc037a15b377d23182ed6a396a3d6c3754..165eaf6be6e5fe4b1c88454d28b113bc2b1f20af 100644 (file)
@@ -22,9 +22,12 @@ use Storable qw(dclone);
 use PVE::Exception qw(raise raise_param_exc);
 use PVE::Storage;
 use PVE::Tools qw(run_command lock_file file_read_firstline);
+use PVE::JSONSchema qw(get_standard_option);
 use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
 use PVE::INotify;
 use PVE::ProcFSTools;
+use PVE::QMPClient;
+use PVE::RPCEnvironment;
 use Time::HiRes qw(gettimeofday);
 
 my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
@@ -32,11 +35,11 @@ my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
 # Note about locking: we use flock on the config file protect
 # against concurent actions.
 # Aditionaly, we have a 'lock' setting in the config file. This
-# can be set to 'migrate' or 'backup'. Most actions are not
+# can be set to 'migrate', 'backup', 'snapshot' or 'rollback'. Most actions are not
 # allowed when such lock is set. But you can ignore this kind of
 # lock with the --skiplock flag.
 
-cfs_register_file('/qemu-server/', 
+cfs_register_file('/qemu-server/',
                  \&parse_vm_config,
                  \&write_vm_config);
 
@@ -53,6 +56,12 @@ PVE::JSONSchema::register_standard_option('pve-qm-stateuri', {
     optional => 1,
 });
 
+PVE::JSONSchema::register_standard_option('pve-snapshot-name', {
+    description => "The name of the snapshot.",
+    type => 'string', format => 'pve-configid',
+    maxLength => 40,
+});
+
 #no warnings 'redefine';
 
 unless(defined(&_VZSYSCALLS_H_)) {
@@ -170,7 +179,7 @@ my $confdesc = {
        optional => 1,
        type => 'string',
        description => "Lock/unlock the VM.",
-       enum => [qw(migrate backup)],
+       enum => [qw(migrate backup snapshot rollback)],
     },
     cpulimit => {
        optional => 1,
@@ -197,8 +206,16 @@ my $confdesc = {
     balloon => {
         optional => 1,
         type => 'integer',
-        description => "Amount of target RAM for the VM in MB.",
-       minimum => 16,
+        description => "Amount of target RAM for the VM in MB. Using zero disables the ballon driver.",
+       minimum => 0,
+    },
+    shares => {
+        optional => 1,
+        type => 'integer',
+        description => "Amount of memory shares for auto-ballooning. The larger the number is, the more memory this VM gets. Number is relative to weights of all other running VMs. Using zero disables auto-ballooning",
+       minimum => 0,
+       maximum => 50000,
+       default => 1000,
     },
     keyboard => {
        optional => 1,
@@ -212,6 +229,13 @@ my $confdesc = {
        type => 'string', format => 'dns-name',
        description => "Set a name for the VM. Only used on the configuration web interface.",
     },
+    scsihw => {
+       optional => 1,
+       type => 'string',
+       description => "scsi controller model",
+       enum => [qw(lsi virtio-scsi-pci megasas)],
+       default => 'lsi',
+    },
     description => {
        optional => 1,
        type => 'string',
@@ -220,7 +244,7 @@ my $confdesc = {
     ostype => {
        optional => 1,
        type => 'string',
-        enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 l24 l26)],
+        enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 l24 l26)],
        description => <<EODESC,
 Used to enable special optimization/features for specific
 operating systems:
@@ -232,11 +256,12 @@ w2k3   => Microsoft Windows 2003
 w2k8   => Microsoft Windows 2008
 wvista => Microsoft Windows Vista
 win7   => Microsoft Windows 7
+win8   => Microsoft Windows 8/2012
 l24    => Linux 2.4 Kernel
 l26    => Linux 2.6/3.X Kernel
 
-other|l24|l26                  ... no special behaviour
-wxp|w2k|w2k3|w2k8|wvista|win7  ... use --localtime switch
+other|l24|l26                       ... no special behaviour
+wxp|w2k|w2k3|w2k8|wvista|win7|win8  ... use --localtime switch
 EODESC
     },
     boot => {
@@ -250,7 +275,7 @@ EODESC
        optional => 1,
        type => 'string', format => 'pve-qm-bootdisk',
        description => "Enable booting from specified disk.",
-       pattern => '(ide|scsi|virtio)\d+',
+       pattern => '(ide|sata|scsi|virtio)\d+',
     },
     smp => {
        optional => 1,
@@ -279,6 +304,12 @@ EODESC
        description => "Enable/disable ACPI.",
        default => 1,
     },
+    agent => {
+       optional => 1,
+       type => 'boolean',
+       description => "Enable/disable Qemu GuestAgent.",
+       default => 0,
+    },
     kvm => {
        optional => 1,
        type => 'boolean',
@@ -288,8 +319,8 @@ EODESC
     tdf => {
        optional => 1,
        type => 'boolean',
-       description => "Enable/disable time drift fix. This is ignored for kvm versions newer that 1.0 (not needed anymore).",
-       default => 1,
+       description => "Enable/disable time drift fix.",
+       default => 0,
     },
     localtime => {
        optional => 1,
@@ -304,7 +335,7 @@ 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 win7/w2k8, and 'cirrur' for other OS types",
+       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)],
     },
     watchdog => {
@@ -366,9 +397,24 @@ EODESCR
        optional => 1,
        description => "Emulated CPU type.",
        type => 'string',
-       enum => [ qw(486 athlon pentium pentium2 pentium3 coreduo core2duo kvm32 kvm64 qemu32 qemu64 phenom cpu64-rhel6 cpu64-rhel5 Conroe Penryn Nehalem Westmere Opteron_G1 Opteron_G2 Opteron_G3 host) ],
+       enum => [ qw(486 athlon pentium pentium2 pentium3 coreduo core2duo kvm32 kvm64 qemu32 qemu64 phenom Conroe Penryn Nehalem Westmere SandyBridge Haswell Opteron_G1 Opteron_G2 Opteron_G3 Opteron_G4 Opteron_G5 host) ],
        default => 'qemu64',
     },
+    parent => get_standard_option('pve-snapshot-name', {
+       optional => 1,
+       description => "Parent snapshot name. This is used internally, and should not be modified.",
+    }),
+    snaptime => {
+       optional => 1,
+       description => "Timestamp for snapshots.",
+       type => 'integer',
+       minimum => 0,
+    },
+    vmstate => {
+       optional => 1,
+       type => 'string', format => 'pve-volume-id',
+       description => "Reference to a volume which stores the VM state. This is used internally for snapshots.",
+    },
 };
 
 # what about other qemu settings ?
@@ -394,10 +440,10 @@ while (my ($k, $v) = each %$confdesc) {
 
 my $MAX_IDE_DISKS = 4;
 my $MAX_SCSI_DISKS = 14;
-my $MAX_VIRTIO_DISKS = 6;
+my $MAX_VIRTIO_DISKS = 16;
 my $MAX_SATA_DISKS = 6;
 my $MAX_USB_DEVICES = 5;
-my $MAX_NETS = 6;
+my $MAX_NETS = 32;
 my $MAX_UNUSED_DISKS = 8;
 my $MAX_HOSTPCI_DEVICES = 2;
 my $MAX_SERIAL_PORTS = 4;
@@ -407,7 +453,6 @@ my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000',  'pcnet',  'virtio',
                      'ne2k_isa', 'i82551', 'i82557b', 'i82559er'];
 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
 
-# fixme:
 my $netdesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-net',
@@ -446,7 +491,7 @@ my $idedesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-drive',
     typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
-    description => "Use volume as IDE hard disk or CD-ROM (n is 0 to 3).",
+    description => "Use volume as IDE hard disk or CD-ROM (n is 0 to " .($MAX_IDE_DISKS -1) . ").",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-ide", $idedesc);
 
@@ -454,7 +499,7 @@ my $scsidesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-drive',
     typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
-    description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to 13).",
+    description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to " . ($MAX_SCSI_DISKS - 1) . ").",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc);
 
@@ -462,7 +507,7 @@ my $satadesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-drive',
     typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
-    description => "Use volume as SATA hard disk or CD-ROM (n is 0 to 5).",
+    description => "Use volume as SATA hard disk or CD-ROM (n is 0 to " . ($MAX_SATA_DISKS - 1). ").",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-sata", $satadesc);
 
@@ -470,7 +515,7 @@ my $virtiodesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-drive',
     typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
-    description => "Use volume as VIRTIO hard disk (n is 0 to 5).",
+    description => "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . ").",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
 
@@ -611,7 +656,7 @@ sub kvm_user_version {
 
     my $tmp = `kvm -help 2>/dev/null`;
 
-    if ($tmp =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?) /) {
+    if ($tmp =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)[,\s]/) {
        $kvm_user_version = $2;
     }
 
@@ -654,50 +699,12 @@ sub os_list_description {
        w2k8 => 'Windows 2008',
        wvista => 'Windows Vista',
        win7 => 'Windows 7',
+       win8 => 'Windows 8/2012',
        l24 => 'Linux 2.4',
        l26 => 'Linux 2.6',
     };
 }
 
-sub disk_devive_info {
-    my $dev = shift;
-
-    die "unknown disk device format '$dev'" if $dev !~ m/^(ide|scsi|virtio)(\d+)$/;
-
-    my $bus = $1;
-    my $index = $2;
-    my $maxdev = 1024;
-
-    if ($bus eq 'ide') {
-       $maxdev = 2;
-    } elsif ($bus eq 'scsi') {
-       $maxdev = 7;
-    }
-
-    my $controller = int($index / $maxdev);
-    my $unit = $index % $maxdev;
-
-
-    return { bus => $bus, desc => uc($bus) . " $controller:$unit",
-            controller => $controller, unit => $unit, index => $index };
-
-}
-
-sub qemu_drive_name {
-    my ($dev, $media) = @_;
-
-    my $info = disk_devive_info($dev);
-    my $mediastr = '';
-
-    if (($info->{bus} eq 'ide') || ($info->{bus} eq 'scsi')) {
-       $mediastr = ($media eq 'cdrom') ? "-cd" : "-hd";
-       return sprintf("%s%i%s%i", $info->{bus}, $info->{controller},
-                      $mediastr, $info->{unit});
-    } else {
-       return sprintf("%s%i", $info->{bus}, $info->{index});
-    }
-}
-
 my $cdrom_path;
 
 sub get_cdrom_path {
@@ -805,6 +812,40 @@ sub create_conf_nolock {
     PVE::Tools::file_set_contents($filename, $data);
 }
 
+my $parse_size = sub {
+    my ($value) = @_;
+
+    return undef if $value !~ m/^(\d+(\.\d+)?)([KMG])?$/;
+    my ($size, $unit) = ($1, $3);
+    if ($unit) {
+       if ($unit eq 'K') {
+           $size = $size * 1024;
+       } elsif ($unit eq 'M') {
+           $size = $size * 1024 * 1024;
+       } elsif ($unit eq 'G') {
+           $size = $size * 1024 * 1024 * 1024;
+       }
+    }
+    return int($size);
+};
+
+my $format_size = sub {
+    my ($size) = @_;
+
+    $size = int($size);
+
+    my $kb = int($size/1024);
+    return $size if $kb*1024 != $size;
+
+    my $mb = int($kb/1024);
+    return "${kb}K" if $mb*1024 != $kb;
+
+    my $gb = int($mb/1024);
+    return "${mb}M" if $gb*1024 != $mb;
+
+    return "${gb}G";
+};
+
 # ideX = [volume=]volume-id[,media=d][,cyls=c,heads=h,secs=s[,trans=t]]
 #        [,snapshot=on|off][,cache=on|off][,format=f][,backup=yes|no]
 #        [,rerror=ignore|report|stop][,werror=enospc|ignore|report|stop]
@@ -829,13 +870,18 @@ sub parse_drive {
     foreach my $p (split (/,/, $data)) {
        next if $p =~ m/^\s*$/;
 
-       if ($p =~ m/^(file|volume|cyls|heads|secs|trans|media|snapshot|cache|format|rerror|werror|backup|aio|bps|bps_rd|bps_wr|iops|iops_rd|iops_wr)=(.+)$/) {
+       if ($p =~ m/^(file|volume|cyls|heads|secs|trans|media|snapshot|cache|format|rerror|werror|backup|aio|bps|mbps|bps_rd|mbps_rd|bps_wr|mbps_wr|iops|iops_rd|iops_wr|size)=(.+)$/) {
            my ($k, $v) = ($1, $2);
 
            $k = 'file' if $k eq 'volume';
 
            return undef if defined $res->{$k};
 
+           if ($k eq 'bps' || $k eq 'bps_rd' || $k eq 'bps_wr') {
+               return undef if !$v || $v !~ m/^\d+/;
+               $k = "m$k";
+               $v = sprintf("%.3f", $v / (1024*1024));
+           }
            $res->{$k} = $v;
        } else {
            if (!$res->{file} && $p !~ m/=/) {
@@ -862,19 +908,25 @@ 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->{bps_rd} && $res->{bps};
-    return undef if $res->{bps_wr} && $res->{bps};
+    
+    return undef if $res->{mbps_rd} && $res->{mbps};
+    return undef if $res->{mbps_wr} && $res->{mbps};
+
+    return undef if $res->{mbps} && $res->{mbps} !~ m/^\d+(\.\d+)?$/;
+    return undef if $res->{mbps_rd} && $res->{mbps_rd} !~ m/^\d+(\.\d+)?$/;
+    return undef if $res->{mbps_wr} && $res->{mbps_wr} !~ m/^\d+(\.\d+)?$/;
+
     return undef if $res->{iops_rd} && $res->{iops};
     return undef if $res->{iops_wr} && $res->{iops};
-
-    return undef if $res->{bps} && $res->{bps} !~ m/^\d+$/;
-    return undef if $res->{bps_rd} && $res->{bps_rd} !~ m/^\d+$/;
-    return undef if $res->{bps_wr} && $res->{bps_wr} !~ m/^\d+$/;
     return undef if $res->{iops} && $res->{iops} !~ m/^\d+$/;
     return undef if $res->{iops_rd} && $res->{iops_rd} !~ m/^\d+$/;
     return undef if $res->{iops_wr} && $res->{iops_wr} !~ m/^\d+$/;
 
 
+    if ($res->{size}) {
+       return undef if !defined($res->{size} = &$parse_size($res->{size})); 
+    }
+
     if ($res->{media} && ($res->{media} eq 'cdrom')) {
        return undef if $res->{snapshot} || $res->{trans} || $res->{format};
        return undef if $res->{heads} || $res->{secs} || $res->{cyls};
@@ -889,16 +941,20 @@ sub parse_drive {
     return $res;
 }
 
-my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio bps bps_rd bps_wr iops iops_rd iops_wr);
+my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio iops iops_rd iops_wr);
 
 sub print_drive {
     my ($vmid, $drive) = @_;
 
     my $opts = '';
-    foreach my $o (@qemu_drive_options, 'backup') {
+    foreach my $o (@qemu_drive_options, 'mbps', 'mbps_rd', 'mbps_wr', 'backup') {
        $opts .= ",$o=$drive->{$o}" if $drive->{$o};
     }
 
+    if ($drive->{size}) {
+       $opts .= ",size=" . &$format_size($drive->{size});
+    }
+
     return "$drive->{file}$opts";
 }
 
@@ -914,21 +970,21 @@ sub scsi_inquiry {
        die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
        return undef;
     }
-    my $version = unpack("I", $versionbuf);    
+    my $version = unpack("I", $versionbuf);
     if ($version < 30000) {
        die "scsi generic interface too old\n"  if !$noerr;
        return undef;
     }
-    
+
     my $buf = "\x00" x 36;
     my $sensebuf = "\x00" x 8;
     my $cmd = pack("C x3 C x11", 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";
 
-    my $packet = pack($sg_io_hdr_t, ord('S'), -3, length($cmd), 
-                     length($sensebuf), 0, length($buf), $buf, 
+    my $packet = pack($sg_io_hdr_t, ord('S'), -3, length($cmd),
+                     length($sensebuf), 0, length($buf), $buf,
                      $cmd, $sensebuf, 6000);
 
     $ret = ioctl($fh, $SG_IO, $packet);
@@ -936,7 +992,7 @@ sub scsi_inquiry {
        die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
        return undef;
     }
-    
+
     my @res = unpack($sg_io_hdr_t, $packet);
     if ($res[17] || $res[18]) {
        die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
@@ -961,16 +1017,16 @@ sub path_is_scsi {
 }
 
 sub print_drivedevice_full {
-    my ($storecfg, $vmid, $drive) = @_;
+    my ($storecfg, $conf, $vmid, $drive, $bridges) = @_;
 
     my $device = '';
     my $maxdev = 0;
 
     if ($drive->{interface} eq 'virtio') {
-       my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}");
+       my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}", $bridges);
        $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
     } elsif ($drive->{interface} eq 'scsi') {
-       $maxdev = 7;
+       $maxdev = ($conf->{scsihw} && $conf->{scsihw} ne 'lsi') ? 256 : 7;
        my $controller = int($drive->{index} / $maxdev);
        my $unit = $drive->{index} % $maxdev;
        my $devicetype = 'hd';
@@ -983,10 +1039,21 @@ sub print_drivedevice_full {
               } else {
                   $path = PVE::Storage::path($storecfg, $drive->{file});
               }
-             $devicetype = 'block' if path_is_scsi($path);
+
+             if($path =~ m/^iscsi\:\/\//){
+                $devicetype = 'generic';
+             }
+             else {
+                $devicetype = 'block' if path_is_scsi($path);
+             }
          }
 
-       $device = "scsi-$devicetype,bus=lsi$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
+        if (!$conf->{scsihw} || $conf->{scsihw} eq 'lsi'){
+            $device = "scsi-$devicetype,bus=scsihw$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}" if !$conf->{scsihw} || $conf->{scsihw} eq 'lsi';
+        } else {
+            $device = "scsi-$devicetype,bus=scsihw$controller.0,channel=0,scsi-id=0,lun=$drive->{index},drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
+        }
+
     } elsif ($drive->{interface} eq 'ide'){
        $maxdev = 2;
        my $controller = int($drive->{index} / $maxdev);
@@ -1019,6 +1086,11 @@ sub print_drive_full {
        $opts .= ",$o=$drive->{$o}" if $drive->{$o};
     }
 
+    foreach my $o (qw(bps bps_rd bps_wr)) {
+       my $v = $drive->{"m$o"};
+       $opts .= ",$o=" . int($v*1024*1024) if $v;
+    }
+
     # use linux-aio by default (qemu default is threads)
     $opts .= ",aio=native" if !$drive->{aio};
 
@@ -1043,7 +1115,7 @@ sub print_drive_full {
 }
 
 sub print_netdevice_full {
-    my ($vmid, $conf, $net, $netid) = @_;
+    my ($vmid, $conf, $net, $netid, $bridges) = @_;
 
     my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
 
@@ -1055,7 +1127,7 @@ sub print_netdevice_full {
     # qemu > 0.15 always try to boot from network - we disable that by
     # not loading the pxe rom file
     my $extra = ($bootorder !~ m/n/) ? "romfile=," : '';
-    my $pciaddr = print_pci_addr("$netid");
+    my $pciaddr = print_pci_addr("$netid", $bridges);
     my $tmpstr = "$device,${extra}mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
     $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex} ;
     return $tmpstr;
@@ -1179,14 +1251,12 @@ sub add_unused_volume {
     }
 
     die "To many unused volume - please delete them first.\n" if !$key;
-    
+
     $config->{$key} = $volid;
 
     return $key;
 }
 
-# fixme: remove all thos $noerr parameters?
-
 PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk);
 sub verify_bootdisk {
     my ($value, $noerr) = @_;
@@ -1342,6 +1412,7 @@ sub json_config_properties {
     my $prop = shift;
 
     foreach my $opt (keys %$confdesc) {
+       next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'vmstate';
        $prop->{$opt} = $confdesc->{$opt};
     }
 
@@ -1488,52 +1559,10 @@ sub destroy_vm {
     warn $@ if $@;
 }
 
-# fixme: remove?
-sub load_diskinfo_old {
-    my ($storecfg, $vmid, $conf) = @_;
-
-    my $info = {};
-    my $res = {};
-    my $vollist;
-
-    foreach_drive($conf, sub {
-       my ($ds, $di) = @_;
-
-       $res->{$ds} = $di;
-
-       return if drive_is_cdrom($di);
-
-       if ($di->{file} =~ m|^/dev/.+|) {
-           $info->{$di->{file}}->{size} = PVE::Storage::file_size_info($di->{file});
-       } else {
-           push @$vollist, $di->{file};
-       }
-    });
-
-    eval {
-       my $dl = PVE::Storage::vdisk_list($storecfg, undef, $vmid, $vollist);
-
-       PVE::Storage::foreach_volid($dl, sub {
-           my ($volid, $sid, $volname, $d) = @_;
-           $info->{$volid} = $d;
-       });
-    };
-    warn $@ if $@;
-
-    foreach my $ds (keys %$res) {
-       my $di = $res->{$ds};
-
-       $res->{$ds}->{disksize} = $info->{$di->{file}} ?
-           $info->{$di->{file}}->{size} / (1024*1024) : 0;
-    }
-
-    return $res;
-}
-
 sub load_config {
-    my ($vmid) = @_;
+    my ($vmid, $node) = @_;
 
-    my $cfspath = cfs_config_path($vmid);
+    my $cfspath = cfs_config_path($vmid, $node);
 
     my $conf = PVE::Cluster::cfs_read_file($cfspath);
 
@@ -1549,6 +1578,7 @@ sub parse_vm_config {
 
     my $res = {
        digest => Digest::SHA::sha1_hex($raw),
+       snapshots => {},
     };
 
     $filename =~ m|/qemu-server/(\d+)\.conf$|
@@ -1556,12 +1586,20 @@ sub parse_vm_config {
 
     my $vmid = $1;
 
+    my $conf = $res;
     my $descr = '';
 
-    while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
-       my $line = $1;
-
+    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} = {}; 
+           next;
+       }
 
        if ($line =~ m/^\#(.*)\s*$/) {
            $descr .= PVE::Tools::decode_text($1) . "\n";
@@ -1570,10 +1608,12 @@ sub parse_vm_config {
 
        if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
            $descr .= PVE::Tools::decode_text($2);
+       } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
+           $conf->{snapstate} = $1;
        } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
            my $key = $1;
            my $value = $2;
-           $res->{$key} = $value;
+           $conf->{$key} = $value;
        } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(\S+)\s*$/) {
            my $key = $1;
            my $value = $2;
@@ -1594,21 +1634,17 @@ sub parse_vm_config {
                }
 
                if ($key eq 'cdrom') {
-                   $res->{ide2} = $value;
+                   $conf->{ide2} = $value;
                } else {
-                   $res->{$key} = $value;
+                   $conf->{$key} = $value;
                }
            }
        }
     }
 
-    $res->{description} = $descr if $descr;
+    $conf->{description} = $descr if $descr;
 
-    # convert old smp to sockets
-    if ($res->{smp} && !$res->{sockets}) {
-       $res->{sockets} = $res->{smp};
-    }
-    delete $res->{smp};
+    delete $res->{snapstate}; # just to be sure
 
     return $res;
 }
@@ -1616,6 +1652,8 @@ sub parse_vm_config {
 sub write_vm_config {
     my ($filename, $conf) = @_;
 
+    delete $conf->{snapstate}; # just to be sure
+
     if ($conf->{cdrom}) {
        die "option ide2 conflicts with cdrom\n" if $conf->{ide2};
        $conf->{ide2} = $conf->{cdrom};
@@ -1631,41 +1669,62 @@ sub write_vm_config {
        delete $conf->{smp};
     }
 
-    my $new_volids = {};
-    foreach my $key (keys %$conf) {
-       next if $key eq 'digest' || $key eq 'description';
-       my $value = $conf->{$key};
-       eval { $value = check_type($key, $value); };
-       die "unable to parse value of '$key' - $@" if $@;
+    my $used_volids = {};
+
+    my $cleanup_config = sub {
+       my ($cref) = @_;
+
+       foreach my $key (keys %$cref) {
+           next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
+               $key eq 'snapstate';
+           my $value = $cref->{$key};
+           eval { $value = check_type($key, $value); };
+           die "unable to parse value of '$key' - $@" if $@;
 
-       $conf->{$key} = $value;
+           $cref->{$key} = $value;
 
-       if (valid_drivename($key)) {
-           my $drive = PVE::QemuServer::parse_drive($key, $value);
-           $new_volids->{$drive->{file}} = 1 if $drive && $drive->{file};
+           if (valid_drivename($key)) {
+               my $drive = PVE::QemuServer::parse_drive($key, $value);
+               $used_volids->{$drive->{file}} = 1 if $drive && $drive->{file};
+           }
        }
+    };
+
+    &$cleanup_config($conf);
+    foreach my $snapname (keys %{$conf->{snapshots}}) {
+       &$cleanup_config($conf->{snapshots}->{$snapname});
     }
 
     # remove 'unusedX' settings if we re-add a volume
     foreach my $key (keys %$conf) {
        my $value = $conf->{$key};
-       if ($key =~ m/^unused/ && $new_volids->{$value}) {
+       if ($key =~ m/^unused/ && $used_volids->{$value}) {
            delete $conf->{$key};
        }
     }
+  
+    my $generate_raw_config = sub {
+       my ($conf) = @_;
 
-    # gererate RAW data
-    my $raw = '';
+       my $raw = '';
 
-    # add description as comment to top of file
-    my $descr = $conf->{description} || '';
-    foreach my $cl (split(/\n/, $descr)) {
-       $raw .= '#' .  PVE::Tools::encode_text($cl) . "\n";
-    }
+       # add description as comment to top of file
+       my $descr = $conf->{description} || '';
+       foreach my $cl (split(/\n/, $descr)) {
+           $raw .= '#' .  PVE::Tools::encode_text($cl) . "\n";
+       }
+
+       foreach my $key (sort keys %$conf) {
+           next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots';
+           $raw .= "$key: $conf->{$key}\n";
+       }
+       return $raw;
+    };
 
-    foreach my $key (sort keys %$conf) {
-       next if $key eq 'digest' || $key eq 'description';
-       $raw .= "$key: $conf->{$key}\n";
+    my $raw = &$generate_raw_config($conf);
+    foreach my $snapname (sort keys %{$conf->{snapshots}}) {
+       $raw .= "\n[$snapname]\n";
+       $raw .= &$generate_raw_config($conf->{snapshots}->{$snapname});
     }
 
     return $raw;
@@ -1675,7 +1734,7 @@ sub update_config_nolock {
     my ($vmid, $conf, $skiplock) = @_;
 
     check_lock($conf) if !$skiplock;
-    
+
     my $cfspath = cfs_config_path($vmid);
 
     PVE::Cluster::cfs_write_file($cfspath, $conf);
@@ -1789,9 +1848,9 @@ sub check_cmdline {
 }
 
 sub check_running {
-    my ($vmid, $nocheck) = @_;
+    my ($vmid, $nocheck, $node) = @_;
 
-    my $filename = config_file($vmid);
+    my $filename = config_file($vmid, $node);
 
     die "unable to find configuration file for VM $vmid - no such machine\n"
        if !$nocheck && ! -f $filename;
@@ -1839,8 +1898,6 @@ sub vzlist {
     return $vzlist;
 }
 
-my $storage_timeout_hash = {};
-
 sub disksize {
     my ($storecfg, $conf) = @_;
 
@@ -1858,49 +1915,16 @@ sub disksize {
     my $volid = $drive->{file};
     return undef if !$volid;
 
-    my $path;
-    my $storeid;
-    my $timeoutid;
-
-    if ($volid =~ m|^/|) {
-       $path = $timeoutid = $volid;
-    } else {
-       eval {
-           $storeid = $timeoutid = PVE::Storage::parse_volume_id($volid);
-           $path = PVE::Storage::path($storecfg, $volid);
-       };
-       if (my $err = $@) {
-           warn $err;
-           return undef;
-       }
-    }
-
-    my $last_timeout = $storage_timeout_hash->{$timeoutid};
-    if ($last_timeout) {
-       if ((time() - $last_timeout) < 30) {
-           # skip storage with errors
-           return undef ;
-       }
-       delete $storage_timeout_hash->{$timeoutid};
-    }
-
-    my ($size, $format, $used);
-
-    ($size, $format, $used) = PVE::Storage::file_size_info($path, 1);
-
-    if (!defined($format)) {
-       # got timeout
-       $storage_timeout_hash->{$timeoutid} = time();
-       return undef;
-    }
-
-    return wantarray ? ($size, $used) : $size;
+    return $drive->{size};
 }
 
 my $last_proc_pid_stat;
 
+# get VM status information
+# This must be fast and should not block ($full == false)
+# We only query KVM using QMP if $full == true (this can be slow)
 sub vmstatus {
-    my ($opt_vmid) = @_;
+    my ($opt_vmid, $full) = @_;
 
     my $res = {};
 
@@ -1923,9 +1947,9 @@ sub vmstatus {
        # fixme: better status?
        $d->{status} = $list->{$vmid}->{pid} ? 'running' : 'stopped';
 
-       my ($size, $used) = disksize($storecfg, $conf);
-       if (defined($size) && defined($used)) {
-           $d->{disk} = $used;
+       my $size = disksize($storecfg, $conf);
+       if (defined($size)) {
+           $d->{disk} = 0; # no info available
            $d->{maxdisk} = $size;
        } else {
            $d->{disk} = 0;
@@ -1938,6 +1962,11 @@ sub vmstatus {
        $d->{name} = $conf->{name} || "VM $vmid";
        $d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024) : 0;
 
+       if ($conf->{balloon}) {
+           $d->{balloon_min} = $conf->{balloon}*(1024*1024);
+           $d->{shares} = $conf->{shares} || 1000;
+       }
+
        $d->{uptime} = 0;
        $d->{cpu} = 0;
        $d->{mem} = 0;
@@ -1970,18 +1999,6 @@ sub vmstatus {
        my $pid = $d->{pid};
        next if !$pid;
 
-       if (my $fh = IO::File->new("/proc/$pid/io", "r")) {
-           my $data = {};
-           while (defined(my $line = <$fh>)) {
-               if ($line =~ m/^([rw]char):\s+(\d+)$/) {
-                   $data->{$1} = $2;
-               }
-           }
-           close($fh);
-           $d->{diskread} = $data->{rchar} || 0;
-           $d->{diskwrite} = $data->{wchar} || 0;
-       }
-
        my $pstat = PVE::ProcFSTools::read_proc_pid_stat($pid);
        next if !$pstat; # not running
 
@@ -2019,6 +2036,72 @@ sub vmstatus {
        }
     }
 
+    return $res if !$full;
+
+    my $qmpclient = PVE::QMPClient->new();
+
+    my $ballooncb = sub {
+       my ($vmid, $resp) = @_;
+
+       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};
+       }
+
+    };
+
+    my $blockstatscb = sub {
+       my ($vmid, $resp) = @_;
+       my $data = $resp->{'return'} || [];
+       my $totalrdbytes = 0;
+       my $totalwrbytes = 0;
+       for my $blockstat (@$data) {
+           $totalrdbytes = $totalrdbytes + $blockstat->{stats}->{rd_bytes};
+           $totalwrbytes = $totalwrbytes + $blockstat->{stats}->{wr_bytes};
+       }
+       $res->{$vmid}->{diskread} = $totalrdbytes;
+       $res->{$vmid}->{diskwrite} = $totalwrbytes;
+    };
+
+    my $statuscb = sub {
+       my ($vmid, $resp) = @_;
+
+       $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
+       # this fails if ballon driver is not loaded, so this must be
+       # the last commnand (following command are aborted if this fails).
+       $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
+
+       my $status = 'unknown';
+       if (!defined($status = $resp->{'return'}->{status})) {
+           warn "unable to get VM status\n";
+           return;
+       }
+
+       $res->{$vmid}->{qmpstatus} = $resp->{'return'}->{status};
+    };
+
+    foreach my $vmid (keys %$list) {
+       next if $opt_vmid && ($vmid ne $opt_vmid);
+       next if !$res->{$vmid}->{pid}; # not running
+       $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
+    }
+
+    $qmpclient->queue_execute();
+
+    foreach my $vmid (keys %$list) {
+       next if $opt_vmid && ($vmid ne $opt_vmid);
+       $res->{$vmid}->{qmpstatus} = $res->{$vmid}->{status} if !$res->{$vmid}->{qmpstatus};
+    }
+
     return $res;
 }
 
@@ -2035,11 +2118,48 @@ 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;
+    };
+
+    PVE::QemuServer::foreach_drive($conf, sub {
+       my ($ds, $drive) = @_;
+       &$test_volid($drive->{file}, drive_is_cdrom($drive));
+    });
+
+    foreach my $snapname (keys %{$conf->{snapshots}}) {
+       my $snap = $conf->{snapshots}->{$snapname};
+       &$test_volid($snap->{vmstate}, 0);
+       PVE::QemuServer::foreach_drive($snap, sub {
+           my ($ds, $drive) = @_;
+           &$test_volid($drive->{file}, drive_is_cdrom($drive));
+        });
+    }
+
+    foreach my $volid (keys %$volhash) {
+       &$func($volid, $volhash->{$volid});     
+    }
+}
+
 sub config_to_command {
-    my ($storecfg, $vmid, $conf, $defaults, $migrate_uri) = @_;
+    my ($storecfg, $vmid, $conf, $defaults) = @_;
 
     my $cmd = [];
+    my $globalFlags = [];
+    my $machineFlags = [];
+    my $rtcFlags = [];
+    my $devices = [];
     my $pciaddr = '';
+    my $bridges = {};
     my $kvmver = kvm_user_version();
     my $vernum = 0; # unknown
     if ($kvmver =~ m/^(\d+)\.(\d+)$/) {
@@ -2058,38 +2178,32 @@ sub config_to_command {
 
     my $use_virtio = 0;
 
-    my $socket = monitor_socket($vmid);
-    push @$cmd, '-chardev', "socket,id=monitor,path=$socket,server,nowait";
-    push @$cmd, '-mon', "chardev=monitor,mode=readline";
-
     my $qmpsocket = qmp_socket($vmid);
     push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait";
     push @$cmd, '-mon', "chardev=qmp,mode=control";
 
-    $socket = vnc_socket($vmid);
+    my $socket = vnc_socket($vmid);
     push @$cmd,  '-vnc', "unix:$socket,x509,password";
 
     push @$cmd, '-pidfile' , pidfile_name($vmid);
 
     push @$cmd, '-daemonize';
 
-    push @$cmd, '-incoming', $migrate_uri if $migrate_uri;
-
     my $use_usb2 = 0;
     for (my $i = 0; $i < $MAX_USB_DEVICES; $i++)  {
        next if !$conf->{"usb$i"};
        $use_usb2 = 1;
     }
     # include usb device config
-    push @$cmd, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
+    push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
 
     # enable absolute mouse coordinates (needed by vnc)
     my $tablet = defined($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
     if ($tablet) {
        if ($use_usb2) {
-           push @$cmd, '-device', 'usb-tablet,bus=ehci.0,port=6';
+           push @$devices, '-device', 'usb-tablet,bus=ehci.0,port=6';
        } else {
-           push @$cmd, '-usbdevice', 'tablet';
+           push @$devices, '-usbdevice', 'tablet';
        }
     }
 
@@ -2097,8 +2211,8 @@ sub config_to_command {
     for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++)  {
           my $d = parse_hostpci($conf->{"hostpci$i"});
           next if !$d;
-         $pciaddr = print_pci_addr("hostpci$i");
-          push @$cmd, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i$pciaddr";
+         $pciaddr = print_pci_addr("hostpci$i", $bridges);
+          push @$devices, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i$pciaddr";
     }
 
     # usb devices
@@ -2106,9 +2220,9 @@ sub config_to_command {
        my $d = parse_usb_device($conf->{"usb$i"});
        next if !$d;
        if ($d->{vendorid} && $d->{productid}) {
-           push @$cmd, '-device', "usb-host,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
+           push @$devices, '-device', "usb-host,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
        } elsif (defined($d->{hostbus}) && defined($d->{hostport})) {
-           push @$cmd, '-device', "usb-host,hostbus=$d->{hostbus},hostport=$d->{hostport}";
+           push @$devices, '-device', "usb-host,hostbus=$d->{hostbus},hostport=$d->{hostport}";
        }
     }
 
@@ -2116,8 +2230,8 @@ sub config_to_command {
     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 @$cmd, '-chardev', "tty,id=serial$i,path=$path";
-           push @$cmd, '-device', "isa-serial,chardev=serial$i";
+           push @$devices, '-chardev', "tty,id=serial$i,path=$path";
+           push @$devices, '-device', "isa-serial,chardev=serial$i";
        }
     }
 
@@ -2125,8 +2239,8 @@ sub config_to_command {
     for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++)  {
        if (my $path = $conf->{"parallel$i"}) {
            die "no such parallel device\n" if ! -c $path;
-           push @$cmd, '-chardev', "parport,id=parallel$i,path=$path";
-           push @$cmd, '-device', "isa-parallel,chardev=parallel$i";
+           push @$devices, '-chardev', "parport,id=parallel$i,path=$path";
+           push @$devices, '-device', "isa-parallel,chardev=parallel$i";
        }
     }
 
@@ -2163,7 +2277,7 @@ sub config_to_command {
 
     my $vga = $conf->{vga};
     if (!$vga) {
-       if ($conf->{ostype} && ($conf->{ostype} eq 'win7' || $conf->{ostype} eq 'w2k8')) {
+       if ($conf->{ostype} && ($conf->{ostype} eq 'win8' || $conf->{ostype} eq 'win7' || $conf->{ostype} eq 'w2k8')) {
            $vga = 'std';
        } else {
            $vga = 'cirrus';
@@ -2174,43 +2288,42 @@ sub config_to_command {
 
     # time drift fix
     my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
-    # ignore - no longer supported by newer kvm
-    # push @$cmd, '-tdf' if $tdf;
 
     my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
+    my $useLocaltime = $conf->{localtime};
 
     if (my $ost = $conf->{ostype}) {
-       # other, wxp, w2k, w2k3, w2k8, wvista, win7, l24, l26
+       # other, wxp, w2k, w2k3, w2k8, wvista, win7, win8, l24, l26
 
        if ($ost =~ m/^w/) { # windows
-           push @$cmd, '-localtime' if !defined($conf->{localtime});
+           $useLocaltime = 1 if !defined($conf->{localtime});
 
-           # use rtc-td-hack when acpi is enabled
+           # use time drift fix when acpi is enabled
            if (!(defined($conf->{acpi}) && $conf->{acpi} == 0)) {
-               push @$cmd, '-rtc-td-hack';
+               $tdf = 1 if !defined($conf->{tdf});
            }
        }
 
-       if ($ost eq 'win7' || $ost eq 'w2k8' || $ost eq 'wvista') {
-           push @$cmd, '-no-kvm-pit-reinjection';
+       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';
        }
-
-       # -tdf ?
-       # -no-acpi
-       # -no-kvm
-       # -win2k-hack ?
     }
 
+    push @$rtcFlags, 'driftfix=slew' if $tdf;
+
     if ($nokvm) {
-       push @$cmd, '-no-kvm';
+       push @$machineFlags, 'accel=tcg';
     } else {
        die "No accelerator found!\n" if !$cpuinfo->{hvm};
     }
 
-    push @$cmd, '-localtime' if $conf->{localtime};
-
-    push @$cmd, '-startdate', $conf->{startdate} if $conf->{startdate};
+    if ($conf->{startdate}) {
+       push @$rtcFlags, "base=$conf->{startdate}";
+    } elsif ($useLocaltime) {
+       push @$rtcFlags, 'base=localtime';
+    }
 
     push @$cmd, '-S' if $conf->{freeze};
 
@@ -2222,20 +2335,33 @@ sub config_to_command {
     #my $soundhw = $conf->{soundhw} || $defaults->{soundhw};
     #push @$cmd, '-soundhw', 'es1370';
     #push @$cmd, '-soundhw', $soundhw if $soundhw;
-    $pciaddr = print_pci_addr("balloon0");
-    push @$cmd, '-device', "virtio-balloon-pci,id=balloon0$pciaddr" if $conf->{balloon};
+
+    if($conf->{agent}) {
+       my $qgasocket = qga_socket($vmid);
+       my $pciaddr = print_pci_addr("qga0", $bridges);
+       push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0";
+       push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
+       push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
+    }
+
+    # enable balloon by default, unless explicitly disabled
+    if (!defined($conf->{balloon}) || $conf->{balloon}) {
+       $pciaddr = print_pci_addr("balloon0", $bridges);
+       push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr";
+    }
 
     if ($conf->{watchdog}) {
        my $wdopts = parse_watchdog($conf->{watchdog});
-       $pciaddr = print_pci_addr("watchdog");
+       $pciaddr = print_pci_addr("watchdog", $bridges);
        my $watchdog = $wdopts->{model} || 'i6300esb';
-       push @$cmd, '-device', "$watchdog$pciaddr";
-       push @$cmd, '-watchdog-action', $wdopts->{action} if $wdopts->{action};
+       push @$devices, '-device', "$watchdog$pciaddr";
+       push @$devices, '-watchdog-action', $wdopts->{action} if $wdopts->{action};
     }
 
     my $vollist = [];
     my $scsicontroller = {};
     my $ahcicontroller = {};
+    my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : $defaults->{scsihw};
 
     foreach_drive($conf, sub {
        my ($ds, $drive) = @_;
@@ -2259,22 +2385,23 @@ sub config_to_command {
        }
 
         if ($drive->{interface} eq 'scsi') {
-           my $maxdev = 7;
-           my $controller = int($drive->{index} / $maxdev);
-           $pciaddr = print_pci_addr("lsi$controller");
-           push @$cmd, '-device', "lsi,id=lsi$controller$pciaddr" if !$scsicontroller->{$controller};
-           $scsicontroller->{$controller}=1;
+
+           my $maxdev = ($scsihw ne 'lsi') ? 256 : 7;
+           my $controller = int($drive->{index} / $maxdev);
+           $pciaddr = print_pci_addr("scsihw$controller", $bridges);
+           push @$devices, '-device', "$scsihw,id=scsihw$controller$pciaddr" if !$scsicontroller->{$controller};
+           $scsicontroller->{$controller}=1;
         }
 
         if ($drive->{interface} eq 'sata') {
            my $controller = int($drive->{index} / $MAX_SATA_DISKS);
-           $pciaddr = print_pci_addr("ahci$controller");
-           push @$cmd, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
+           $pciaddr = print_pci_addr("ahci$controller", $bridges);
+           push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
            $ahcicontroller->{$controller}=1;
         }
 
-       push @$cmd, '-drive',print_drive_full($storecfg, $vmid, $drive);
-       push @$cmd, '-device',print_drivedevice_full($storecfg,$vmid, $drive);
+       push @$devices, '-drive',print_drive_full($storecfg, $vmid, $drive);
+       push @$devices, '-device',print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
     });
 
     push @$cmd, '-m', $conf->{memory} || $defaults->{memory};
@@ -2292,10 +2419,16 @@ sub config_to_command {
          }
 
          my $netdevfull = print_netdev_full($vmid,$conf,$d,"net$i");
-         push @$cmd, '-netdev', $netdevfull;
+         push @$devices, '-netdev', $netdevfull;
+
+         my $netdevicefull = print_netdevice_full($vmid,$conf,$d,"net$i",$bridges);
+         push @$devices, '-device', $netdevicefull;
+    }
 
-         my $netdevicefull = print_netdevice_full($vmid,$conf,$d,"net$i");
-         push @$cmd, '-device', $netdevicefull;
+    #bridges
+    while (my ($k, $v) = each %$bridges) {
+       $pciaddr = print_pci_addr("pci.$k");
+       unshift @$devices, '-device', "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr" if $k > 0;
     }
 
 
@@ -2318,6 +2451,14 @@ sub config_to_command {
        push @$cmd, @$aa;
     }
 
+    push @$cmd, @$devices;
+    push @$cmd, '-rtc', join(',', @$rtcFlags) 
+       if scalar(@$rtcFlags);
+    push @$cmd, '-machine', join(',', @$machineFlags) 
+       if scalar(@$machineFlags);
+    push @$cmd, '-global', join(',', @$globalFlags)
+       if scalar(@$globalFlags);
+
     return wantarray ? ($cmd, $vollist) : $cmd;
 }
 
@@ -2326,14 +2467,14 @@ sub vnc_socket {
     return "${var_run_tmpdir}/$vmid.vnc";
 }
 
-sub monitor_socket {
+sub qmp_socket {
     my ($vmid) = @_;
-    return "${var_run_tmpdir}/$vmid.mon";
+    return "${var_run_tmpdir}/$vmid.qmp";
 }
 
-sub qmp_socket {
+sub qga_socket {
     my ($vmid) = @_;
-    return "${var_run_tmpdir}/$vmid.qmp";
+    return "${var_run_tmpdir}/$vmid.qga";
 }
 
 sub pidfile_name {
@@ -2363,24 +2504,13 @@ sub next_migrate_port {
 sub vm_devices_list {
     my ($vmid) = @_;
 
-    my $res = vm_monitor_command ($vmid, "info pci");
-
-    my @lines = split ("\n", $res);
-    my $devices;
-    my $bus;
-    my $addr;
-    my $id;
+    my $res = vm_mon_cmd($vmid, 'query-pci');
 
-    foreach my $line (@lines) {
-       $line =~ s/^\s+//;
-       if ($line =~ m/^Bus  (\d+), device   (\d+), function (\d+):$/) {
-           $bus=$1;
-           $addr=$2;
-       }
-       if ($line =~ m/^id "([a-z][a-z_\-]*\d*)"$/) {
-            $id=$1;
-            $devices->{$id}->{bus}=$bus;
-            $devices->{$id}->{addr}=$addr;
+    my $devices = {};
+    foreach my $pcibus (@$res) {
+       foreach my $device (@{$pcibus->{devices}}) {
+           next if !$device->{'qdev_id'};
+           $devices->{$device->{'qdev_id'}} = $device;
        }
     }
 
@@ -2395,9 +2525,11 @@ sub vm_deviceplug {
     my $devices_list = vm_devices_list($vmid);
     return 1 if defined($devices_list->{$deviceid});
 
+    qemu_bridgeadd($storecfg, $conf, $vmid, $deviceid); #add bridge if we need it for the device
+
     if ($deviceid =~ m/^(virtio)(\d+)$/) {
         return undef if !qemu_driveadd($storecfg, $vmid, $device);
-        my $devicefull = print_drivedevice_full($storecfg, $vmid, $device);
+        my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device);
         qemu_deviceadd($vmid, $devicefull);
         if(!qemu_deviceaddverify($vmid, $deviceid)) {
            qemu_drivedel($vmid, $deviceid);
@@ -2405,17 +2537,19 @@ sub vm_deviceplug {
         }
     }
 
-    if ($deviceid =~ m/^(lsi)(\d+)$/) {
+    if ($deviceid =~ m/^(scsihw)(\d+)$/) {
+        my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : "lsi";
         my $pciaddr = print_pci_addr($deviceid);
-        my $devicefull = "lsi,id=$deviceid$pciaddr";
+        my $devicefull = "$scsihw,id=$deviceid$pciaddr";
         qemu_deviceadd($vmid, $devicefull);
         return undef if(!qemu_deviceaddverify($vmid, $deviceid));
     }
 
     if ($deviceid =~ m/^(scsi)(\d+)$/) {
-        return undef if !qemu_findorcreatelsi($storecfg,$conf, $vmid, $device);
+        return 1 if ($conf->{scsihw} && $conf->{scsihw} ne 'lsi'); #virtio-scsi not yet support hotplug
+        return undef if !qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device);
         return undef if !qemu_driveadd($storecfg, $vmid, $device);
-        my $devicefull = print_drivedevice_full($storecfg, $vmid, $device);
+        my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device);
         if(!qemu_deviceadd($vmid, $devicefull)) {
            qemu_drivedel($vmid, $deviceid);
            return undef;
@@ -2432,6 +2566,14 @@ sub vm_deviceplug {
         }
     }
 
+    if ($deviceid =~ m/^(pci\.)(\d+)$/) {
+       my $bridgeid = $2;
+       my $pciaddr = print_pci_addr($deviceid);
+       my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
+       qemu_deviceadd($vmid, $devicefull);
+       return undef if !qemu_deviceaddverify($vmid, $deviceid);
+    }
+
     return 1;
 }
 
@@ -2472,7 +2614,7 @@ sub vm_deviceunplug {
 sub qemu_deviceadd {
     my ($vmid, $devicefull) = @_;
 
-    my $ret = vm_monitor_command($vmid, "device_add $devicefull");
+    my $ret = vm_human_monitor_command($vmid, "device_add $devicefull");
     $ret =~ s/^\s+//;
     # Otherwise, if the command succeeds, no output is sent. So any non-empty string shows an error
     return 1 if $ret eq "";
@@ -2484,7 +2626,7 @@ sub qemu_deviceadd {
 sub qemu_devicedel {
     my($vmid, $deviceid) = @_;
 
-    my $ret = vm_monitor_command($vmid, "device_del $deviceid");
+    my $ret = vm_human_monitor_command($vmid, "device_del $deviceid");
     $ret =~ s/^\s+//;
     return 1 if $ret eq "";
     syslog("err", "detaching device $deviceid failed : $ret");
@@ -2495,7 +2637,7 @@ sub qemu_driveadd {
     my($storecfg, $vmid, $device) = @_;
 
     my $drive = print_drive_full($storecfg, $vmid, $device);
-    my $ret = vm_monitor_command($vmid, "drive_add auto $drive");
+    my $ret = vm_human_monitor_command($vmid, "drive_add auto $drive");
     # If the command succeeds qemu prints: "OK"
     if ($ret !~ m/OK/s) {
         syslog("err", "adding drive failed: $ret");
@@ -2507,7 +2649,7 @@ sub qemu_driveadd {
 sub qemu_drivedel {
     my($vmid, $deviceid) = @_;
 
-    my $ret = vm_monitor_command($vmid, "drive_del drive-$deviceid");
+    my $ret = vm_human_monitor_command($vmid, "drive_del drive-$deviceid");
     $ret =~ s/^\s+//;
     if ($ret =~ m/Device \'.*?\' not found/s) {
         # NB: device not found errors mean the drive was auto-deleted and we ignore the error
@@ -2545,16 +2687,36 @@ sub qemu_devicedelverify {
     return undef;
 }
 
-sub qemu_findorcreatelsi {
+sub qemu_findorcreatescsihw {
     my ($storecfg, $conf, $vmid, $device) = @_;
 
-    my $maxdev = 7;
+    my $maxdev = ($conf->{scsihw} && $conf->{scsihw} ne 'lsi') ? 256 : 7;
     my $controller = int($device->{index} / $maxdev);
-    my $lsiid="lsi$controller";
+    my $scsihwid="scsihw$controller";
+    my $devices_list = vm_devices_list($vmid);
+
+    if(!defined($devices_list->{$scsihwid})) {
+       return undef if !vm_deviceplug($storecfg, $conf, $vmid, $scsihwid);
+    }
+    return 1;
+}
+
+sub qemu_bridgeadd {
+    my ($storecfg, $conf, $vmid, $device) = @_;
+
+    my $bridges = {};
+    my $bridgeid = undef;
+    print_pci_addr($device, $bridges);
+
+    while (my ($k, $v) = each %$bridges) {
+       $bridgeid = $k;
+    }
+    return if $bridgeid < 1;
+    my $bridge = "pci.$bridgeid";
     my $devices_list = vm_devices_list($vmid);
 
-    if(!defined($devices_list->{$lsiid})) {
-       return undef if !vm_deviceplug($storecfg, $conf, $vmid, $lsiid);
+    if(!defined($devices_list->{$bridge})) {
+       return undef if !vm_deviceplug($storecfg, $conf, $vmid, $bridge);
     }
     return 1;
 }
@@ -2563,10 +2725,10 @@ sub qemu_netdevadd {
     my ($vmid, $conf, $device, $deviceid) = @_;
 
     my $netdev = print_netdev_full($vmid, $conf, $device, $deviceid);
-    my $ret = vm_monitor_command($vmid, "netdev_add $netdev");
+    my $ret = vm_human_monitor_command($vmid, "netdev_add $netdev");
     $ret =~ s/^\s+//;
 
-    #if the command succeeds, no output is sent. So any non-empty string shows an error 
+    #if the command succeeds, no output is sent. So any non-empty string shows an error
     return 1 if $ret eq "";
     syslog("err", "adding netdev failed: $ret");
     return undef;
@@ -2575,9 +2737,9 @@ sub qemu_netdevadd {
 sub qemu_netdevdel {
     my ($vmid, $deviceid) = @_;
 
-    my $ret = vm_monitor_command($vmid, "netdev_del $deviceid");
+    my $ret = vm_human_monitor_command($vmid, "netdev_del $deviceid");
     $ret =~ s/^\s+//;
-    #if the command succeeds, no output is sent. So any non-empty string shows an error 
+    #if the command succeeds, no output is sent. So any non-empty string shows an error
     return 1 if $ret eq "";
     syslog("err", "deleting netdev failed: $ret");
     return undef;
@@ -2586,6 +2748,8 @@ sub qemu_netdevdel {
 sub qemu_block_set_io_throttle {
     my ($vmid, $deviceid, $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr) = @_;
 
+    return if !check_running($vmid) ;
+
     $bps = 0 if !$bps;
     $bps_rd = 0 if !$bps_rd;
     $bps_wr = 0 if !$bps_wr;
@@ -2593,127 +2757,19 @@ sub qemu_block_set_io_throttle {
     $iops_rd = 0 if !$iops_rd;
     $iops_wr = 0 if !$iops_wr;
 
-    my $ret = vm_monitor_command($vmid, "block_set_io_throttle $deviceid $bps $bps_rd $bps_wr $iops $iops_rd $iops_wr");
-    $ret =~ s/^\s+//;
-    return 1 if $ret eq "";
-    syslog("err", "error setting block_set_io_throttle: $ret");
-    return undef;
-}
-
-sub vm_start {
-    my ($storecfg, $vmid, $statefile, $skiplock) = @_;
+    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));
 
-    lock_config($vmid, sub {
-       my $conf = load_config($vmid);
+}
 
-       check_lock($conf) if !$skiplock;
+# old code, only used to shutdown old VM after update
+sub __read_avail {
+    my ($fh, $timeout) = @_;
 
-       die "VM $vmid already running\n" if check_running($vmid);
+    my $sel = new IO::Select;
+    $sel->add($fh);
 
-       my $migrate_uri;
-       my $migrate_port = 0;
-
-       if ($statefile) {
-           if ($statefile eq 'tcp') {
-               $migrate_port = next_migrate_port();
-               $migrate_uri = "tcp:localhost:${migrate_port}";
-           } else {
-               if (-f $statefile) {
-                   $migrate_uri = "exec:cat $statefile";
-               } else {
-                   warn "state file '$statefile' does not exist - doing normal startup\n";
-               }
-           }
-       }
-
-       my $defaults = load_defaults();
-
-       my ($cmd, $vollist) = config_to_command($storecfg, $vmid, $conf, $defaults, $migrate_uri);
-       # host pci devices
-        for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++)  {
-          my $d = parse_hostpci($conf->{"hostpci$i"});
-          next if !$d;
-          my $info = pci_device_info("0000:$d->{pciid}");
-          die "IOMMU not present\n" if !check_iommu_support();
-          die "no pci device info for device '$d->{pciid}'\n" if !$info;
-          die "can't unbind pci device '$d->{pciid}'\n" if !pci_dev_bind_to_stub($info);
-          die "can't reset pci device '$d->{pciid}'\n" if !pci_dev_reset($info);
-        }
-
-       PVE::Storage::activate_volumes($storecfg, $vollist);
-
-       eval  { run_command($cmd, timeout => $migrate_uri ? undef : 30); };
-       my $err = $@;
-       die "start failed: $err" if $err;
-
-       if ($statefile) {
-
-           if ($statefile eq 'tcp') {
-               print "migration listens on port $migrate_port\n";
-           } else {
-               unlink $statefile;
-               # fixme: send resume - is that necessary ?
-               eval { vm_monitor_command($vmid, "cont"); };
-           }
-       }
-
-       # always set migrate speed (overwrite kvm default of 32m)
-       # we set a very hight default of 8192m which is basically unlimited
-       my $migrate_speed = $defaults->{migrate_speed} || 8192;
-       $migrate_speed = $conf->{migrate_speed} || $migrate_speed;
-       eval {
-           my $cmd = "migrate_set_speed ${migrate_speed}m";
-           vm_monitor_command($vmid, $cmd);
-       };
-
-       my $migrate_downtime = $defaults->{migrate_downtime};
-       $migrate_downtime = $conf->{migrate_downtime} if defined($conf->{migrate_downtime});
-       if (defined($migrate_downtime)) {
-           my $cmd = "migrate_set_downtime ${migrate_downtime}";
-           eval { vm_monitor_command($vmid, $cmd); };
-       }
-
-       vm_balloonset($vmid, $conf->{balloon}) if $conf->{balloon};
-
-    });
-}
-
-sub qmp__read_avail {
-    my ($fh, $timeout) = @_;
-
-    my $sel = new IO::Select;
-    $sel->add($fh);
-
-    my $res = '';
-    my $buf;
-
-    my @ready;
-    while (scalar (@ready = $sel->can_read($timeout))) {
-       my $count;
-       if ($count = $fh->sysread($buf, 8192)) {
-               $res .= $buf;
-               last;
-       } else {
-           if (!defined($count)) {
-               die "$!\n";
-           }
-           last;
-       }
-    }
-
-    die "qmp read timeout\n" if !scalar(@ready);
-    my $obj = from_json($res);
-    return $obj;
-}
-
-sub __read_avail {
-    my ($fh, $timeout) = @_;
-
-    my $sel = new IO::Select;
-    $sel->add($fh);
-
-    my $res = '';
-    my $buf;
+    my $res = '';
+    my $buf;
 
     my @ready;
     while (scalar (@ready = $sel->can_read($timeout))) {
@@ -2738,6 +2794,7 @@ sub __read_avail {
     return $res;
 }
 
+# old code, only used to shutdown old VM after update
 sub vm_monitor_command {
     my ($vmid, $cmdstr, $nocheck) = @_;
 
@@ -2746,7 +2803,7 @@ sub vm_monitor_command {
     eval {
        die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
 
-       my $sname = monitor_socket($vmid);
+       my $sname = "${var_run_tmpdir}/$vmid.mon";
 
        my $sock = IO::Socket::UNIX->new( Peer => $sname ) ||
            die "unable to connect to VM $vmid socket - $!\n";
@@ -2812,88 +2869,189 @@ sub vm_monitor_command {
     return $res;
 }
 
-sub vm_qmp_command {
-    my ($vmid, $cmdstr, $nocheck) = @_;
-    #http://git.qemu.org/?p=qemu.git;a=blob;f=qmp-commands.hx;h=db980fa811325aeca8ad43472ba468702d4a25a2;hb=HEAD
-    my $res;
+sub qemu_block_resize {
+    my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
 
-    eval {
-       die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
+    my $running = PVE::QemuServer::check_running($vmid);
 
-       my $sname = qmp_socket($vmid);
-       my $sock = IO::Socket::UNIX->new( Peer => $sname ) ||
-            die "unable to connect to VM $vmid socket - $!\n";
+    return if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
 
+    return if !$running;
 
-       my $timeout = 3;
-        
-       # maybe this works with qmp, need to be tested 
+    vm_mon_cmd($vmid, "block_resize", device => $deviceid, size => int($size));
 
-       # hack: migrate sometime blocks the monitor (when migrate_downtime
-       # is set)
-       #if ($cmdstr =~ m/^(info\s+migrate|migrate\s)/) {
-       #    $timeout = 60*60; # 1 hour
-       #}
+}
 
-       # read banner;
-       my $data = qmp__read_avail($sock, $timeout);
-       # '{"QMP": {"version": {"qemu": {"micro": 93, "minor": 0, "major": 1}, "package": " (qemu-kvm-devel)"}, "capabilities": []}} ';
-       die "got unexpected qemu qmp banner\n" if !$data->{QMP};
+sub qemu_volume_snapshot {
+    my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
 
-       my $sel = new IO::Select;
-       $sel->add($sock);
+    my $running = PVE::QemuServer::check_running($vmid);
 
-        #negociation
-        my $negociation = '{ "execute": "qmp_capabilities" }';
-        
-        if (!scalar(my @ready = $sel->can_write($timeout))) {
-           die "monitor write error - timeout";
-        }
+    return if !PVE::Storage::volume_snapshot($storecfg, $volid, $snap, $running);
 
-        my $b;
-        if (!($b = $sock->syswrite($negociation)) || ($b != length($negociation))) {
-            die "monitor write error - $!";
-        }
+    return if !$running;
+
+    vm_mon_cmd($vmid, "snapshot-drive", device => $deviceid, name => $snap);
 
-        $res = qmp__read_avail($sock, $timeout);
-        #  res = '{"return": {}}
-        die "qmp negociation error\n" if !$res->{return};
+}
 
+sub qemu_volume_snapshot_delete {
+    my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
 
-       $timeout = 20;
+    my $running = PVE::QemuServer::check_running($vmid);
 
+    return if !PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
 
-       my $fullcmd = undef;    
-       #generate json from hash for complex cmd
-       if (ref($cmdstr) eq "HASH") {
-               $fullcmd = to_json($cmdstr);
+    return if !$running;
 
-               if ($fullcmd->{execute}  =~ m/^(info\s+migrate|migrate\s)/) {
-                     $timeout = 60*60; # 1 hour
-               } elsif ($fullcmd->{execute} =~ m/^(eject|change)/) {
-                     $timeout = 60; # note: cdrom mount command is slow
-               }
+    vm_mon_cmd($vmid, "delete-drive-snapshot", device => $deviceid, name => $snap);
+}
+
+sub qga_freezefs {
+    my ($vmid) = @_;
+
+    #need to impplement call to qemu-ga
+}
+
+sub qga_unfreezefs {
+    my ($vmid) = @_;
+
+    #need to impplement call to qemu-ga
+}
+
+sub vm_start {
+    my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused) = @_;
+
+    lock_config($vmid, sub {
+       my $conf = load_config($vmid, $migratedfrom);
+
+       check_lock($conf) if !$skiplock;
+
+       die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
+
+       my $defaults = load_defaults();
+
+       # 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 $migrate_port = 0;
+
+       if ($statefile) {
+           if ($statefile eq 'tcp') {
+               $migrate_port = next_migrate_port();
+               my $migrate_uri = "tcp:localhost:${migrate_port}";
+               push @$cmd, '-incoming', $migrate_uri;
+               push @$cmd, '-S';
+           } else {
+               push @$cmd, '-loadstate', $statefile;
+           }
+       } elsif ($paused) {
+           push @$cmd, '-S';
        }
-       #execute command for simple action
-       else {
-               $fullcmd = '{ "execute": "'.$cmdstr.'" }';
+
+       # host pci devices
+        for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++)  {
+          my $d = parse_hostpci($conf->{"hostpci$i"});
+          next if !$d;
+          my $info = pci_device_info("0000:$d->{pciid}");
+          die "IOMMU not present\n" if !check_iommu_support();
+          die "no pci device info for device '$d->{pciid}'\n" if !$info;
+          die "can't unbind pci device '$d->{pciid}'\n" if !pci_dev_bind_to_stub($info);
+          die "can't reset pci device '$d->{pciid}'\n" if !pci_dev_reset($info);
+        }
+
+       PVE::Storage::activate_volumes($storecfg, $vollist);
+
+       eval  { run_command($cmd, timeout => $statefile ? undef : 30,
+                   umask => 0077); };
+       my $err = $@;
+       die "start failed: $err" if $err;
+
+       print "migration listens on port $migrate_port\n" if $migrate_port;
+
+       if ($statefile && $statefile ne 'tcp')  {
+           eval { vm_mon_cmd_nocheck($vmid, "cont"); };
+           warn $@ if $@;
        }
-       
-       if (!($b = $sock->syswrite($fullcmd)) || ($b != length($fullcmd))) {
-           die "monitor write error - $!";
+
+       # always set migrate speed (overwrite kvm default of 32m)
+       # we set a very hight default of 8192m which is basically unlimited
+       my $migrate_speed = $defaults->{migrate_speed} || 8192;
+       $migrate_speed = $conf->{migrate_speed} || $migrate_speed;
+       $migrate_speed = $migrate_speed * 1048576;
+       eval {
+           vm_mon_cmd_nocheck($vmid, "migrate_set_speed", value => $migrate_speed);
+       };
+
+       my $migrate_downtime = $defaults->{migrate_downtime};
+       $migrate_downtime = $conf->{migrate_downtime} if defined($conf->{migrate_downtime});
+       if (defined($migrate_downtime)) {
+           eval { vm_mon_cmd_nocheck($vmid, "migrate_set_downtime", value => $migrate_downtime); };
        }
 
-       if (ref($cmdstr) ne "HASH") {
-         return if ($cmdstr eq 'q') || ($cmdstr eq 'quit');
+       if($migratedfrom) {
+           my $capabilities = {};
+           $capabilities->{capability} =  "xbzrle";
+           $capabilities->{state} = JSON::true;
+           eval { PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); };
        }
 
-       $res = qmp__read_avail($sock, $timeout);
+       # fixme: how do we handle that on migration?
 
-    };
+       if (!defined($conf->{balloon}) || $conf->{balloon}) {
+           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 => "stats-polling-interval", 
+                      value => 2);
+       }
+    });
+}
 
-    my $err = $@;
+sub vm_mon_cmd {
+    my ($vmid, $execute, %params) = @_;
 
-    if ($err) {
+    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} && $cmd->{arguments}->{timeout}) {
+       $timeout = $cmd->{arguments}->{timeout};
+       delete $cmd->{arguments}->{timeout};
+    }
+    eval {
+       die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
+       my $sname = PVE::QemuServer::qmp_socket($vmid);
+       if (-e $sname) {
+           my $qmpclient = PVE::QMPClient->new();
+
+           $res = $qmpclient->cmd($vmid, $cmd, $timeout);
+       } elsif (-e "${var_run_tmpdir}/$vmid.mon") {
+           die "can't execute complex command on old monitor - stop/start your vm to fix the problem\n"
+               if scalar(%{$cmd->{arguments}});
+           vm_monitor_command($vmid, $cmd->{execute}, $nocheck);
+       } else {
+           die "unable to open monitor socket\n";
+       }
+    };
+    if (my $err = $@) {
        syslog("err", "VM $vmid qmp command failed - $err");
        die $err;
     }
@@ -2901,6 +3059,19 @@ sub vm_qmp_command {
     return $res;
 }
 
+sub vm_human_monitor_command {
+    my ($vmid, $cmdline) = @_;
+
+    my $res;
+
+    my $cmd = {
+       execute => 'human-monitor-command',
+       arguments => { 'command-line' => $cmdline},
+    };
+
+    return vm_qmp_command($vmid, $cmd);
+}
+
 sub vm_commandline {
     my ($storecfg, $vmid) = @_;
 
@@ -2922,7 +3093,7 @@ sub vm_reset {
 
        check_lock($conf) if !$skiplock;
 
-       vm_monitor_command($vmid, "system_reset");
+       vm_mon_cmd($vmid, "system_reset");
     });
 }
 
@@ -2930,14 +3101,13 @@ sub get_vm_volumes {
     my ($conf) = @_;
 
     my $vollist = [];
-    foreach_drive($conf, sub {
-       my ($ds, $drive) = @_;
+    foreach_volid($conf, sub {
+       my ($volid, $is_cdrom) = @_;
 
-       my ($sid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1);
-       return if !$sid;
+       return if $volid =~ m|^/|;
 
-       my $volid = $drive->{file};
-       return if !$volid || $volid =~ m|^/|;
+       my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
+       return if !$sid;
 
        push @$vollist, $volid;
     });
@@ -2956,7 +3126,7 @@ sub vm_stop_cleanup {
            PVE::Storage::deactivate_volumes($storecfg, $vollist);
        }
 
-       foreach my $ext (qw(mon pid vnc)) {
+       foreach my $ext (qw(mon qmp pid vnc qga)) {
            unlink "/var/run/qemu-server/${vmid}.$ext";
        }
     };
@@ -2967,12 +3137,18 @@ sub vm_stop_cleanup {
 # We need that when migration VMs to other nodes (files already moved)
 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
 sub vm_stop {
-    my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
-
-    $timeout = 60 if !defined($timeout);
+    my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
 
     $force = 1 if !defined($force) && !$shutdown;
 
+    if ($migratedfrom){
+       my $pid = check_running($vmid, $nocheck, $migratedfrom);
+       kill 15, $pid if $pid;
+       my $conf = load_config($vmid, $migratedfrom);
+       vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive);
+       return;
+    }
+
     lock_config($vmid, sub {
 
        my $pid = check_running($vmid, $nocheck);
@@ -2982,13 +3158,20 @@ sub vm_stop {
        if (!$nocheck) {
            $conf = load_config($vmid);
            check_lock($conf) if !$skiplock;
+           if (!defined($timeout) && $shutdown && $conf->{startup}) {
+               my $opts = parse_startup($conf->{startup});
+               $timeout = $opts->{down} if $opts->{down};
+           }
        }
 
+       $timeout = 60 if !defined($timeout);
+
        eval {
            if ($shutdown) {
-               vm_monitor_command($vmid, "system_powerdown", $nocheck);
+               $nocheck ? vm_mon_cmd_nocheck($vmid, "system_powerdown") : vm_mon_cmd($vmid, "system_powerdown");
+
            } else {
-               vm_monitor_command($vmid, "quit", $nocheck);
+               $nocheck ? vm_mon_cmd_nocheck($vmid, "quit") : vm_mon_cmd($vmid, "quit");
            }
        };
        my $err = $@;
@@ -3048,7 +3231,7 @@ sub vm_suspend {
 
        check_lock($conf) if !$skiplock;
 
-       vm_monitor_command($vmid, "stop");
+       vm_mon_cmd($vmid, "stop");
     });
 }
 
@@ -3061,7 +3244,7 @@ sub vm_resume {
 
        check_lock($conf) if !$skiplock;
 
-       vm_monitor_command($vmid, "cont");
+       vm_mon_cmd($vmid, "cont");
     });
 }
 
@@ -3072,7 +3255,8 @@ sub vm_sendkey {
 
        my $conf = load_config($vmid);
 
-       vm_monitor_command($vmid, "sendkey $key");
+       # there is no qmp command, so we use the human monitor command
+       vm_human_monitor_command($vmid, "sendkey $key");
     });
 }
 
@@ -3176,7 +3360,7 @@ sub pci_dev_bind_to_stub {
 }
 
 sub print_pci_addr {
-    my ($id) = @_;
+    my ($id, $bridges) = @_;
 
     my $res = '';
     my $devices = {
@@ -3184,9 +3368,10 @@ sub print_pci_addr {
        #addr2 : first videocard
        balloon0 => { bus => 0, addr => 3 },
        watchdog => { bus => 0, addr => 4 },
-       lsi0 => { bus => 0, addr => 5 },
-       lsi1 => { bus => 0, addr => 6 },
+       scsihw0 => { bus => 0, addr => 5 },
+       scsihw1 => { bus => 0, addr => 6 },
        ahci0 => { bus => 0, addr => 7 },
+       qga0 => { bus => 0, addr => 8 },
        virtio0 => { bus => 0, addr => 10 },
        virtio1 => { bus => 0, addr => 11 },
        virtio2 => { bus => 0, addr => 12 },
@@ -3202,22 +3387,56 @@ sub print_pci_addr {
        net4 => { bus => 0, addr => 22 },
        net5 => { bus => 0, addr => 23 },
        #addr29 : usb-host (pve-usb.cfg)
+       'pci.1' => { bus => 0, addr => 30 },
+       'pci.2' => { bus => 0, addr => 31 },
+       'net6' => { bus => 1, addr => 1 },
+       'net7' => { bus => 1, addr => 2 },
+       'net8' => { bus => 1, addr => 3 },
+       'net9' => { bus => 1, addr => 4 },
+       'net10' => { bus => 1, addr => 5 },
+       'net11' => { bus => 1, addr => 6 },
+       'net12' => { bus => 1, addr => 7 },
+       'net13' => { bus => 1, addr => 8 },
+       'net14' => { bus => 1, addr => 9 },
+       'net15' => { bus => 1, addr => 10 },
+       'net16' => { bus => 1, addr => 11 },
+       'net17' => { bus => 1, addr => 12 },
+       'net18' => { bus => 1, addr => 13 },
+       'net19' => { bus => 1, addr => 14 },
+       'net20' => { bus => 1, addr => 15 },
+       'net21' => { bus => 1, addr => 16 },
+       'net22' => { bus => 1, addr => 17 },
+       'net23' => { bus => 1, addr => 18 },
+       'net24' => { bus => 1, addr => 19 },
+       'net25' => { bus => 1, addr => 20 },
+       'net26' => { bus => 1, addr => 21 },
+       'net27' => { bus => 1, addr => 22 },
+       'net28' => { bus => 1, addr => 23 },
+       'net29' => { bus => 1, addr => 24 },
+       'net30' => { bus => 1, addr => 25 },
+       'net31' => { bus => 1, addr => 26 },
+       'virtio6' => { bus => 2, addr => 1 },
+       'virtio7' => { bus => 2, addr => 2 },
+       'virtio8' => { bus => 2, addr => 3 },
+       'virtio9' => { bus => 2, addr => 4 },
+       'virtio10' => { bus => 2, addr => 5 },
+       'virtio11' => { bus => 2, addr => 6 },
+       'virtio12' => { bus => 2, addr => 7 },
+       'virtio13' => { bus => 2, addr => 8 },
+       'virtio14' => { bus => 2, addr => 9 },
+       'virtio15' => { bus => 2, addr => 10 },
     };
 
     if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) {
           my $addr = sprintf("0x%x", $devices->{$id}->{addr});
-          $res = ",bus=pci.$devices->{$id}->{bus},addr=$addr";
+          my $bus = $devices->{$id}->{bus};
+          $res = ",bus=pci.$bus,addr=$addr";
+          $bridges->{$bus} = 1 if $bridges;
     }
     return $res;
 
 }
 
-sub vm_balloonset {
-    my ($vmid, $value) = @_;
-
-    vm_monitor_command($vmid, "balloon $value");
-}
-
 # vzdump restore implementaion
 
 sub archive_read_firstfile {
@@ -3268,50 +3487,413 @@ sub restore_cleanup {
 sub restore_archive {
     my ($archive, $vmid, $user, $opts) = @_;
 
-    if ($archive ne '-') {
-       my $firstfile = archive_read_firstfile($archive);
-       die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n"
-           if $firstfile ne 'qemu-server.conf';
+    my $format = $opts->{format};
+    my $comp;
+
+    if ($archive =~ m/\.tgz$/ || $archive =~ m/\.tar\.gz$/) {
+       $format = 'tar' if !$format;
+       $comp = 'gzip';
+    } elsif ($archive =~ m/\.tar$/) {
+       $format = 'tar' if !$format;
+    } elsif ($archive =~ m/.tar.lzo$/) {
+       $format = 'tar' if !$format;
+       $comp = 'lzop';
+    } elsif ($archive =~ m/\.vma$/) {
+       $format = 'vma' if !$format;
+    } elsif ($archive =~ m/\.vma\.gz$/) {
+       $format = 'vma' if !$format;
+       $comp = 'gzip';
+    } elsif ($archive =~ m/\.vma\.lzo$/) {
+       $format = 'vma' if !$format;
+       $comp = 'lzop';
+    } else {
+       $format = 'vma' if !$format; # default
     }
 
-    my $tocmd = "/usr/lib/qemu-server/qmextract";
+    # try to detect archive format
+    if ($format eq 'tar') {
+       return restore_tar_archive($archive, $vmid, $user, $opts);
+    } else {
+       return restore_vma_archive($archive, $vmid, $user, $opts, $comp);
+    }
+}
 
-    $tocmd .= " --storage " . PVE::Tools::shellquote($opts->{storage}) if $opts->{storage};
-    $tocmd .= " --pool " . PVE::Tools::shellquote($opts->{pool}) if $opts->{pool};
-    $tocmd .= ' --prealloc' if $opts->{prealloc};
-    $tocmd .= ' --info' if $opts->{info};
+sub restore_update_config_line {
+    my ($outfd, $cookie, $vmid, $map, $line, $unique) = @_;
+
+    return if $line =~ m/^\#qmdump\#/;
+    return if $line =~ m/^\#vzdump\#/;
+    return if $line =~ m/^lock:/;
+    return if $line =~ m/^unused\d+:/;
+    return if $line =~ m/^parent:/;
+
+    if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
+       # try to convert old 1.X settings
+       my ($id, $ind, $ethcfg) = ($1, $2, $3);
+       foreach my $devconfig (PVE::Tools::split_list($ethcfg)) {
+           my ($model, $macaddr) = split(/\=/, $devconfig);
+           $macaddr = PVE::Tools::random_ether_addr() if !$macaddr || $unique;
+           my $net = {
+               model => $model,
+               bridge => "vmbr$ind",
+               macaddr => $macaddr,
+           };
+           my $netstr = print_net($net);
 
-    # tar option "xf" does not autodetect compression when read from STDIN,
-    # so we pipe to zcat
-    my $cmd = "zcat -f|tar xf " . PVE::Tools::shellquote($archive) . " " .
-       PVE::Tools::shellquote("--to-command=$tocmd");
+           print $outfd "net$cookie->{netcount}: $netstr\n";
+           $cookie->{netcount}++;
+       }
+    } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
+       my ($id, $netstr) = ($1, $2);
+       my $net = parse_net($netstr);
+       $net->{macaddr} = PVE::Tools::random_ether_addr() if $net->{macaddr};
+       $netstr = print_net($net);
+       print $outfd "$id: $netstr\n";
+    } elsif ($line =~ m/^((ide|scsi|virtio|sata)\d+):\s*(\S+)\s*$/) {
+       my $virtdev = $1;
+       my $value = $2;
+       if ($line =~ m/backup=no/) {
+           print $outfd "#$line";
+       } elsif ($virtdev && $map->{$virtdev}) {
+           my $di = PVE::QemuServer::parse_drive($virtdev, $value);
+           $di->{file} = $map->{$virtdev};
+           $value = PVE::QemuServer::print_drive($vmid, $di);
+           print $outfd "$virtdev: $value\n";
+       } else {
+           print $outfd $line;
+       }
+    } else {
+       print $outfd $line;
+    }
+}
 
-    my $tmpdir = "/var/tmp/vzdumptmp$$";
-    mkpath $tmpdir;
+sub scan_volids {
+    my ($cfg, $vmid) = @_;
 
-    local $ENV{VZDUMP_TMPDIR} = $tmpdir;
-    local $ENV{VZDUMP_VMID} = $vmid;
-    local $ENV{VZDUMP_USER} = $user;
+    my $info = PVE::Storage::vdisk_list($cfg, undef, $vmid);
 
-    my $conffile = PVE::QemuServer::config_file($vmid);
-    my $tmpfn = "$conffile.$$.tmp";
+    my $volid_hash = {};
+    foreach my $storeid (keys %$info) {
+       foreach my $item (@{$info->{$storeid}}) {
+           next if !($item->{volid} && $item->{size});
+           $volid_hash->{$item->{volid}} = $item;
+       }
+    }
 
-    # disable interrupts (always do cleanups)
-    local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
-       print STDERR "got interrupt - ignored\n";
-    };
+    return $volid_hash;
+}
 
-    eval {
-       # enable interrupts
-       local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
-           die "interrupted by signal\n";
-       };
+sub update_disksize {
+    my ($vmid, $conf, $volid_hash) = @_;
+    my $changes;
 
-       if ($archive eq '-') {
-           print "extracting archive from STDIN\n";
-           run_command($cmd, input => "<&STDIN");
-       } else {
-           print "extracting archive '$archive'\n";
+    my $used = {};
+
+    # update size info
+    foreach my $opt (keys %$conf) {
+       if (PVE::QemuServer::valid_drivename($opt)) {
+           my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt});
+           my $volid = $drive->{file};
+           next if !$volid;
+
+           $used->{$volid} = 1;
+
+           next if PVE::QemuServer::drive_is_cdrom($drive);
+           next if !$volid_hash->{$volid};
+
+           $drive->{size} = $volid_hash->{$volid}->{size};
+           $changes = 1;
+           $conf->{$opt} = PVE::QemuServer::print_drive($vmid, $drive);
+       }
+    }
+
+    foreach my $volid (sort keys %$volid_hash) {
+       next if $volid =~ m/vm-$vmid-state-/;
+       next if $used->{$volid};
+       $changes = 1;
+       PVE::QemuServer::add_unused_volume($conf, $volid);
+    }
+
+    return $changes;
+}
+
+sub rescan {
+    my ($vmid, $nolock) = @_;
+
+    my $cfg = PVE::Cluster::cfs_read_file("storage.cfg");
+
+    my $volid_hash = scan_volids($cfg, $vmid);
+
+    my $updatefn =  sub {
+       my ($vmid) = @_;
+
+       my $conf = PVE::QemuServer::load_config($vmid);
+           
+       PVE::QemuServer::check_lock($conf);
+
+       my $changes = PVE::QemuServer::update_disksize($vmid, $conf, $volid_hash);
+
+       PVE::QemuServer::update_config_nolock($vmid, $conf, 1) if $changes;
+    };
+
+    if (defined($vmid)) {
+       if ($nolock) {
+           &$updatefn($vmid);
+       } else {
+           PVE::QemuServer::lock_config($vmid, $updatefn, $vmid);
+       }
+    } else {
+       my $vmlist = config_list();
+       foreach my $vmid (keys %$vmlist) {
+           if ($nolock) {
+               &$updatefn($vmid);
+           } else {
+               PVE::QemuServer::lock_config($vmid, $updatefn, $vmid);
+           }    
+       }
+    }
+}
+
+sub restore_vma_archive {
+    my ($archive, $vmid, $user, $opts, $comp) = @_;
+
+    my $input = $archive eq '-' ? "<&STDIN" : undef;
+    my $readfrom = $archive;
+
+    my $uncomp = '';
+    if ($comp) {
+       $readfrom = '-';
+       my $qarchive = PVE::Tools::shellquote($archive);
+       if ($comp eq 'gzip') {
+           $uncomp = "zcat $qarchive|";
+       } elsif ($comp eq 'lzop') {
+           $uncomp = "lzop -d -c $qarchive|";
+       } else {
+           die "unknown compression method '$comp'\n";
+       }
+       
+    }
+
+    my $tmpdir = "/var/tmp/vzdumptmp$$";
+    rmtree $tmpdir;
+
+    # disable interrupts (always do cleanups)
+    local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
+       warn "got interrupt - ignored\n";
+    };
+
+    my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
+    POSIX::mkfifo($mapfifo, 0600);
+    my $fifofh;
+
+    my $openfifo = sub {
+       open($fifofh, '>', $mapfifo) || die $!;
+    };
+
+    my $cmd = "${uncomp}vma extract -v -r $mapfifo $readfrom $tmpdir";
+
+    my $oldtimeout;
+    my $timeout = 5;
+
+    my $devinfo = {};
+
+    my $rpcenv = PVE::RPCEnvironment::get();
+
+    my $conffile = PVE::QemuServer::config_file($vmid);
+    my $tmpfn = "$conffile.$$.tmp";
+
+    my $print_devmap = sub {
+       my $virtdev_hash = {};
+
+       my $cfgfn = "$tmpdir/qemu-server.conf";
+
+       # we can read the config - that is already extracted
+       my $fh = IO::File->new($cfgfn, "r") ||
+           "unable to read qemu-server.conf - $!\n";
+
+       while (defined(my $line = <$fh>)) {
+           if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
+               my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
+               die "archive does not contain data for drive '$virtdev'\n"
+                   if !$devinfo->{$devname};
+               if (defined($opts->{storage})) {
+                   $storeid = $opts->{storage} || 'local';
+               } elsif (!$storeid) {
+                   $storeid = 'local';
+               }
+               $format = 'raw' if !$format;
+               $devinfo->{$devname}->{devname} = $devname;
+               $devinfo->{$devname}->{virtdev} = $virtdev;
+               $devinfo->{$devname}->{format} = $format;
+               $devinfo->{$devname}->{storeid} = $storeid;
+
+               # 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']);
+               }
+
+               $virtdev_hash->{$virtdev} = $devinfo->{$devname};
+           }
+       }
+
+       foreach my $devname (keys %$devinfo) {
+           die "found no device mapping information for device '$devname'\n" 
+               if !$devinfo->{$devname}->{virtdev};        
+       }
+
+       my $map = {};
+       my $cfg = cfs_read_file('storage.cfg');
+       foreach my $virtdev (sort keys %$virtdev_hash) {
+           my $d = $virtdev_hash->{$virtdev};
+           my $alloc_size = int(($d->{size} + 1024 - 1)/1024);
+           my $scfg = PVE::Storage::storage_config($cfg, $d->{storeid});
+           my $volid = PVE::Storage::vdisk_alloc($cfg, $d->{storeid}, $vmid,
+                                                 $d->{format}, undef, $alloc_size);
+           print STDERR "new volume ID is '$volid'\n";
+           $d->{volid} = $volid;
+           my $path = PVE::Storage::path($cfg, $volid);
+
+           my $write_zeros = 1;
+           # fixme: what other storages types initialize volumes with zero?
+           if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') {
+               $write_zeros = 0;
+           }
+
+           print $fifofh "${write_zeros}:$d->{devname}=$path\n";
+
+           print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
+           $map->{$virtdev} = $volid;
+       }
+
+       $fh->seek(0, 0) || die "seek failed - $!\n";
+
+       my $outfd = new IO::File ($tmpfn, "w") ||
+           die "unable to write config for VM $vmid\n";
+
+       my $cookie = { netcount => 0 };
+       while (defined(my $line = <$fh>)) {
+           restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});  
+       }
+
+       $fh->close();
+       $outfd->close();
+    };
+
+    eval {
+       # enable interrupts
+       local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
+           die "interrupted by signal\n";
+       };
+       local $SIG{ALRM} = sub { die "got timeout\n"; };
+
+       $oldtimeout = alarm($timeout);
+
+       my $parser = sub {
+           my $line = shift;
+
+           print "$line\n";
+
+           if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
+               my ($dev_id, $size, $devname) = ($1, $2, $3);
+               $devinfo->{$devname} = { size => $size, dev_id => $dev_id };
+           } elsif ($line =~ m/^CTIME: /) {
+               &$print_devmap();
+               print $fifofh "done\n";
+               my $tmp = $oldtimeout || 0;
+               $oldtimeout = undef;
+               alarm($tmp);
+               close($fifofh);
+           }
+       };
+       print "restore vma archive: $cmd\n";
+       run_command($cmd, input => $input, outfunc => $parser, afterfork => $openfifo);
+    };
+    my $err = $@;
+
+    alarm($oldtimeout) if $oldtimeout;
+
+    unlink $mapfifo;
+
+    if ($err) {
+       rmtree $tmpdir;
+       unlink $tmpfn;
+
+       my $cfg = cfs_read_file('storage.cfg');
+       foreach my $devname (keys %$devinfo) {
+           my $volid = $devinfo->{$devname}->{volid};
+           next if !$volid;
+           eval {
+               if ($volid =~ m|^/|) {
+                   unlink $volid || die 'unlink failed\n';
+               } else {
+                   PVE::Storage::vdisk_free($cfg, $volid);
+               }
+               print STDERR "temporary volume '$volid' sucessfuly removed\n";
+           };
+           print STDERR "unable to cleanup '$volid' - $@" if $@;
+       }
+       die $err;
+    }
+
+    rmtree $tmpdir;
+    
+    rename $tmpfn, $conffile ||
+       die "unable to commit configuration file '$conffile'\n";
+
+    eval { rescan($vmid, 1); };
+    warn $@ if $@;
+}
+
+sub restore_tar_archive {
+    my ($archive, $vmid, $user, $opts) = @_;
+
+    if ($archive ne '-') {
+       my $firstfile = archive_read_firstfile($archive);
+       die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n"
+           if $firstfile ne 'qemu-server.conf';
+    }
+
+    my $tocmd = "/usr/lib/qemu-server/qmextract";
+
+    $tocmd .= " --storage " . PVE::Tools::shellquote($opts->{storage}) if $opts->{storage};
+    $tocmd .= " --pool " . PVE::Tools::shellquote($opts->{pool}) if $opts->{pool};
+    $tocmd .= ' --prealloc' if $opts->{prealloc};
+    $tocmd .= ' --info' if $opts->{info};
+
+    # tar option "xf" does not autodetect compression when read from STDIN,
+    # so we pipe to zcat
+    my $cmd = "zcat -f|tar xf " . PVE::Tools::shellquote($archive) . " " .
+       PVE::Tools::shellquote("--to-command=$tocmd");
+
+    my $tmpdir = "/var/tmp/vzdumptmp$$";
+    mkpath $tmpdir;
+
+    local $ENV{VZDUMP_TMPDIR} = $tmpdir;
+    local $ENV{VZDUMP_VMID} = $vmid;
+    local $ENV{VZDUMP_USER} = $user;
+
+    my $conffile = PVE::QemuServer::config_file($vmid);
+    my $tmpfn = "$conffile.$$.tmp";
+
+    # disable interrupts (always do cleanups)
+    local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
+       print STDERR "got interrupt - ignored\n";
+    };
+
+    eval {
+       # enable interrupts
+       local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
+           die "interrupted by signal\n";
+       };
+
+       if ($archive eq '-') {
+           print "extracting archive from STDIN\n";
+           run_command($cmd, input => "<&STDIN");
+       } else {
+           print "extracting archive '$archive'\n";
            run_command($cmd);
        }
 
@@ -3339,50 +3921,9 @@ sub restore_archive {
        my $outfd = new IO::File ($tmpfn, "w") ||
            die "unable to write config for VM $vmid\n";
 
-       my $netcount = 0;
-
+       my $cookie = { netcount => 0 };
        while (defined (my $line = <$srcfd>)) {
-           next if $line =~ m/^\#vzdump\#/;
-           next if $line =~ m/^lock:/;
-           next if $line =~ m/^unused\d+:/;
-
-           if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
-               # try to convert old 1.X settings
-               my ($id, $ind, $ethcfg) = ($1, $2, $3);
-               foreach my $devconfig (PVE::Tools::split_list($ethcfg)) {
-                   my ($model, $macaddr) = split(/\=/, $devconfig);
-                   $macaddr = PVE::Tools::random_ether_addr() if !$macaddr || $opts->{unique};
-                   my $net = {
-                       model => $model,
-                       bridge => "vmbr$ind",
-                       macaddr => $macaddr,
-                   };
-                   my $netstr = print_net($net);
-                   print $outfd "net${netcount}: $netstr\n";
-                   $netcount++;
-               }
-           } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && ($opts->{unique})) {
-               my ($id, $netstr) = ($1, $2);
-               my $net = parse_net($netstr);
-               $net->{macaddr} = PVE::Tools::random_ether_addr() if $net->{macaddr};
-               $netstr = print_net($net);
-               print $outfd "$id: $netstr\n";
-           } elsif ($line =~ m/^((ide|scsi|virtio)\d+):\s*(\S+)\s*$/) {
-               my $virtdev = $1;
-               my $value = $2;
-               if ($line =~ m/backup=no/) {
-                   print $outfd "#$line";
-               } elsif ($virtdev && $map->{$virtdev}) {
-                   my $di = PVE::QemuServer::parse_drive($virtdev, $value);
-                   $di->{file} = $map->{$virtdev};
-                   $value = PVE::QemuServer::print_drive($vmid, $di);
-                   print $outfd "$virtdev: $value\n";
-               } else {
-                   print $outfd $line;
-               }
-           } else {
-               print $outfd $line;
-           }
+           restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});  
        }
 
        $srcfd->close();
@@ -3403,6 +3944,455 @@ sub restore_archive {
 
     rename $tmpfn, $conffile ||
        die "unable to commit configuration file '$conffile'\n";
+
+    eval { rescan($vmid, 1); };
+    warn $@ if $@;
+};
+
+
+# Internal snapshots
+
+# NOTE: Snapshot create/delete involves several non-atomic
+# action, and can take a long time.
+# So we try to avoid locking the file and use 'lock' variable
+# inside the config file instead.
+
+my $snapshot_copy_config = sub {
+    my ($source, $dest) = @_;
+
+    foreach my $k (keys %$source) {
+       next if $k eq 'snapshots';
+       next if $k eq 'snapstate';
+       next if $k eq 'snaptime';
+       next if $k eq 'vmstate';
+       next if $k eq 'lock';
+       next if $k eq 'digest';
+       next if $k eq 'description';
+       next if $k =~ m/^unused\d+$/;
+               
+       $dest->{$k} = $source->{$k};
+    }
+};
+
+my $snapshot_apply_config = sub {
+    my ($conf, $snap) = @_;
+
+    # copy snapshot list
+    my $newconf = {
+       snapshots => $conf->{snapshots},
+    };
+
+    # keep description and list of unused disks
+    foreach my $k (keys %$conf) {
+       next if !($k =~ m/^unused\d+$/ || $k eq 'description');
+       $newconf->{$k} = $conf->{$k};
+    }
+
+    &$snapshot_copy_config($snap, $newconf);
+
+    return $newconf;
+};
+
+sub foreach_writable_storage {
+    my ($conf, $func) = @_;
+
+    my $sidhash = {};
+
+    foreach my $ds (keys %$conf) {
+       next if !valid_drivename($ds);
+
+       my $drive = parse_drive($ds, $conf->{$ds});
+       next if !$drive;
+       next if drive_is_cdrom($drive);
+
+       my $volid = $drive->{file};
+
+       my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
+       $sidhash->{$sid} = $sid if $sid;        
+    }
+
+    foreach my $sid (sort keys %$sidhash) {
+       &$func($sid);
+    }
+}
+
+my $alloc_vmstate_volid = sub {
+    my ($storecfg, $vmid, $conf, $snapname) = @_;
+    
+    # Note: we try to be smart when selecting a $target storage
+
+    my $target;
+
+    # search shared storage first
+    foreach_writable_storage($conf, sub {
+       my ($sid) = @_;
+       my $scfg = PVE::Storage::storage_config($storecfg, $sid);
+       return if !$scfg->{shared};
+
+       $target = $sid if !$target || $scfg->{path}; # prefer file based storage
+    });
+
+    if (!$target) {
+       # now search local storage
+       foreach_writable_storage($conf, sub {
+           my ($sid) = @_;
+           my $scfg = PVE::Storage::storage_config($storecfg, $sid);
+           return if $scfg->{shared};
+
+           $target = $sid if !$target || $scfg->{path}; # prefer file based storage;
+       });
+    }
+
+    $target = 'local' if !$target;
+
+    my $driver_state_size = 500; # assume 32MB is enough to safe all driver state;
+    # we abort live save after $conf->{memory}, so we need at max twice that space
+    my $size = $conf->{memory}*2 + $driver_state_size;
+
+    my $name = "vm-$vmid-state-$snapname";
+    my $scfg = PVE::Storage::storage_config($storecfg, $target);
+    $name .= ".raw" if $scfg->{path}; # add filename extension for file base storage
+    my $volid = PVE::Storage::vdisk_alloc($storecfg, $target, $vmid, 'raw', $name, $size*1024);
+
+    return $volid;
 };
 
+my $snapshot_prepare = sub {
+    my ($vmid, $snapname, $save_vmstate, $comment) = @_;
+
+    my $snap;
+
+    my $updatefn =  sub {
+
+       my $conf = load_config($vmid);
+
+       check_lock($conf);
+
+       $conf->{lock} = 'snapshot';
+
+       die "snapshot name '$snapname' already used\n" 
+           if defined($conf->{snapshots}->{$snapname}); 
+
+       my $storecfg = PVE::Storage::config();
+
+       foreach_drive($conf, sub {
+           my ($ds, $drive) = @_;
+
+           return if drive_is_cdrom($drive);
+           my $volid = $drive->{file};
+
+           my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
+           if ($storeid) {
+               my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+               die "can't snapshot volume '$volid'\n"          
+                   if !(($scfg->{path} && $volname =~ m/\.qcow2$/) ||
+                        ($scfg->{type} eq 'nexenta') || 
+                        ($scfg->{type} eq 'rbd') || 
+                        ($scfg->{type} eq 'sheepdog'));
+           } elsif ($volid =~ m|^(/.+)$| && -e $volid) {
+               die "snapshot device '$volid' is not possible\n";
+           } else {
+               die "can't snapshot volume '$volid'\n";
+           }
+       });
+
+
+       $snap = $conf->{snapshots}->{$snapname} = {};
+
+       if ($save_vmstate && check_running($vmid)) {
+           $snap->{vmstate} = &$alloc_vmstate_volid($storecfg, $vmid, $conf, $snapname);
+       }
+
+       &$snapshot_copy_config($conf, $snap);
+
+       $snap->{snapstate} = "prepare";
+       $snap->{snaptime} = time();
+       $snap->{description} = $comment if $comment;
+
+       update_config_nolock($vmid, $conf, 1);
+    };
+
+    lock_config($vmid, $updatefn);
+
+    return $snap;
+};
+
+my $snapshot_commit = sub {
+    my ($vmid, $snapname) = @_;
+
+    my $updatefn = sub {
+
+       my $conf = load_config($vmid);
+
+       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 "wrong snapshot state\n" 
+           if !($snap->{snapstate} && $snap->{snapstate} eq "prepare"); 
+       
+       delete $snap->{snapstate};
+       delete $conf->{lock};
+
+       my $newconf = &$snapshot_apply_config($conf, $snap);
+
+       $newconf->{parent} = $snapname;
+
+       update_config_nolock($vmid, $newconf, 1);
+    };
+
+    lock_config($vmid, $updatefn);
+};
+
+sub snapshot_rollback {
+    my ($vmid, $snapname) = @_;
+
+    my $snap;
+
+    my $prepare = 1;
+
+    my $storecfg = PVE::Storage::config();
+    my $updatefn = sub {
+
+       my $conf = load_config($vmid);
+
+       $snap = $conf->{snapshots}->{$snapname};
+
+       die "snapshot '$snapname' does not exist\n" if !defined($snap); 
+
+       die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n" 
+           if $snap->{snapstate};
+
+       if ($prepare) {
+           check_lock($conf);
+           vm_stop($storecfg, $vmid, undef, undef, 5, undef, undef);
+       }
+
+       die "unable to rollback vm $vmid: vm is running\n"
+           if check_running($vmid);
+
+       if ($prepare) {
+           $conf->{lock} = 'rollback';
+       } else {
+           die "got wrong lock\n" if !($conf->{lock} && $conf->{lock} eq 'rollback');
+           delete $conf->{lock};
+       }
+
+       if (!$prepare) {
+           # copy snapshot config to current config
+           $conf = &$snapshot_apply_config($conf, $snap);
+           $conf->{parent} = $snapname;
+       }
+
+       update_config_nolock($vmid, $conf, 1);
+
+       if (!$prepare && $snap->{vmstate}) {
+           my $statefile = PVE::Storage::path($storecfg, $snap->{vmstate});
+           vm_start($storecfg, $vmid, $statefile);
+       }
+    };
+
+    lock_config($vmid, $updatefn);
+    
+    foreach_drive($snap, sub {
+       my ($ds, $drive) = @_;
+
+       return if drive_is_cdrom($drive);
+
+       my $volid = $drive->{file};
+       my $device = "drive-$ds";
+
+       PVE::Storage::volume_snapshot_rollback($storecfg, $volid, $snapname);
+    });
+
+    $prepare = 0;
+    lock_config($vmid, $updatefn);
+}
+
+my $savevm_wait = sub {
+    my ($vmid) = @_;
+
+    for(;;) {
+       my $stat = PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "query-savevm");
+       if (!$stat->{status}) {
+           die "savevm not active\n";
+       } elsif ($stat->{status} eq 'active') {
+           sleep(1);
+           next;
+       } elsif ($stat->{status} eq 'completed') {
+           last;
+       } else {
+           die "query-savevm returned status '$stat->{status}'\n";
+       }
+    }
+};
+
+sub snapshot_create {
+    my ($vmid, $snapname, $save_vmstate, $freezefs, $comment) = @_;
+
+    my $snap = &$snapshot_prepare($vmid, $snapname, $save_vmstate, $comment);
+
+    $freezefs = $save_vmstate = 0 if !$snap->{vmstate}; # vm is not running
+
+    my $drivehash = {};
+
+    my $running = check_running($vmid);
+
+    eval {
+       # create internal snapshots of all drives
+
+       my $storecfg = PVE::Storage::config();
+
+       if ($running) {
+           if ($snap->{vmstate}) {
+               my $path = PVE::Storage::path($storecfg, $snap->{vmstate});     
+               vm_mon_cmd($vmid, "savevm-start", statefile => $path);
+               &$savevm_wait($vmid);
+           } else {
+               vm_mon_cmd($vmid, "savevm-start");
+           }
+       };
+
+       qga_freezefs($vmid) if $running && $freezefs;
+       foreach_drive($snap, sub {
+           my ($ds, $drive) = @_;
+
+           return if drive_is_cdrom($drive);
+
+           my $volid = $drive->{file};
+           my $device = "drive-$ds";
+
+           qemu_volume_snapshot($vmid, $device, $storecfg, $volid, $snapname);
+           $drivehash->{$ds} = 1;
+       });
+    };
+    my $err = $@;
+
+    eval { gqa_unfreezefs($vmid) if $running && $freezefs; };
+    warn $@ if $@;
+
+    eval { vm_mon_cmd($vmid, "savevm-end") if $running; };
+    warn $@ if $@;
+
+    if ($err) {
+       warn "snapshot create failed: starting cleanup\n";
+       eval { snapshot_delete($vmid, $snapname, 0, $drivehash); };
+       warn $@ if $@;
+       die $err;
+    }
+
+    &$snapshot_commit($vmid, $snapname);
+}
+
+# Note: $drivehash is only set when called from snapshot_create.
+sub snapshot_delete {
+    my ($vmid, $snapname, $force, $drivehash) = @_;
+
+    my $prepare = 1;
+
+    my $snap;
+    my $unused = [];
+
+    my $unlink_parent = sub {
+       my ($confref, $new_parent) = @_;
+
+       if ($confref->{parent} && $confref->{parent} eq $snapname) {
+           if ($new_parent) {
+               $confref->{parent} = $new_parent;
+           } else {
+               delete $confref->{parent};
+           }
+       }
+    };
+    my $updatefn =  sub {
+       my ($remove_drive) = @_;
+
+       my $conf = load_config($vmid);
+
+       check_lock($conf) if !$drivehash;
+
+       $snap = $conf->{snapshots}->{$snapname};
+
+       die "snapshot '$snapname' does not exist\n" if !defined($snap); 
+
+       # remove parent refs
+       &$unlink_parent($conf, $snap->{parent});
+       foreach my $sn (keys %{$conf->{snapshots}}) {
+           next if $sn eq $snapname;
+           &$unlink_parent($conf->{snapshots}->{$sn}, $snap->{parent});
+       }
+
+       if ($remove_drive) {
+           if ($remove_drive eq 'vmstate') {
+               delete $snap->{$remove_drive};
+           } else {
+               my $drive = parse_drive($remove_drive, $snap->{$remove_drive});
+               my $volid = $drive->{file};
+               delete $snap->{$remove_drive};
+               add_unused_volume($conf, $volid);
+           }
+       }
+
+       if ($prepare) {
+           $snap->{snapstate} = 'delete';
+       } else {
+           delete $conf->{snapshots}->{$snapname};
+           delete $conf->{lock} if $drivehash;
+           foreach my $volid (@$unused) {
+               add_unused_volume($conf, $volid);
+           }
+       }
+
+       update_config_nolock($vmid, $conf, 1);
+    };
+
+    lock_config($vmid, $updatefn);
+
+    # now remove vmstate file
+
+    my $storecfg = PVE::Storage::config();
+
+    if ($snap->{vmstate}) {
+       eval {  PVE::Storage::vdisk_free($storecfg, $snap->{vmstate}); };
+       if (my $err = $@) {
+           die $err if !$force;
+           warn $err;
+       }
+       # save changes (remove vmstate from snapshot)
+       lock_config($vmid, $updatefn, 'vmstate') if !$force;
+    };
+
+    # now remove all internal snapshots
+    foreach_drive($snap, sub {
+       my ($ds, $drive) = @_;
+
+       return if drive_is_cdrom($drive);
+
+       my $volid = $drive->{file};
+       my $device = "drive-$ds";
+
+       if (!$drivehash || $drivehash->{$ds}) {
+           eval { qemu_volume_snapshot_delete($vmid, $device, $storecfg, $volid, $snapname); };
+           if (my $err = $@) {
+               die $err if !$force;
+               warn $err;
+           }
+       }
+
+       # save changes (remove drive fron snapshot)
+       lock_config($vmid, $updatefn, $ds) if !$force;
+       push @$unused, $volid;
+    });
+
+    # now cleanup config
+    $prepare = 0;
+    lock_config($vmid, $updatefn);
+}
+
 1;