]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
activate LVM LVs more carefully
[qemu-server.git] / PVE / QemuServer.pm
index 7168c86c8f306b6c7bed7895cca4a3055eb90af3..8bee83e73de4e20ebc52764374e72ff41b3f0a0e 100644 (file)
@@ -24,18 +24,31 @@ use PVE::Tools qw(run_command lock_file file_read_firstline);
 use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
 use PVE::INotify;
 use PVE::ProcFSTools;
-use Time::HiRes qw (gettimeofday);
+use Time::HiRes qw(gettimeofday);
 
-my $clock_ticks = POSIX::sysconf(&POSIX::_SC_CLK_TCK);
+my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
 
-# Note about locking: we use flock on the config file protect 
+# 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
 # allowed when such lock is set. But you can ignore this kind of
 # lock with the --skiplock flag.
 
-cfs_register_file('/qemu-server/', \&parse_vm_config);  
+cfs_register_file('/qemu-server/', \&parse_vm_config);
+
+PVE::JSONSchema::register_standard_option('skiplock', {
+    description => "Ignore locks - only root is allowed to use this option.",
+    type => 'boolean', 
+    optional => 1,
+});
+
+PVE::JSONSchema::register_standard_option('pve-qm-stateuri', {
+    description => "Some command save/restore state from this location.",
+    type => 'string',
+    maxLength => 128,
+    optional => 1,
+});
 
 #no warnings 'redefine';
 
@@ -71,31 +84,31 @@ unless(defined(&_VZSYSCALLS_H_)) {
 sub fairsched_mknod {
     my ($parent, $weight, $desired) = @_;
 
-    return syscall(&__NR_fairsched_mknod, int ($parent), int ($weight), int ($desired));
+    return syscall(&__NR_fairsched_mknod, int($parent), int($weight), int($desired));
 }
 
 sub fairsched_rmnod {
     my ($id) = @_;
 
-    return syscall(&__NR_fairsched_rmnod, int ($id));
+    return syscall(&__NR_fairsched_rmnod, int($id));
 }
 
 sub fairsched_mvpr {
     my ($pid, $newid) = @_;
 
-    return syscall(&__NR_fairsched_mvpr, int ($pid), int ($newid));
+    return syscall(&__NR_fairsched_mvpr, int($pid), int($newid));
 }
 
 sub fairsched_vcpus {
     my ($id, $vcpus) = @_;
 
-    return syscall(&__NR_fairsched_vcpus, int ($id), int ($vcpus));
+    return syscall(&__NR_fairsched_vcpus, int($id), int($vcpus));
 }
 
 sub fairsched_rate {
     my ($id, $op, $rate) = @_;
 
-    return syscall(&__NR_fairsched_rate, int ($id), int ($op), int ($rate));
+    return syscall(&__NR_fairsched_rate, int($id), int($op), int($rate));
 }
 
 use constant FAIRSCHED_SET_RATE  => 0;
@@ -105,10 +118,10 @@ use constant FAIRSCHED_GET_RATE  => 2;
 sub fairsched_cpulimit {
     my ($id, $limit) = @_;
 
-    my $cpulim1024 = int ($limit * 1024 / 100);
+    my $cpulim1024 = int($limit * 1024 / 100);
     my $op = $cpulim1024 ? FAIRSCHED_SET_RATE : FAIRSCHED_DROP_RATE;
 
-    return fairsched_rate ($id, $op, $cpulim1024);
+    return fairsched_rate($id, $op, $cpulim1024);
 }
 
 my $nodename = PVE::INotify::nodename();
@@ -140,6 +153,12 @@ my $confdesc = {
        description => "Automatic restart after crash (currently ignored).",
        default => 0,
     },
+    hotplug => {
+        optional => 1,
+        type => 'boolean',
+        description => "Activate hotplug for disk and network device",
+        default => 0,
+    },
     reboot => {
        optional => 1,
        type => 'boolean',
@@ -170,10 +189,16 @@ my $confdesc = {
     memory => {
        optional => 1,
        type => 'integer',
-       description => "Amount of RAM for the VM in MB.",
+       description => "Amount of RAM for the VM in MB. This is the maximum available memory when you use the balloon device.",
        minimum => 16,
        default => 512,
     },
+    balloon => {
+        optional => 1,
+        type => 'integer',
+        description => "Amount of target RAM for the VM in MB.",
+       minimum => 16,
+    },
     keyboard => {
        optional => 1,
        type => 'string',
@@ -218,7 +243,7 @@ EODESC
        type => 'string',
        description => "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n).",
        pattern => '[acdn]{1,4}',
-       default => 'cad',
+       default => 'cdn',
     },
     bootdisk => {
        optional => 1,
@@ -265,7 +290,7 @@ EODESC
        description => "Enable/disable time drift fix.",
        default => 1,
     },
-    localtime => { 
+    localtime => {
        optional => 1,
        type => 'boolean',
        description => "Set the real time clock to local time. This is enabled by default if ostype indicates a Microsoft OS.",
@@ -289,7 +314,7 @@ EODESC
     },
     startdate => {
        optional => 1,
-       type => 'string', 
+       type => 'string',
        typetext => "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
        description => "Set the initial date of the real time clock. Valid format for date are: 'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
        pattern => '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
@@ -372,7 +397,7 @@ my $MAX_PARALLEL_PORTS = 3;
 
 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);
+my $nic_model_list_txt = join(' ', sort @$nic_model_list);
 
 # fixme:
 my $netdesc = {
@@ -380,11 +405,11 @@ my $netdesc = {
     type => 'string', format => 'pve-qm-net',
     typetext => "MODEL=XX:XX:XX:XX:XX:XX [,bridge=<dev>][,rate=<mbps>]",
     description => <<EODESCR,
-Specify network devices. 
+Specify network devices.
 
 MODEL is one of: $nic_model_list_txt
 
-XX:XX:XX:XX:XX:XX should be an unique MAC address. This is 
+XX:XX:XX:XX:XX:XX should be an unique MAC address. This is
 automatically generated if not specified.
 
 The bridge parameter can be used to automatically add the interface to a bridge device. The Proxmox VE standard bridge is called 'vmbr0'.
@@ -408,11 +433,11 @@ for (my $i = 0; $i < $MAX_NETS; $i++)  {
 }
 
 my $drivename_hash;
-    
+
 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] [,format=f] [,backup=yes|no] [,aio=native|threads]',
+    typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe] [,format=f] [,backup=yes|no] [,aio=native|threads]',
     description => "Use volume as IDE hard disk or CD-ROM (n is 0 to 3).",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-ide", $idedesc);
@@ -420,7 +445,7 @@ PVE::JSONSchema::register_standard_option("pve-qm-ide", $idedesc);
 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] [,format=f] [,backup=yes|no] [,aio=native|threads]',
+    typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe] [,format=f] [,backup=yes|no] [,aio=native|threads]',
     description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to 13).",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc);
@@ -428,7 +453,7 @@ PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc);
 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] [,format=f] [,backup=yes|no] [,aio=native|threads]',
+    typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe] [,format=f] [,backup=yes|no] [,aio=native|threads]',
     description => "Use volume as VIRTIO hard disk (n is 0 to 5).",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
@@ -441,10 +466,10 @@ my $usbdesc = {
 Configure an USB device (n is 0 to 4). This can be used to
 pass-through usb devices to the guest. HOSTUSBDEVICE syntax is:
 
-'bus-port(.port)*' (decimal numbers) or 
+'bus-port(.port)*' (decimal numbers) or
 'vendor_id:product_id' (hexadeciaml numbers)
 
-You can use the 'lsusb -t' command to list existing usb devices.  
+You can use the 'lsusb -t' command to list existing usb devices.
 
 Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
 
@@ -472,31 +497,29 @@ PVE::JSONSchema::register_standard_option("pve-qm-hostpci", $hostpcidesc);
 
 my $serialdesc = {
        optional => 1,
-       type => 'string', format => 'pve-qm-serial',
+       type => 'string',
        pattern => '/dev/ttyS\d+',
        description =>  <<EODESCR,
-Map host serial devices (n is 0 to 3). 
+Map host serial devices (n is 0 to 3).
 
 Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
 
 Experimental: user reported problems with this option.
 EODESCR
 };
-PVE::JSONSchema::register_standard_option("pve-qm-serial", $serialdesc);
 
 my $paralleldesc= {
        optional => 1,
-       type => 'string', format => 'pve-qm-parallel',
+       type => 'string',
        pattern => '/dev/parport\d+',
        description =>  <<EODESCR,
-Map host parallel devices (n is 0 to 2).  
+Map host parallel devices (n is 0 to 2).
 
 Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
 
 Experimental: user reported problems with this option.
 EODESCR
 };
-PVE::JSONSchema::register_standard_option("pve-qm-parallel", $paralleldesc);
 
 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++)  {
     $confdesc->{"parallel$i"} = $paralleldesc;
@@ -545,10 +568,10 @@ sub kvm_version {
 
     return $kvm_api_version if $kvm_api_version;
 
-    my $fh = IO::File->new ("</dev/kvm") ||
+    my $fh = IO::File->new("</dev/kvm") ||
        return 0;
 
-    if (my $v = $fh->ioctl (KVM_GET_API_VERSION(), 0)) {
+    if (my $v = $fh->ioctl(KVM_GET_API_VERSION(), 0)) {
        $kvm_api_version = $v;
     }
 
@@ -566,7 +589,7 @@ sub kvm_user_version {
     $kvm_user_version = 'unknown';
 
     my $tmp = `kvm -help 2>/dev/null`;
-    
+
     if ($tmp =~ m/^QEMU( PC)? emulator version (\d+\.\d+\.\d+) /) {
        $kvm_user_version = $2;
     }
@@ -579,7 +602,7 @@ my $kernel_has_vhost_net = -c '/dev/vhost-net';
 
 sub disknames {
     # order is important - used to autoselect boot disk
-    return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))),  
+    return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))),
             (map { "scsi$_" } (0 .. ($MAX_SCSI_DISKS - 1))),
             (map { "virtio$_" } (0 .. ($MAX_VIRTIO_DISKS - 1))));
 }
@@ -587,13 +610,13 @@ sub disknames {
 sub valid_drivename {
     my $dev = shift;
 
-    return defined ($drivename_hash->{$dev});
+    return defined($drivename_hash->{$dev});
 }
 
 sub option_exists {
     my $key = shift;
     return defined($confdesc->{$key});
-} 
+}
 
 sub nic_models {
     return $nic_model_list;
@@ -611,33 +634,7 @@ sub os_list_description {
        win7 => 'Windows 7',
        l24 => 'Linux 2.4',
        l26 => 'Linux 2.6',
-    }; 
-}
-
-# a clumsy way to split an argument string into an array,
-# we simply pass it to the cli (exec call)
-# fixme: use Text::ParseWords::shellwords() ?
-sub split_args {
-    my ($str) = @_;
-
-    my $args = [];
-
-    return $args if !$str;
-
-    my $cmd = 'perl -e \'foreach my $a (@ARGV) { print "$a\n"; } \' -- ' . $str;
-
-    eval {
-       run_command ($cmd, outfunc => sub {
-           my $data = shift;
-           push @$args, $data;
-       });
     };
-    
-    my $err = $@;
-
-    die "unable to parse args: $str\n" if $err;
-
-    return $args;
 }
 
 sub disk_devive_info {
@@ -655,7 +652,7 @@ sub disk_devive_info {
        $maxdev = 7;
     }
 
-    my $controller = int ($index / $maxdev);
+    my $controller = int($index / $maxdev);
     my $unit = $index % $maxdev;
 
 
@@ -665,17 +662,17 @@ sub disk_devive_info {
 }
 
 sub qemu_drive_name {
-    my ($dev, $media) = @_; 
+    my ($dev, $media) = @_;
 
-    my $info = disk_devive_info ($dev);
+    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}, 
+       return sprintf("%s%i%s%i", $info->{bus}, $info->{controller},
                       $mediastr, $info->{unit});
     } else {
-       return sprintf("%s%i", $info->{bus}, $info->{index}); 
+       return sprintf("%s%i", $info->{bus}, $info->{index});
     }
 }
 
@@ -700,7 +697,7 @@ sub get_iso_path {
     } elsif ($cdrom =~ m|^/|) {
        return $cdrom;
     } else {
-       return PVE::Storage::path ($storecfg, $cdrom);
+       return PVE::Storage::path($storecfg, $cdrom);
     }
 }
 
@@ -710,9 +707,9 @@ sub filename_to_volume_id {
 
     if (!($file eq 'none' || $file eq 'cdrom' ||
          $file =~ m|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
-                           
+
        return undef if $file =~ m|/|;
-       
+
        if ($media && $media eq 'cdrom') {
            $file = "local:iso/$file";
        } else {
@@ -738,7 +735,7 @@ sub verify_media_type {
     }
 
     return if ($vtype eq $etype);
-    
+
     raise_param_exc({ $opt => "unexpected media type ($vtype != $etype)" });
 }
 
@@ -750,7 +747,7 @@ sub cleanup_drive_path {
     if (($drive->{file} !~ m/^(cdrom|none)$/) &&
        ($drive->{file} !~ m|^/dev/.+|) &&
        ($drive->{file} !~ m/^([^:]+):(.+)$/) &&
-       ($drive->{file} !~ m/^\d+$/)) { 
+       ($drive->{file} !~ m/^\d+$/)) {
        my ($vtype, $volid) = PVE::Storage::path_to_volume_id($storecfg, $drive->{file});
        raise_param_exc({ $opt => "unable to associate path '$drive->{file}' to any storage"}) if !$vtype;
        $drive->{media} = 'cdrom' if !$drive->{media} && $vtype eq 'iso';
@@ -764,10 +761,10 @@ sub cleanup_drive_path {
 sub create_conf_nolock {
     my ($vmid, $settings) = @_;
 
-    my $filename = config_file ($vmid);
+    my $filename = config_file($vmid);
 
     die "configuration file '$filename' already exists\n" if -f $filename;
-    
+
     my $defaults = load_defaults();
 
     $settings->{name} = "vm$vmid" if !$settings->{name};
@@ -794,7 +791,7 @@ sub parse_drive {
     my ($key, $data) = @_;
 
     my $res = {};
-    
+
     # $key may be undefined - used to verify JSON parameters
     if (!defined($key)) {
        $res->{interface} = 'unknown'; # should not harm when used to verify parameters
@@ -815,7 +812,7 @@ sub parse_drive {
            $k = 'file' if $k eq 'volume';
 
            return undef if defined $res->{$k};
-           
+
            $res->{$k} = $v;
        } else {
            if (!$res->{file} && $p !~ m/=/) {
@@ -828,8 +825,8 @@ sub parse_drive {
 
     return undef if !$res->{file};
 
-    return undef if $res->{cache} && 
-       $res->{cache} !~ m/^(off|none|writethrough|writeback)$/;
+    return undef if $res->{cache} &&
+       $res->{cache} !~ m/^(off|none|writethrough|writeback|unsafe)$/;
     return undef if $res->{snapshot} && $res->{snapshot} !~ m/^(on|off)$/;
     return undef if $res->{cyls} && $res->{cyls} !~ m/^\d+$/;
     return undef if $res->{heads} && $res->{heads} !~ m/^\d+$/;
@@ -844,7 +841,7 @@ sub parse_drive {
 
     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}; 
+       return undef if $res->{heads} || $res->{secs} || $res->{cyls};
        return undef if $res->{interface} eq 'virtio';
     }
 
@@ -874,32 +871,32 @@ sub print_drivedevice_full {
 
     my $device = '';
     my $maxdev = 0;
-    if ($drive->{interface} eq 'virtio') {
 
-      $device="virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=device-$drive->{interface}$drive->{index}";              
+    if ($drive->{interface} eq 'virtio') {
+      my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}");
+      $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
     }
 
     elsif ($drive->{interface} eq 'scsi') {
 
       $maxdev = 7;
-      my $controller = int ($drive->{index} / $maxdev);
+      my $controller = int($drive->{index} / $maxdev);
       my $unit = $drive->{index} % $maxdev;
 
-      $device="scsi-disk,bus=scsi$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=device-$drive->{interface}$drive->{index}";
+      $device = "scsi-disk,bus=scsi$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=device-$drive->{interface}$drive->{index}";
     }
 
     elsif ($drive->{interface} eq 'ide'){
 
       $maxdev = 2;
-      my $controller = int ($drive->{index} / $maxdev);
+      my $controller = int($drive->{index} / $maxdev);
       my $unit = $drive->{index} % $maxdev;
 
-      $device="ide-drive,bus=ide.$controller,unit=$unit,drive=drive-$drive->{interface}$drive->{index},id=device-$drive->{interface}$drive->{index}";
+      $device = "ide-drive,bus=ide.$controller,unit=$unit,drive=drive-$drive->{interface}$drive->{index},id=device-$drive->{interface}$drive->{index}";
     }
 
     if ($drive->{interface} eq 'usb'){
-      #  -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0 
+      #  -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
     }
 
     return $device;
@@ -911,20 +908,20 @@ sub print_drive_full {
     my $opts = '';
     foreach my $o (@qemu_drive_options) {
        $opts .= ",$o=$drive->{$o}" if $drive->{$o};
-    } 
+    }
 
     # use linux-aio by default (qemu default is threads)
-    $opts .= ",aio=native" if !$drive->{aio}; 
+    $opts .= ",aio=native" if !$drive->{aio};
 
     my $path;
     my $volid = $drive->{file};
-    if (drive_is_cdrom ($drive)) {
-       $path = get_iso_path ($storecfg, $vmid, $volid);
+    if (drive_is_cdrom($drive)) {
+       $path = get_iso_path($storecfg, $vmid, $volid);
     } else {
        if ($volid =~ m|^/|) {
            $path = $volid;
        } else {
-           $path = PVE::Storage::path ($storecfg, $volid);
+           $path = PVE::Storage::path($storecfg, $volid);
        }
     }
 
@@ -963,11 +960,11 @@ sub parse_net {
 
     my $res = {};
 
-    foreach my $kvp (split (/,/, $data)) {
+    foreach my $kvp (split(/,/, $data)) {
 
        if ($kvp =~ m/^(ne2k_pci|e1000|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i) {
-           my $model = lc ($1);
-           my $mac = uc($3) || random_ether_addr ();
+           my $model = lc($1);
+           my $mac = uc($3) || PVE::Tools::random_ether_addr();
            $res->{model} = $model;
            $res->{macaddr} = $mac;
        } elsif ($kvp =~ m/^bridge=(\S+)$/) {
@@ -977,7 +974,7 @@ sub parse_net {
        } else {
            return undef;
        }
-       
+
     }
 
     return undef if !$res->{model};
@@ -1017,7 +1014,7 @@ sub add_unused_volume {
            return if $vid eq $volid; # do not add duplicates
        } else {
            $key = $test;
-       } 
+       }
     }
 
     die "To many unused volume - please delete them first.\n" if !$key;
@@ -1031,7 +1028,7 @@ PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk);
 sub verify_bootdisk {
     my ($value, $noerr) = @_;
 
-    return $value if valid_drivename($value); 
+    return $value if valid_drivename($value);
 
     return undef if $noerr;
 
@@ -1045,7 +1042,7 @@ sub verify_net {
     return $value if parse_net($value);
 
     return undef if $noerr;
-    
+
     die "unable to parse network options\n";
 }
 
@@ -1053,10 +1050,10 @@ PVE::JSONSchema::register_format('pve-qm-drive', \&verify_drive);
 sub verify_drive {
     my ($value, $noerr) = @_;
 
-    return $value if parse_drive (undef, $value);
+    return $value if parse_drive(undef, $value);
 
     return undef if $noerr;
-    
+
     die "unable to parse drive options\n";
 }
 
@@ -1078,7 +1075,7 @@ sub verify_watchdog {
     return $value if parse_watchdog($value);
 
     return undef if $noerr;
-    
+
     die "unable to parse watchdog options\n";
 }
 
@@ -1089,7 +1086,7 @@ sub parse_watchdog {
 
     my $res = {};
 
-    foreach my $p (split (/,/, $value)) {
+    foreach my $p (split(/,/, $value)) {
        next if $p =~ m/^\s*$/;
 
        if ($p =~ m/^(model=)?(i6300esb|ib700)$/) {
@@ -1109,7 +1106,7 @@ sub parse_usb_device {
 
     return undef if !$value;
 
-    my @dl = split (/,/, $value);
+    my @dl = split(/,/, $value);
     my $found;
 
     my $res = {};
@@ -1130,7 +1127,7 @@ sub parse_usb_device {
 
     return $res;
 }
+
 PVE::JSONSchema::register_format('pve-qm-usb-device', \&verify_usb_device);
 sub verify_usb_device {
     my ($value, $noerr) = @_;
@@ -1138,61 +1135,8 @@ sub verify_usb_device {
     return $value if parse_usb_device($value);
 
     return undef if $noerr;
-    
-    die "unable to parse usb device\n";
-}
 
-PVE::JSONSchema::register_format('pve-qm-parallel', \&verify_parallel);
-sub verify_parallel {
-    my ($value, $noerr) = @_;
-
-    return $value if parse_parallel($value);
-
-    return undef if $noerr;
-
-    die "invalid device name\n";
-}
-
-sub parse_parallel {
-    my ($value) = @_;
-
-    return undef if !$value;
-
-    my $res = {};
-    if ($value =~ m|^/dev/parport\d+$|) {
-       $res->{dev} = $value;
-    } else {
-       return undef;
-    }
-
-    return $res;
-}
-
-PVE::JSONSchema::register_format('pve-qm-serial', \&verify_serial);
-sub verify_serial {
-    my ($value, $noerr) = @_;
-
-    return $value if parse_serial($value);
-
-    return undef if $noerr;
-
-    die "invalid device name\n";
-
-}
-
-sub parse_serial {
-    my ($value) = @_;
-
-    return undef if !$value;
-
-    my $res = {};
-    if ($value =~ m|^/dev/ttyS\d+$|) {
-       $res->{dev} = $value;
-    } else {
-       return undef;
-    }
-
-    return $res;
+    die "unable to parse usb device\n";
 }
 
 # add JSON properties for create and set function
@@ -1213,7 +1157,7 @@ sub check_type {
 
     my $type = $confdesc->{$key}->{type};
 
-    if (!defined ($value)) {
+    if (!defined($value)) {
        die "got undefined value\n";
     }
 
@@ -1222,9 +1166,9 @@ sub check_type {
     }
 
     if ($type eq 'boolean') {
-       return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i); 
-       return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i); 
-       die "type check ('boolean') failed - got '$value'\n";   
+       return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
+       return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
+       die "type check ('boolean') failed - got '$value'\n";
     } elsif ($type eq 'integer') {
        return int($1) if $value =~ m/^(\d+)$/;
        die "type check ('integer') failed - got '$value'\n";
@@ -1232,15 +1176,15 @@ sub check_type {
        if (my $fmt = $confdesc->{$key}->{format}) {
            if ($fmt eq 'pve-qm-drive') {
                # special case - we need to pass $key to parse_drive()
-               my $drive = parse_drive ($key, $value);
+               my $drive = parse_drive($key, $value);
                return $value if $drive;
                die "unable to parse drive options\n";
            }
            PVE::JSONSchema::check_format($fmt, $value);
-           return $value;      
-       } 
+           return $value;
+       }
        $value =~ s/^\"(.*)\"$/$1/;
-       return $value;  
+       return $value;
     } else {
        die "internal error"
     }
@@ -1249,11 +1193,13 @@ sub check_type {
 sub lock_config {
     my ($vmid, $code, @param) = @_;
 
-    my $filename = config_file_lock ($vmid);
+    my $filename = config_file_lock($vmid);
 
-    lock_file($filename, 10, $code, @param);
+    my $res = lock_file($filename, 10, $code, @param);
 
     die $@ if $@;
+
+    return $res;
 }
 
 sub cfs_config_path {
@@ -1288,12 +1234,12 @@ sub config_file_lock {
 sub touch_config {
     my ($vmid) = @_;
 
-    my $conf = config_file ($vmid);
+    my $conf = config_file($vmid);
     utime undef, undef, $conf;
 }
 
 sub create_disks {
-    my ($storecfg, $vmid, $settings) = @_;
+    my ($storecfg, $vmid, $settings, $conf, $default_storage) = @_;
 
     my $vollist = [];
 
@@ -1301,44 +1247,45 @@ sub create_disks {
        foreach_drive($settings, sub {
            my ($ds, $disk) = @_;
 
-           return if drive_is_cdrom ($disk);
+           return if drive_is_cdrom($disk);
 
            my $file = $disk->{file};
 
            if ($file =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
-               my $storeid = $2 || 'local';
+               my $storeid = $2 || $default_storage;
                my $size = $3;
-               my $defformat = PVE::Storage::storage_default_format ($storecfg, $storeid);
+               my $defformat = PVE::Storage::storage_default_format($storecfg, $storeid);
                my $fmt = $disk->{format} || $defformat;
-               syslog ('info', "VM $vmid creating new disk - size is $size GB");
+               syslog('info', "VM $vmid creating new disk - size is $size GB");
 
-               my $volid = PVE::Storage::vdisk_alloc ($storecfg, $storeid, $vmid, 
+               my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid,
                                                       $fmt, undef, $size*1024*1024);
 
                $disk->{file} = $volid;
-               delete ($disk->{format}); # no longer needed
+               delete $disk->{format}; # no longer needed
                push @$vollist, $volid;
-               $settings->{$ds} = PVE::QemuServer::print_drive ($vmid, $disk);
+               $settings->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
            } else {
                my $path;
                if ($disk->{file} =~ m|^/dev/.+|) {
                    $path = $disk->{file};
                } else {
-                   $path = PVE::Storage::path ($storecfg, $disk->{file});
+                   $path = PVE::Storage::path($storecfg, $disk->{file});
                }
                if (!(-f $path || -b $path)) {
                    die "image '$path' does not exists\n";
                }
            }
+           PVE::QemuServer::vm_deviceadd($storecfg, $conf, $vmid, $ds, $disk) if defined($conf);
        });
     };
 
     my $err = $@;
 
     if ($err) {
-       syslog ('err', "VM $vmid creating disks failed");
+       syslog('err', "VM $vmid creating disks failed");
        foreach my $volid (@$vollist) {
-           eval { PVE::Storage::vdisk_free ($storecfg, $volid); };
+           eval { PVE::Storage::vdisk_free($storecfg, $volid); };
            warn $@ if $@;
        }
        die $err;
@@ -1347,58 +1294,44 @@ sub create_disks {
     return $vollist;
 }
 
-sub unlink_image {
-    my ($storecfg, $vmid, $volid) = @_;
-
-    die "reject to unlink absolute path '$volid'"
-       if $volid =~ m|^/|;
-    
-    my ($path, $owner) = PVE::Storage::path ($storecfg, $volid);
-
-    die "reject to unlink '$volid' - not owned by this VM"
-       if !$owner || ($owner != $vmid);
-
-    syslog ('info', "VM $vmid deleting volume '$volid'");
-
-    PVE::Storage::vdisk_free ($storecfg, $volid);
-
-    touch_config ($vmid);
-}
-
 sub destroy_vm {
-    my ($storecfg, $vmid) = @_;
+    my ($storecfg, $vmid, $keep_empty_config) = @_;
 
-    my $conffile = config_file ($vmid);
+    my $conffile = config_file($vmid);
 
-    my $conf = load_config ($vmid);
+    my $conf = load_config($vmid);
 
-    check_lock ($conf);
+    check_lock($conf);
 
-    # only remove disks owned by this VM 
+    # only remove disks owned by this VM
     foreach_drive($conf, sub {
        my ($ds, $drive) = @_;
 
-       return if drive_is_cdrom ($drive);
+       return if drive_is_cdrom($drive);
 
        my $volid = $drive->{file};
-       next if !$volid || $volid =~ m|^/|;
+       return if !$volid || $volid =~ m|^/|;
 
-       my ($path, $owner) = PVE::Storage::path ($storecfg, $volid);
-       next if !$path || !$owner || ($owner != $vmid);
+       my ($path, $owner) = PVE::Storage::path($storecfg, $volid);
+       return if !$path || !$owner || ($owner != $vmid);
 
-       PVE::Storage::vdisk_free ($storecfg, $volid);
+       PVE::Storage::vdisk_free($storecfg, $volid);
     });
-    
-    unlink $conffile;
+
+    if ($keep_empty_config) {
+       PVE::Tools::file_set_contents($conffile, "memory: 128\n");
+    } else {
+       unlink $conffile;
+    }
 
     # also remove unused disk
     eval {
-       my $dl = PVE::Storage::vdisk_list ($storecfg, undef, $vmid);
+       my $dl = PVE::Storage::vdisk_list($storecfg, undef, $vmid);
 
        eval {
-           PVE::Storage::foreach_volid ($dl, sub {
+           PVE::Storage::foreach_volid($dl, sub {
                my ($volid, $sid, $volname, $d) = @_;
-               PVE::Storage::vdisk_free ($storecfg, $volid);       
+               PVE::Storage::vdisk_free($storecfg, $volid);
            });
        };
        warn $@ if $@;
@@ -1420,19 +1353,19 @@ sub load_diskinfo_old {
 
        $res->{$ds} = $di;
 
-       return if drive_is_cdrom ($di);
+       return if drive_is_cdrom($di);
 
        if ($di->{file} =~ m|^/dev/.+|) {
-           $info->{$di->{file}}->{size} = PVE::Storage::file_size_info ($di->{file});
+           $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);
+       my $dl = PVE::Storage::vdisk_list($storecfg, undef, $vmid, $vollist);
 
-       PVE::Storage::foreach_volid ($dl, sub {
+       PVE::Storage::foreach_volid($dl, sub {
            my ($volid, $sid, $volname, $d) = @_;
            $info->{$volid} = $d;
        });
@@ -1442,7 +1375,7 @@ sub load_diskinfo_old {
     foreach my $ds (keys %$res) {
        my $di = $res->{$ds};
 
-       $res->{$ds}->{disksize} = $info->{$di->{file}} ? 
+       $res->{$ds}->{disksize} = $info->{$di->{file}} ?
            $info->{$di->{file}}->{size} / (1024*1024) : 0;
     }
 
@@ -1459,7 +1392,7 @@ sub load_config {
     die "no such VM ('$vmid')\n" if !defined($conf);
 
     return $conf;
-}  
+}
 
 sub parse_vm_config {
     my ($filename, $raw) = @_;
@@ -1470,14 +1403,14 @@ sub parse_vm_config {
        digest => Digest::SHA1::sha1_hex($raw),
     };
 
-    $filename =~ m|/qemu-server/(\d+)\.conf$| 
+    $filename =~ m|/qemu-server/(\d+)\.conf$|
        || die "got strange filename '$filename'";
 
     my $vmid = $1;
 
     while ($raw && $raw =~ s/^(.*?)(\n|$)//) {
        my $line = $1;
-       
+
        next if $line =~ m/^\#/;
 
        next if $line =~ m/^\s*$/;
@@ -1502,7 +1435,7 @@ sub parse_vm_config {
                    my $v = parse_drive($key, $value);
                    if (my $volid = filename_to_volume_id($vmid, $v->{file}, $v->{media})) {
                        $v->{file} = $volid;
-                       $value = print_drive ($vmid, $v);
+                       $value = print_drive($vmid, $v);
                    } else {
                        warn "vm $vmid - unable to parse value of '$key'\n";
                        next;
@@ -1521,7 +1454,7 @@ sub parse_vm_config {
     # convert old smp to sockets
     if ($res->{smp} && !$res->{sockets}) {
        $res->{sockets} = $res->{smp};
-    } 
+    }
     delete $res->{smp};
 
     return $res;
@@ -1530,7 +1463,7 @@ sub parse_vm_config {
 sub change_config {
     my ($vmid, $settings, $unset, $skiplock) = @_;
 
-    lock_config ($vmid, &change_config_nolock, $settings, $unset, $skiplock);
+    lock_config($vmid, &change_config_nolock, $settings, $unset, $skiplock);
 }
 
 sub change_config_nolock {
@@ -1544,7 +1477,7 @@ sub change_config_nolock {
 
     # we do not use 'smp' any longer
     if ($settings->{sockets}) {
-       $unset->{smp} = 1; 
+       $unset->{smp} = 1;
     } elsif ($settings->{smp}) {
        $settings->{sockets} = $settings->{smp};
        $unset->{smp} = 1;
@@ -1574,19 +1507,19 @@ sub change_config_nolock {
     my $filename = config_file($vmid);
     my $tmpfn = "$filename.$$.tmp";
 
-    my $fh = new IO::File ($filename, "r") ||
+    my $fh = new IO::File($filename, "r") ||
        die "unable to read config for VM $vmid\n";
 
     my $werror = "unable to write config for VM $vmid\n";
 
-    my $out = new IO::File ($tmpfn, "w") || die $werror;
+    my $out = new IO::File($tmpfn, "w") || die $werror;
 
     eval {
 
        my $done;
 
        while (my $line = <$fh>) {
-       
+
            if (($line =~ m/^\#/) || ($line =~ m/^\s*$/)) {
                die $werror unless print $out $line;
                next;
@@ -1605,13 +1538,13 @@ sub change_config_nolock {
                next if $done->{$key};
                $done->{$key} = 1;
 
-               if (defined ($res->{$key})) {
+               if (defined($res->{$key})) {
                    $value = $res->{$key};
                    delete $res->{$key};
                }
-               if (!defined ($unset->{$key})) {
+               if (!defined($unset->{$key})) {
                    die $werror unless print $out "$key: $value\n";
-               } 
+               }
 
                next;
            }
@@ -1621,7 +1554,7 @@ sub change_config_nolock {
 
        foreach my $key (keys %$res) {
 
-           if (!defined ($unset->{$key})) {
+           if (!defined($unset->{$key})) {
                die $werror unless print $out "$key: $res->{$key}\n";
            }
        }
@@ -1640,7 +1573,7 @@ sub change_config_nolock {
     if (!$out->close()) {
        $err = "close failed - $!\n";
        unlink $tmpfn;
-       die $err;       
+       die $err;
     }
 
     if (!rename($tmpfn, $filename)) {
@@ -1650,7 +1583,7 @@ sub change_config_nolock {
     }
 }
 
-sub load_defaults { 
+sub load_defaults {
 
     my $res = {};
 
@@ -1660,7 +1593,7 @@ sub load_defaults {
            $res->{$key} = $default;
        }
     }
-   
+
     my $conf = PVE::Cluster::cfs_read_file('datacenter.cfg');
     $res->{keyboard} = $conf->{keyboard} if $conf->{keyboard};
 
@@ -1676,6 +1609,7 @@ sub config_list {
     foreach my $vmid (keys %$ids) {
        my $d = $ids->{$vmid};
        next if !$d->{node} || $d->{node} ne $nodename;
+       next if !$d->{type} || $d->{type} ne 'qemu';
        $res->{$vmid}->{exists} = 1;
     }
     return $res;
@@ -1686,10 +1620,9 @@ sub check_local_resources {
     my ($conf, $noerr) = @_;
 
     my $loc_res = 0;
-    # fixme:
-    die "implement me";
-    $loc_res = 1 if $conf->{hostusb};
-    $loc_res = 1 if $conf->{hostpci};
+
+    $loc_res = 1 if $conf->{hostusb}; # old syntax
+    $loc_res = 1 if $conf->{hostpci}; # old syntax
 
     foreach my $k (keys %$conf) {
        $loc_res = 1 if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
@@ -1709,12 +1642,12 @@ sub check_lock {
 sub check_cmdline {
     my ($pidfile, $pid) = @_;
 
-    my $fh = IO::File->new ("/proc/$pid/cmdline", "r");
-    if (defined ($fh)) {
+    my $fh = IO::File->new("/proc/$pid/cmdline", "r");
+    if (defined($fh)) {
        my $line = <$fh>;
        $fh->close;
        return undef if !$line;
-       my @param = split (/\0/, $line);
+       my @param = split(/\0/, $line);
 
        my $cmd = $param[0];
        return if !$cmd || ($cmd !~ m|kvm$|);
@@ -1733,19 +1666,19 @@ sub check_cmdline {
 }
 
 sub check_running {
-    my ($vmid) = @_;
+    my ($vmid, $nocheck) = @_;
 
-    my $filename = config_file ($vmid);
+    my $filename = config_file($vmid);
 
     die "unable to find configuration file for VM $vmid - no such machine\n"
-       if ! -f $filename;
+       if !$nocheck && ! -f $filename;
 
-    my $pidfile = pidfile_name ($vmid);
+    my $pidfile = pidfile_name($vmid);
 
-    if (my $fd = IO::File->new ("<$pidfile")) {
-       my $st = stat ($fd);
+    if (my $fd = IO::File->new("<$pidfile")) {
+       my $st = stat($fd);
        my $line = <$fd>;
-       close ($fd);
+       close($fd);
 
        my $mtime = $st->mtime;
        if ($mtime > time()) {
@@ -1754,8 +1687,11 @@ sub check_running {
 
        if ($line =~ m/^(\d+)$/) {
            my $pid = $1;
-
-           return $pid if ((-d "/proc/$pid") && check_cmdline ($pidfile, $pid));
+           if (check_cmdline($pidfile, $pid)) {
+               if (my $pinfo = PVE::ProcFSTools::check_process_running($pid)) {
+                   return $pid;
+               }
+           }
        }
     }
 
@@ -1763,16 +1699,16 @@ sub check_running {
 }
 
 sub vzlist {
-    
+
     my $vzlist = config_list();
 
-    my $fd = IO::Dir->new ($var_run_tmpdir) || return $vzlist;
+    my $fd = IO::Dir->new($var_run_tmpdir) || return $vzlist;
 
-    while (defined(my $de = $fd->read)) { 
+    while (defined(my $de = $fd->read)) {
        next if $de !~ m/^(\d+)\.pid$/;
        my $vmid = $1;
-       next if !defined ($vzlist->{$vmid});
-       if (my $pid = check_running ($vmid)) {
+       next if !defined($vzlist->{$vmid});
+       if (my $pid = check_running($vmid)) {
            $vzlist->{$vmid}->{pid} = $pid;
        }
     }
@@ -1806,7 +1742,7 @@ sub disksize {
     if ($volid =~ m|^/|) {
        $path = $timeoutid = $volid;
     } else {
-       $storeid = $timeoutid = PVE::Storage::parse_volume_id ($volid);
+       $storeid = $timeoutid = PVE::Storage::parse_volume_id($volid);
        $path = PVE::Storage::path($storecfg, $volid);
     }
 
@@ -1839,10 +1775,12 @@ sub vmstatus {
 
     my $res = {};
 
-    my $storecfg = PVE::Storage::config(); 
+    my $storecfg = PVE::Storage::config();
 
     my $list = vzlist();
-    my ($uptime) = PVE::ProcFSTools::read_proc_uptime();
+    my ($uptime) = PVE::ProcFSTools::read_proc_uptime(1);
+
+    my $cpucount = $cpuinfo->{cpus} || 1;
 
     foreach my $vmid (keys %$list) {
        next if $opt_vmid && ($vmid ne $opt_vmid);
@@ -1866,13 +1804,13 @@ sub vmstatus {
        }
 
        $d->{cpus} = ($conf->{sockets} || 1) * ($conf->{cores} || 1);
-       $d->{name} = $conf->{name} || "VM $vmid";
-       $d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024) : 0; 
+       $d->{cpus} = $cpucount if $d->{cpus} > $cpucount;
 
+       $d->{name} = $conf->{name} || "VM $vmid";
+       $d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024) : 0;
 
        $d->{uptime} = 0;
        $d->{cpu} = 0;
-       $d->{relcpu} = 0;
        $d->{mem} = 0;
 
        $d->{netout} = 0;
@@ -1890,13 +1828,11 @@ sub vmstatus {
        my $vmid = $1;
        my $d = $res->{$vmid};
        next if !$d;
-       
+
        $d->{netout} += $netdev->{$dev}->{receive};
        $d->{netin} += $netdev->{$dev}->{transmit};
     }
 
-    my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
-    my $cpucount = $cpuinfo->{cpus} || 1;
     my $ctime = gettimeofday;
 
     foreach my $vmid (keys %$list) {
@@ -1907,7 +1843,7 @@ sub vmstatus {
 
        if (my $fh = IO::File->new("/proc/$pid/io", "r")) {
            my $data = {};
-           while (defined (my $line = <$fh>)) {
+           while (defined(my $line = <$fh>)) {
                if ($line =~ m/^([rw]char):\s+(\d+)$/) {
                    $data->{$1} = $2;
                }
@@ -1917,53 +1853,40 @@ sub vmstatus {
            $d->{diskwrite} = $data->{wchar} || 0;
        }
 
-       my $statstr = file_read_firstline("/proc/$pid/stat");
-       next if !$statstr;
+       my $pstat = PVE::ProcFSTools::read_proc_pid_stat($pid);
+       next if !$pstat; # not running
 
-       my ($utime, $stime, $vsize, $rss, $starttime);
-       if ($statstr =~ m/^$pid \(.*\) \S (-?\d+) -?\d+ -?\d+ -?\d+ -?\d+ \d+ \d+ \d+ \d+ \d+ (\d+) (\d+) (-?\d+) (-?\d+) -?\d+ -?\d+ -?\d+ 0 (\d+) (\d+) (-?\d+) \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ -?\d+ -?\d+ \d+ \d+ \d+/) {
-           ($utime, $stime, $vsize, $rss, $starttime) = ($2, $3, $7, $8 * 4096, $6);
-       } else {
-           next;
-       }
-       my $used = $utime + $stime;
+       my $used = $pstat->{utime} + $pstat->{stime};
 
-       my $vcpus = $d->{cpus} > $cpucount ? $cpucount : $d->{cpus};
+       $d->{uptime} = int(($uptime - $pstat->{starttime})/$cpuinfo->{user_hz});
 
-       $d->{uptime} = int ($uptime - ($starttime/100));
-
-       if ($vsize) {
-           $d->{mem} = int (($rss/$vsize)*$d->{maxmem});
+       if ($pstat->{vsize}) {
+           $d->{mem} = int(($pstat->{rss}/$pstat->{vsize})*$d->{maxmem});
        }
 
        my $old = $last_proc_pid_stat->{$pid};
        if (!$old) {
-           $last_proc_pid_stat->{$pid} = { 
-               time => $ctime, 
+           $last_proc_pid_stat->{$pid} = {
+               time => $ctime,
                used => $used,
                cpu => 0,
-               relcpu => 0,
            };
            next;
        }
 
-       my $dtime = ($ctime -  $old->{time}) * $cpucount * $clock_ticks;
+       my $dtime = ($ctime -  $old->{time}) * $cpucount * $cpuinfo->{user_hz};
 
        if ($dtime > 1000) {
            my $dutime = $used -  $old->{used};
 
-           $d->{cpu} = $dutime/$dtime;
-           $d->{relcpu} = ($d->{cpu} * $cpucount) / $vcpus;
+           $d->{cpu} = (($dutime/$dtime)* $cpucount) / $d->{cpus};
            $last_proc_pid_stat->{$pid} = {
-               time => $ctime, 
+               time => $ctime,
                used => $used,
                cpu => $d->{cpu},
-               relcpu => $d->{relcpu},
            };
        } else {
            $d->{cpu} = $old->{cpu};
-           $d->{relcpu} = $old->{relcpu};
        }
     }
 
@@ -1976,7 +1899,7 @@ sub foreach_drive {
     foreach my $ds (keys %$conf) {
        next if !valid_drivename($ds);
 
-       my $drive = parse_drive ($ds, $conf->{$ds});
+       my $drive = parse_drive($ds, $conf->{$ds});
        next if !$drive;
 
        &$func($ds, $drive);
@@ -1987,7 +1910,7 @@ sub config_to_command {
     my ($storecfg, $vmid, $conf, $defaults, $migrate_uri) = @_;
 
     my $cmd = [];
-
+    my $pciaddr = '';
     my $kvmver = kvm_user_version();
     my $vernum = 0; # unknown
     if ($kvmver =~ m/^(\d+)\.(\d+)\.(\d+)$/) {
@@ -2004,31 +1927,32 @@ sub config_to_command {
 
     my $use_virtio = 0;
 
-    my $socket = monitor_socket ($vmid);
+    my $socket = monitor_socket($vmid);
     push @$cmd, '-chardev', "socket,id=monitor,path=$socket,server,nowait";
     push @$cmd, '-mon', "chardev=monitor,mode=readline";
 
-    $socket = vnc_socket ($vmid);
+    $socket = vnc_socket($vmid);
     push @$cmd,  '-vnc', "unix:$socket,x509,password";
 
-    push @$cmd, '-pidfile' , pidfile_name ($vmid);
+    push @$cmd, '-pidfile' , pidfile_name($vmid);
+
     push @$cmd, '-daemonize';
 
     push @$cmd, '-incoming', $migrate_uri if $migrate_uri;
 
     # include usb device config
     push @$cmd, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg';
-    
+
     # enable absolute mouse coordinates (needed by vnc)
-    my $tablet = defined ($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
+    my $tablet = defined($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
     push @$cmd, '-device', 'usb-tablet,bus=ehci.0,port=6' if $tablet;
 
     # host pci devices
     for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++)  {
           my $d = parse_hostpci($conf->{"hostpci$i"});
           next if !$d;
-          push @$cmd, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i";
+         $pciaddr = print_pci_addr("hostpci$i");
+          push @$cmd, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i$pciaddr";
     }
 
     # usb devices
@@ -2042,33 +1966,28 @@ sub config_to_command {
        }
     }
 
-    if (my $usbdl = $conf->{hostusb}) {
-       my @dl = split (/,/, $usbdl);
-       foreach my $dev (@dl) {
-           push @$cmd, '-usbdevice', "host:$dev" if $dev;
-       }
-    }
-
     # serial devices
     for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++)  {
-          my $d = parse_serial($conf->{"serial$i"});
-          next if !$d;
-          push @$cmd, '-chardev', "tty,id=serial$i,path=$d->{dev}";
-          push @$cmd, '-device', "isa-serial,chardev=serial$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";
+       }
     }
 
     # parallel devices
     for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++)  {
-          my $d = parse_parallel($conf->{"parallel$i"});
-          next if !$d;
-          push @$cmd, '-chardev', "parport,id=parallel$i,path=$d->{dev}";
-          push @$cmd, '-device', "isa-parallel,chardev=parallel$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";
+       }
     }
 
     my $vmname = $conf->{name} || "vm$vmid";
 
     push @$cmd, '-name', $vmname;
-    
+
     my $sockets = 1;
     $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
     $sockets = $conf->{sockets} if  $conf->{sockets};
@@ -2081,18 +2000,14 @@ sub config_to_command {
 
     push @$cmd, '-cpu', $conf->{cpu} if $conf->{cpu};
 
-    $boot_opt = "menu=on";
-    if ($conf->{boot}) {
-       $boot_opt .= ",order=$conf->{boot}";
-    }
-
     push @$cmd, '-nodefaults';
 
-    push @$cmd, '-boot', $boot_opt if $boot_opt;
+    my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
+    push @$cmd, '-boot', "menu=on,order=$bootorder";
 
-    push @$cmd, '-no-acpi' if defined ($conf->{acpi}) && $conf->{acpi} == 0;
+    push @$cmd, '-no-acpi' if defined($conf->{acpi}) && $conf->{acpi} == 0;
 
-    push @$cmd, '-no-reboot' if  defined ($conf->{reboot}) && $conf->{reboot} == 0;
+    push @$cmd, '-no-reboot' if  defined($conf->{reboot}) && $conf->{reboot} == 0;
 
     my $vga = $conf->{vga};
     if (!$vga) {
@@ -2102,34 +2017,38 @@ sub config_to_command {
            $vga = 'cirrus';
        }
     }
-  
+
     push @$cmd, '-vga', $vga if $vga; # for kvm 77 and later
 
     # time drift fix
-    my $tdf = defined ($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
+    my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
     push @$cmd, '-tdf' if $tdf;
 
-    my $nokvm = defined ($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
+    my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
 
     if (my $ost = $conf->{ostype}) {
        # other, wxp, w2k, w2k3, w2k8, wvista, win7, l24, l26
 
        if ($ost =~ m/^w/) { # windows
-           push @$cmd, '-localtime' if !defined ($conf->{localtime});
+           push @$cmd, '-localtime' if !defined($conf->{localtime});
 
            # use rtc-td-hack when acpi is enabled
-           if (!(defined ($conf->{acpi}) && $conf->{acpi} == 0)) {
+           if (!(defined($conf->{acpi}) && $conf->{acpi} == 0)) {
                push @$cmd, '-rtc-td-hack';
            }
        }
 
        # -tdf ?
-       # -no-acpi 
-       # -no-kvm 
+       # -no-acpi
+       # -no-kvm
        # -win2k-hack ?
     }
 
-    push @$cmd, '-no-kvm' if $nokvm;
+    if ($nokvm) {
+       push @$cmd, '-no-kvm';
+    } else {
+       die "No accelerator found!\n" if !$cpuinfo->{hvm};
+    }
 
     push @$cmd, '-localtime' if $conf->{localtime};
 
@@ -2145,10 +2064,14 @@ 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->{watchdog}) {
        my $wdopts = parse_watchdog($conf->{watchdog});
-       push @$cmd, '-watchdog', $wdopts->{model} || 'i6300esb';
+       $pciaddr = print_pci_addr("watchdog");
+       my $watchdog = $wdopts->{model} || 'i6300esb';
+       push @$cmd, '-device', "$watchdog$pciaddr";
        push @$cmd, '-watchdog-action', $wdopts->{action} if $wdopts->{action};
     }
 
@@ -2158,31 +2081,31 @@ sub config_to_command {
     foreach_drive($conf, sub {
        my ($ds, $drive) = @_;
 
-       eval { 
-           PVE::Storage::parse_volume_id ($drive->{file});
+       if (PVE::Storage::parse_volume_id($drive->{file}, 1)) {
            push @$vollist, $drive->{file};
-       }; # ignore errors
-
+       }
+       
        $use_virtio = 1 if $ds =~ m/^virtio/;
         if ($drive->{interface} eq 'scsi') {
            my $maxdev = 7;
-           my $controller = int ($drive->{index} / $maxdev);
-           push @$cmd, '-device', "lsi,id=scsi$controller" if !$scsicontroller->{$controller};
-           my $scsicontroller->{$controller}=1;
+           my $controller = int($drive->{index} / $maxdev);
+          $pciaddr = print_pci_addr("scsi$controller");
+           push @$cmd, '-device', "lsi,id=scsi$controller$pciaddr" if !$scsicontroller->{$controller};
+           $scsicontroller->{$controller}=1;
         }
-       my $tmp = print_drive_full ($storecfg, $vmid, $drive);
+       my $tmp = print_drive_full($storecfg, $vmid, $drive);
        $tmp .= ",boot=on" if $conf->{bootdisk} && ($conf->{bootdisk} eq $ds);
        push @$cmd, '-drive', $tmp;
-       push @$cmd, '-device',print_drivedevice_full ($storecfg,$vmid, $drive);
+       push @$cmd, '-device',print_drivedevice_full($storecfg,$vmid, $drive);
     });
 
     push @$cmd, '-m', $conf->{memory} || $defaults->{memory};
+
     my $foundnet = 0;
 
     foreach my $k (sort keys %$conf) {
        next if $k !~ m/^net(\d+)$/;
-       my $i = int ($1);
+       my $i = int($1);
 
        die "got strange net id '$i'\n" if $i >= ${MAX_NETS};
 
@@ -2193,7 +2116,7 @@ sub config_to_command {
            my $ifname = "tap${vmid}i$i";
 
            # kvm uses TUNSETIFF ioctl, and that limits ifname length
-           die "interface name '$ifname' is too long (max 15 character)\n" 
+           die "interface name '$ifname' is too long (max 15 character)\n"
                if length($ifname) >= 16;
 
            my $device = $net->{model};
@@ -2214,17 +2137,18 @@ sub config_to_command {
            # not loading the pxe rom file
            my $extra = (!$conf->{boot} || ($conf->{boot} !~ m/n/)) ?
                "romfile=," : '';
-           push @$cmd, '-device', "$device,${extra}mac=$net->{macaddr},netdev=${k}";
+           $pciaddr = print_pci_addr("${k}");
+           push @$cmd, '-device', "$device,${extra}mac=$net->{macaddr},netdev=${k}$pciaddr";
        }
-    } 
-       
+    }
+
     push @$cmd, '-net', 'none' if !$foundnet;
 
     # hack: virtio with fairsched is unreliable, so we do not use fairsched
     # when the VM uses virtio devices.
-    if (!$use_virtio && $have_ovz) { 
-     
-       my $cpuunits = defined ($conf->{cpuunits}) ? 
+    if (!$use_virtio && $have_ovz) {
+
+       my $cpuunits = defined($conf->{cpuunits}) ?
            $conf->{cpuunits} : $defaults->{cpuunits};
 
        push @$cmd, '-cpuunits', $cpuunits if $cpuunits;
@@ -2235,13 +2159,13 @@ sub config_to_command {
 
     # add custom args
     if ($conf->{args}) {
-       my $aa = split_args ($conf->{args});
+       my $aa = PVE::Tools::split_args($conf->{args});
        push @$cmd, @$aa;
     }
 
     return wantarray ? ($cmd, $vollist) : $cmd;
 }
+
 sub vnc_socket {
     my ($vmid) = @_;
     return "${var_run_tmpdir}/$vmid.vnc";
@@ -2257,63 +2181,125 @@ sub pidfile_name {
     return "${var_run_tmpdir}/$vmid.pid";
 }
 
-sub random_ether_addr {
+sub next_migrate_port {
+
+    for (my $p = 60000; $p < 60010; $p++) {
 
-    my $rand = Digest::SHA1::sha1_hex (rand(), time());
+       my $sock = IO::Socket::INET->new(Listen => 5,
+                                        LocalAddr => 'localhost',
+                                        LocalPort => $p,
+                                        ReuseAddr => 1,
+                                        Proto     => 0);
 
-    my $mac = '';
-    for (my $i = 0; $i < 6; $i++) {
-       my $ss = hex (substr ($rand, $i*2, 2));
-       if (!$i) {
-           $ss &= 0xfe; # clear multicast
-           $ss |= 2; # set local id
+       if ($sock) {
+           close($sock);
+           return $p;
        }
-       $ss = sprintf ("%02X", $ss);
+    }
 
-       if (!$i) {
-           $mac .= "$ss";
-       } else {
-           $mac .= ":$ss";
+    die "unable to find free migration 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;
+    
+    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;
        }
     }
+
+    return $devices;
+}
+
+sub vm_deviceadd {
+    my ($storecfg, $conf, $vmid, $deviceid, $device) = @_;
+    return if !check_running($vmid) || !$conf->{hotplug} || $conf->{$deviceid};
     
-    return $mac;
+    if($deviceid =~ m/^(virtio)(\d+)$/) {
+
+        my $drive = print_drive_full($storecfg, $vmid, $device);
+        my $ret = vm_monitor_command($vmid, "drive_add auto $drive");
+        # If the command succeeds qemu prints: "OK"
+        if ($ret !~ m/OK/s) {
+           die "adding drive failed: $ret";
+        }
+       
+        my $devicefull = print_drivedevice_full($storecfg, $vmid, $device);
+        $ret = vm_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 
+        die 'error on hotplug device : $ret' if $ret ne "";
+    }
+
+    for (my $i = 0; $i <= 5; $i++) {
+        my $devices_list = vm_devices_list($vmid);
+        return if defined($devices_list->{$deviceid});   
+        sleep 1;
+    }
+       
+    die "error on hotplug device $deviceid";
 }
 
-sub next_migrate_port {
+sub vm_devicedel {
+    my ($vmid, $conf, $deviceid) = @_;
 
-    for (my $p = 60000; $p < 60010; $p++) {
+    return if !check_running ($vmid) || !$conf->{hotplug};
 
-       my $sock = IO::Socket::INET->new (Listen => 5,
-                                         LocalAddr => 'localhost',
-                                         LocalPort => $p,
-                                         ReuseAddr => 1,
-                                         Proto     => 0);
+    die "can't unplug bootdisk" if $conf->{bootdisk} eq $deviceid;
+
+    if($deviceid =~ m/^(virtio)(\d+)$/){
+
+        my $ret = vm_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 
+        }
+        elsif ($ret ne "") {
+            die "deleting drive $deviceid failed : $ret";
+        }
+
+        $ret = vm_monitor_command($vmid, "device_del $deviceid");
+        $ret =~ s/^\s+//;
+        die 'detaching device $deviceid failed : $ret' if $ret ne "";
 
-       if ($sock) {
-           close ($sock);
-           return $p;
-       }
     }
 
-    die "unable to find free migration port";
+    #need to verify the device is correctly remove as device_del is async and empty return is not reliable
+    for (my $i = 0; $i <= 5; $i++) {
+        my $devices_list = vm_devices_list($vmid);
+        return if !defined($devices_list->{$deviceid});
+        sleep 1;
+    }
+    die "error on hot-plugging device $deviceid";
+
+
 }
 
 sub vm_start {
     my ($storecfg, $vmid, $statefile, $skiplock) = @_;
 
-    lock_config ($vmid, sub {
-       my $conf = load_config ($vmid);
+    lock_config($vmid, sub {
+       my $conf = load_config($vmid);
 
-       check_lock ($conf) if !$skiplock;
+       check_lock($conf) if !$skiplock;
 
-       if (check_running ($vmid)) {
-           my $msg = "VM $vmid already running - start failed\n" ;
-           syslog ('err', $msg);
-           die $msg;
-       } else {
-           syslog ('info', "VM $vmid start");
-       }
+       die "VM $vmid already running\n" if check_running($vmid);
 
        my $migrate_uri;
        my $migrate_port = 0;
@@ -2333,7 +2319,7 @@ sub vm_start {
 
        my $defaults = load_defaults();
 
-       my ($cmd, $vollist) = config_to_command ($storecfg, $vmid, $conf, $defaults, $migrate_uri);
+       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"});
@@ -2347,15 +2333,9 @@ sub vm_start {
 
        PVE::Storage::activate_volumes($storecfg, $vollist);
 
-       eval  { run_command ($cmd, timeout => $migrate_uri ? undef : 30); };
-
+       eval  { run_command($cmd, timeout => $migrate_uri ? undef : 30); };
        my $err = $@;
-
-       if ($err) {
-           my $msg = "start failed: $err";
-           syslog ('err', "VM $vmid $msg");
-           die $msg;
-       }
+       die "start failed: $err" if $err;
 
        if ($statefile) {
 
@@ -2364,21 +2344,26 @@ sub vm_start {
            } else {
                unlink $statefile;
                # fixme: send resume - is that necessary ?
-               eval  { vm_monitor_command ($vmid, "cont", 1) };
+               eval { vm_monitor_command($vmid, "cont"); };
            }
        }
-
-       if (my $migrate_speed = 
-           $conf->{migrate_speed} || $defaults->{migrate_speed}) {
+       
+       # 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";
-           eval { vm_monitor_command ($vmid, $cmd, 1); };
-       }
+           vm_monitor_command($vmid, $cmd); 
+       };
 
-       if (my $migrate_downtime = 
+       if (my $migrate_downtime =
            $conf->{migrate_downtime} || $defaults->{migrate_downtime}) {
            my $cmd = "migrate_set_downtime ${migrate_downtime}";
-           eval { vm_monitor_command ($vmid, $cmd, 1); };
+           eval { vm_monitor_command($vmid, $cmd); };
        }
+
+       vm_balloonset($vmid, $conf->{balloon}) if $conf->{balloon};
     });
 }
 
@@ -2386,15 +2371,15 @@ sub __read_avail {
     my ($fh, $timeout) = @_;
 
     my $sel = new IO::Select;
-    $sel->add ($fh);
+    $sel->add($fh);
 
     my $res = '';
     my $buf;
 
     my @ready;
-    while (scalar (@ready = $sel->can_read ($timeout))) {
+    while (scalar (@ready = $sel->can_read($timeout))) {
        my $count;
-       if ($count = $fh->sysread ($buf, 8192)) {
+       if ($count = $fh->sysread($buf, 8192)) {
            if ($buf =~ /^(.*)\(qemu\) $/s) {
                $res .= $1;
                last;
@@ -2402,78 +2387,76 @@ sub __read_avail {
                $res .= $buf;
            }
        } else {
-           if (!defined ($count)) {
+           if (!defined($count)) {
                die "$!\n";
            }
            last;
        }
     }
 
-    die "monitor read timeout\n" if !scalar (@ready);
+    die "monitor read timeout\n" if !scalar(@ready);
 
     return $res;
 }
 
 sub vm_monitor_command {
-    my ($vmid, $cmdstr, $nolog) = @_;
+    my ($vmid, $cmdstr, $nocheck) = @_;
 
     my $res;
 
-    syslog ("info", "VM $vmid monitor command '$cmdstr'") if !$nolog;
-
     eval {
-       die "VM not running\n"  if !check_running ($vmid);
+       die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
 
-       my $sname = monitor_socket ($vmid);
+       my $sname = monitor_socket($vmid);
 
-       my $sock = IO::Socket::UNIX->new ( Peer => $sname ) ||
+       my $sock = IO::Socket::UNIX->new( Peer => $sname ) ||
            die "unable to connect to VM $vmid socket - $!\n";
 
        my $timeout = 3;
 
-       # hack: migrate sometime blocks the monitor (when migrate_downtime 
+       # 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 = __read_avail ($sock, $timeout);
-       
+       my $data = __read_avail($sock, $timeout);
+
        if ($data !~ m/^QEMU\s+(\S+)\s+monitor\s/) {
            die "got unexpected qemu monitor banner\n";
        }
 
        my $sel = new IO::Select;
-       $sel->add ($sock);
+       $sel->add($sock);
 
-       if (!scalar (my @ready = $sel->can_write ($timeout))) {
+       if (!scalar(my @ready = $sel->can_write($timeout))) {
            die "monitor write error - timeout";
        }
 
        my $fullcmd = "$cmdstr\r";
 
        my $b;
-       if (!($b = $sock->syswrite ($fullcmd)) || ($b != length ($fullcmd))) {
+       if (!($b = $sock->syswrite($fullcmd)) || ($b != length($fullcmd))) {
            die "monitor write error - $!";
        }
 
        return if ($cmdstr eq 'q') || ($cmdstr eq 'quit');
 
-       $timeout = 20; 
+       $timeout = 20;
 
        if ($cmdstr =~ m/^(info\s+migrate|migrate\s)/) {
            $timeout = 60*60; # 1 hour
        } elsif ($cmdstr =~ m/^(eject|change)/) {
            $timeout = 60; # note: cdrom mount command is slow
        }
-       if ($res = __read_avail ($sock, $timeout)) {
-  
-           my @lines = split ("\r?\n", $res);
+       if ($res = __read_avail($sock, $timeout)) {
+
+           my @lines = split("\r?\n", $res);
 
            shift @lines if $lines[0] !~ m/^unknown command/; # skip echo
-           
-           $res = join ("\n", @lines);
+
+           $res = join("\n", @lines);
            $res .= "\n";
        }
     };
@@ -2481,7 +2464,7 @@ sub vm_monitor_command {
     my $err = $@;
 
     if ($err) {
-       syslog ("err", "VM $vmid monitor command failed - $err");
+       syslog("err", "VM $vmid monitor command failed - $err");
        die $err;
     }
 
@@ -2491,239 +2474,265 @@ sub vm_monitor_command {
 sub vm_commandline {
     my ($storecfg, $vmid) = @_;
 
-    my $conf = load_config ($vmid);
+    my $conf = load_config($vmid);
 
     my $defaults = load_defaults();
 
-    my $cmd = config_to_command ($storecfg, $vmid, $conf, $defaults);
+    my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults);
 
-    return join (' ', @$cmd);
+    return join(' ', @$cmd);
 }
 
 sub vm_reset {
     my ($vmid, $skiplock) = @_;
 
-    lock_config ($vmid, sub {
+    lock_config($vmid, sub {
 
-       my $conf = load_config ($vmid);
+       my $conf = load_config($vmid);
 
-       check_lock ($conf) if !$skiplock;
+       check_lock($conf) if !$skiplock;
+
+       vm_monitor_command($vmid, "system_reset");
+    });
+}
+
+sub get_vm_volumes {
+    my ($conf) = @_;
+
+    my $vollist = [];
+    foreach_drive($conf, sub {
+       my ($ds, $drive) = @_;
 
-       syslog ("info", "VM $vmid sending 'reset'");
+       my ($sid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1);
+       return if !$sid;
 
-       vm_monitor_command ($vmid, "system_reset", 1);
+       my $volid = $drive->{file};
+       return if !$volid || $volid =~ m|^/|;
+
+       push @$vollist, $volid;
     });
+
+    return $vollist;
+}
+
+sub vm_stop_cleanup {
+    my ($storecfg, $vmid, $conf) = @_;
+
+    fairsched_rmnod($vmid); # try to destroy group
+
+    my $vollist = get_vm_volumes($conf);
+    PVE::Storage::deactivate_volumes($storecfg, $vollist);
 }
 
 sub vm_shutdown {
-    my ($vmid, $skiplock) = @_;
+    my ($storecfg, $vmid, $skiplock, $timeout) = @_;
 
-    lock_config ($vmid, sub {
+    $timeout = 60 if !$timeout;
 
-       my $conf = load_config ($vmid);
+    lock_config($vmid, sub {
 
-       check_lock ($conf) if !$skiplock;
+       my $conf = load_config($vmid);
 
-       syslog ("info", "VM $vmid sending 'shutdown'");
+       check_lock($conf) if !$skiplock;
 
-       vm_monitor_command ($vmid, "system_powerdown", 1);
-    });
-}
+       vm_monitor_command($vmid, "system_powerdown");
 
-sub vm_stop {
-    my ($vmid, $skiplock) = @_;
+       my $pid = check_running($vmid);
 
-    lock_config ($vmid, sub {
+       if ($pid && $timeout) {
+           print "waiting until VM $vmid stopps (PID $pid)\n";
 
-       my $pid = check_running ($vmid);
+           my $count = 0;
+           while (($count < $timeout) && check_running($vmid)) {
+               $count++;
+               sleep 1;
+           }
 
-       if (!$pid) {
-           syslog ('info', "VM $vmid already stopped");
-           return;
+           die "shutdown failed - got timeout\n" if check_running($vmid);
        }
 
-       my $conf = load_config ($vmid);
+       vm_stop_cleanup($storecfg, $vmid, $conf);
+    });
+}
 
-       check_lock ($conf) if !$skiplock;
-    
-       syslog ("info", "VM $vmid stopping");
+# Note: use $nockeck to skip tests if VM configuration file exists.
+# We need that when migration VMs to other nodes (files already moved) 
+sub vm_stop {
+    my ($storecfg, $vmid, $skiplock, $nocheck, $timeout) = @_;
 
-       eval { vm_monitor_command ($vmid, "quit", 1); };
+    $timeout = 60 if !$timeout;
 
+    lock_config($vmid, sub {
+
+       my $pid = check_running($vmid, $nocheck);
+       return if !$pid;
+
+       my $conf;
+       if (!$nocheck) {
+           $conf = load_config($vmid);
+           check_lock($conf) if !$skiplock;
+       }
+
+       eval { vm_monitor_command($vmid, "quit", $nocheck); };
        my $err = $@;
 
        if (!$err) {
-           # wait some time
-           my $timeout = 50; # fixme: how long?
-
            my $count = 0;
-           while (($count < $timeout) && check_running ($vmid)) {
+           while (($count < $timeout) && check_running($vmid, $nocheck)) {
                $count++;
                sleep 1;
            }
 
            if ($count >= $timeout) {
-               syslog ('info', "VM $vmid still running - terminating now with SIGTERM");
+               warn "VM still running - terminating now with SIGTERM\n";
                kill 15, $pid;
            }
        } else {
-           syslog ('info', "VM $vmid quit failed - terminating now with SIGTERM");
+           warn "VM quit failed - terminating now with SIGTERM\n";
            kill 15, $pid;
        }
 
        # wait again
-       my $timeout = 10;
+       $timeout = 10;
 
        my $count = 0;
-       while (($count < $timeout) && check_running ($vmid)) {
+       while (($count < $timeout) && check_running($vmid, $nocheck)) {
            $count++;
            sleep 1;
        }
 
        if ($count >= $timeout) {
-           syslog ('info', "VM $vmid still running - terminating now with SIGKILL\n");
+           warn "VM still running - terminating now with SIGKILL\n";
            kill 9, $pid;
+           sleep 1;
        }
 
-       fairsched_rmnod ($vmid); # try to destroy group
-    });
+       vm_stop_cleanup($storecfg, $vmid, $conf) if $conf;
+   });
 }
 
 sub vm_suspend {
     my ($vmid, $skiplock) = @_;
 
-    lock_config ($vmid, sub {
-
-       my $conf = load_config ($vmid);
+    lock_config($vmid, sub {
 
-       check_lock ($conf) if !$skiplock;
+       my $conf = load_config($vmid);
 
-       syslog ("info", "VM $vmid suspend");
+       check_lock($conf) if !$skiplock;
 
-       vm_monitor_command ($vmid, "stop", 1);
+       vm_monitor_command($vmid, "stop");
     });
 }
 
 sub vm_resume {
     my ($vmid, $skiplock) = @_;
 
-    lock_config ($vmid, sub {
-
-       my $conf = load_config ($vmid);
+    lock_config($vmid, sub {
 
-       check_lock ($conf) if !$skiplock;
+       my $conf = load_config($vmid);
 
-       syslog ("info", "VM $vmid resume");
+       check_lock($conf) if !$skiplock;
 
-       vm_monitor_command ($vmid, "cont", 1);
+       vm_monitor_command($vmid, "cont");
     });
 }
 
-sub vm_cad {
-    my ($vmid, $skiplock) = @_;
+sub vm_sendkey {
+    my ($vmid, $skiplock, $key) = @_;
 
-    lock_config ($vmid, sub {
+    lock_config($vmid, sub {
 
-       my $conf = load_config ($vmid);
+       my $conf = load_config($vmid);
 
-       check_lock ($conf) if !$skiplock;
+       check_lock($conf) if !$skiplock;
 
-       syslog ("info", "VM $vmid sending cntl-alt-delete");
-    
-       vm_monitor_command ($vmid, "sendkey ctrl-alt-delete", 1);
+       vm_monitor_command($vmid, "sendkey $key");
     });
 }
 
 sub vm_destroy {
     my ($storecfg, $vmid, $skiplock) = @_;
 
-    lock_config ($vmid, sub {
-
-       my $conf = load_config ($vmid);
+    lock_config($vmid, sub {
 
-       check_lock ($conf) if !$skiplock;
+       my $conf = load_config($vmid);
 
-       syslog ("info", "VM $vmid destroy called (removing all data)");
+       check_lock($conf) if !$skiplock;
 
-       eval {
-           if (!check_running($vmid)) {
-               fairsched_rmnod($vmid); # try to destroy group
-               destroy_vm($storecfg, $vmid);
-           } else {
-               die "VM is running\n";
-           }
-       };
-
-       my $err = $@;
-
-       if ($err) {
-           syslog ("err", "VM $vmid destroy failed - $err");
-           die $err;
+       if (!check_running($vmid)) {
+           fairsched_rmnod($vmid); # try to destroy group
+           destroy_vm($storecfg, $vmid);
+       } else {
+           die "VM $vmid is running - destroy failed\n";
        }
     });
 }
 
 sub vm_stopall {
-    my ($timeout) = @_;
+    my ($storecfg, $timeout) = @_;
 
     $timeout = 3*60 if !$timeout;
 
+    my $cleanuphash = {};
+
     my $vzlist = vzlist();
     my $count = 0;
     foreach my $vmid (keys %$vzlist) {
        next if !$vzlist->{$vmid}->{pid};
        $count++;
+       $cleanuphash->{$vmid} = 1;
     }
 
-    if ($count) {
+    return if !$count;
 
-       my $msg = "Stopping Qemu Server - sending shutdown requests to all VMs\n";
-       syslog ('info', $msg);
-       print STDERR $msg;
+    my $msg = "Stopping Qemu Server - sending shutdown requests to all VMs\n";
+    syslog('info', $msg);
+    warn $msg;
 
-       foreach my $vmid (keys %$vzlist) {
-           next if !$vzlist->{$vmid}->{pid};
-           eval { vm_shutdown ($vmid, 1); };
-           print STDERR $@ if $@;
+    foreach my $vmid (keys %$vzlist) {
+       next if !$vzlist->{$vmid}->{pid};
+       eval { vm_shutdown($storecfg, $vmid, 1); };
+       my $err = $@;
+       if ($err) {
+           warn $err;
+       } else {
+           delete $cleanuphash->{$vmid};
        }
+    }
 
-       my $wt = 5;
-       my $maxtries = int (($timeout + $wt -1)/$wt);
-       my $try = 0;
-       while (($try < $maxtries) && $count) {
-           $try++;
-           sleep $wt;
-
-           $vzlist = vzlist();
-           $count = 0;
-           foreach my $vmid (keys %$vzlist) {
-               next if !$vzlist->{$vmid}->{pid};
-               $count++;
-           }
-           last if !$count;
+    my $wt = 5;
+    my $maxtries = int(($timeout + $wt -1)/$wt);
+    my $try = 0;
+    while (($try < $maxtries) && $count) {
+       $try++;
+       sleep $wt;
+       
+       $vzlist = vzlist();
+       $count = 0;
+       foreach my $vmid (keys %$vzlist) {
+           next if !$vzlist->{$vmid}->{pid};
+           $count++;
        }
+       last if !$count;
+    }
 
-       return if !$count;
+    if ($count) {
 
        foreach my $vmid (keys %$vzlist) {
            next if !$vzlist->{$vmid}->{pid};
-       
-           $msg = "VM $vmid still running - sending stop now\n";
-           syslog ('info', $msg);
-           print $msg;
-
-           eval { vm_monitor_command ($vmid, "quit", 1); };
-           print STDERR $@ if $@;
 
+           warn "VM $vmid still running - sending stop now\n";
+           eval { vm_monitor_command($vmid, "quit"); };
+           warn $@ if $@;
        }
 
        $timeout = 30;
-       $maxtries = int (($timeout + $wt -1)/$wt);
+       $maxtries = int(($timeout + $wt -1)/$wt);
        $try = 0;
        while (($try < $maxtries) && $count) {
            $try++;
            sleep $wt;
-
+       
            $vzlist = vzlist();
            $count = 0;
            foreach my $vmid (keys %$vzlist) {
@@ -2733,24 +2742,34 @@ sub vm_stopall {
            last if !$count;
        }
 
-       return if !$count;
+       if ($count) {
 
-       foreach my $vmid (keys %$vzlist) {
-           next if !$vzlist->{$vmid}->{pid};
-       
-           $msg = "VM $vmid still running - terminating now with SIGTERM\n";
-           syslog ('info', $msg);
-           print $msg;
-           kill 15, $vzlist->{$vmid}->{pid};
+           foreach my $vmid (keys %$vzlist) {
+               next if !$vzlist->{$vmid}->{pid};
+
+               warn "VM $vmid still running - terminating now with SIGTERM\n";
+               kill 15, $vzlist->{$vmid}->{pid};
+           }
+           sleep 1;
        }
 
        # this is called by system shotdown scripts, so remaining
        # processes gets killed anyways (no need to send kill -9 here)
+    }
 
-       $msg = "Qemu Server stopped\n";
-       syslog ('info', $msg);
-       print STDERR $msg;
+    $vzlist = vzlist();
+    foreach my $vmid (keys %$cleanuphash) {
+       next if $vzlist->{$vmid}->{pid};
+       eval { 
+           my $conf = load_config($vmid);
+           vm_stop_cleanup($storecfg, $vmid, $conf); 
+       };
+       warn $@ if $@;
     }
+
+    $msg = "Qemu Server stopped\n";
+    syslog('info', $msg);
+    print $msg;
 }
 
 # pci helpers
@@ -2758,7 +2777,7 @@ sub vm_stopall {
 sub file_write {
     my ($filename, $buf) = @_;
 
-    my $fh = IO::File->new ($filename, "w");
+    my $fh = IO::File->new($filename, "w");
     return undef if !$fh;
 
     my $res = print $fh $buf;
@@ -2807,7 +2826,7 @@ sub pci_dev_reset {
 
     my $fn = "$pcisysfs/devices/$name/reset";
 
-    return file_write ($fn, "1");
+    return file_write($fn, "1");
 }
 
 sub pci_dev_bind_to_stub {
@@ -2819,19 +2838,246 @@ sub pci_dev_bind_to_stub {
     return 1 if -d $testdir;
 
     my $data = "$dev->{vendor} $dev->{product}";
-    return undef if !file_write ("$pcisysfs/drivers/pci-stub/new_id", $data);
+    return undef if !file_write("$pcisysfs/drivers/pci-stub/new_id", $data);
 
     my $fn = "$pcisysfs/devices/$name/driver/unbind";
-    if (!file_write ($fn, $name)) {
+    if (!file_write($fn, $name)) {
        return undef if -f $fn;
     }
 
     $fn = "$pcisysfs/drivers/pci-stub/bind";
     if (! -d $testdir) {
-       return undef if !file_write ($fn, $name);
+       return undef if !file_write($fn, $name);
     }
 
     return -d $testdir;
 }
 
+sub print_pci_addr { 
+    my ($id) = @_;
+
+    my $res = '';
+    my $devices = {
+       #addr1 : ide,parallel,serial (motherboard)
+       #addr2 : first videocard
+       balloon0 => { bus => 0, addr => 3 },
+       watchdog => { bus => 0, addr => 4 },
+       scsi0 => { bus => 0, addr => 5 },
+       scsi1 => { bus => 0, addr => 6 },
+       virtio0 => { bus => 0, addr => 10 },
+       virtio1 => { bus => 0, addr => 11 },
+       virtio2 => { bus => 0, addr => 12 },
+       virtio3 => { bus => 0, addr => 13 },
+       virtio4 => { bus => 0, addr => 14 },
+       virtio5 => { bus => 0, addr => 15 },
+       hostpci0 => { bus => 0, addr => 16 },
+       hostpci1 => { bus => 0, addr => 17 },
+       net0 => { bus => 0, addr => 18 },
+       net1 => { bus => 0, addr => 19 },
+       net2 => { bus => 0, addr => 20 },
+       net3 => { bus => 0, addr => 21 },
+       net4 => { bus => 0, addr => 22 },
+       net5 => { bus => 0, addr => 23 },
+       #addr29 : usb-host (pve-usb.cfg)
+    };
+
+    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";
+    }
+    return $res;
+
+}
+
+sub vm_balloonset {
+    my ($vmid, $value) = @_;
+
+    vm_monitor_command($vmid, "balloon $value");
+}
+
+# vzdump restore implementaion
+
+sub archive_read_firstfile {
+    my $archive = shift;
+    
+    die "ERROR: file '$archive' does not exist\n" if ! -f $archive;
+
+    # try to detect archive type first
+    my $pid = open (TMP, "tar tf '$archive'|") ||
+       die "unable to open file '$archive'\n";
+    my $firstfile = <TMP>;
+    kill 15, $pid;
+    close TMP;
+
+    die "ERROR: archive contaions no data\n" if !$firstfile;
+    chomp $firstfile;
+
+    return $firstfile;
+}
+
+sub restore_cleanup {
+    my $statfile = shift;
+
+    print STDERR "starting cleanup\n";
+
+    if (my $fd = IO::File->new($statfile, "r")) {
+       while (defined(my $line = <$fd>)) {
+           if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
+               my $volid = $2;
+               eval {
+                   if ($volid =~ m|^/|) {
+                       unlink $volid || die 'unlink failed\n';
+                   } else {
+                       my $cfg = cfs_read_file('storage.cfg');
+                       PVE::Storage::vdisk_free($cfg, $volid);
+                   }
+                   print STDERR "temporary volume '$volid' sucessfuly removed\n";  
+               };
+               print STDERR "unable to cleanup '$volid' - $@" if $@;
+           } else {
+               print STDERR "unable to parse line in statfile - $line";
+           }   
+       }
+       $fd->close();
+    }
+}
+
+sub restore_archive {
+    my ($archive, $vmid, $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 .= ' --prealloc' if $opts->{prealloc};
+    $tocmd .= ' --info' if $opts->{info};
+
+    # tar option "xf" does not autodetect compression when read fron 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;
+
+    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);
+       }
+
+       return if $opts->{info};
+
+       # read new mapping
+       my $map = {};
+       my $statfile = "$tmpdir/qmrestore.stat";
+       if (my $fd = IO::File->new($statfile, "r")) {
+           while (defined (my $line = <$fd>)) {
+               if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
+                   $map->{$1} = $2 if $1;
+               } else {
+                   print STDERR "unable to parse line in statfile - $line\n";
+               }
+           }
+           $fd->close();
+       }
+
+       my $confsrc = "$tmpdir/qemu-server.conf";
+
+       my $srcfd = new IO::File($confsrc, "r") ||
+           die "unable to open file '$confsrc'\n";
+
+       my $outfd = new IO::File ($tmpfn, "w") ||
+           die "unable to write config for VM $vmid\n";
+
+       my $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;
+           }
+       }
+
+       $srcfd->close();
+       $outfd->close();
+    };
+    my $err = $@;
+
+    if ($err) {        
+
+       unlink $tmpfn;
+
+       restore_cleanup("$tmpdir/qmrestore.stat") if !$opts->{info};
+       
+       die $err;
+    } 
+
+    rmtree $tmpdir;
+
+    rename $tmpfn, $conffile ||
+       die "unable to commit configuration file '$conffile'\n";
+};
+
 1;