]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
activate LVM LVs more carefully
[qemu-server.git] / PVE / QemuServer.pm
index 450b57df03a9bc5d795c717ebe9cbfff4ea7bdab..8bee83e73de4e20ebc52764374e72ff41b3f0a0e 100644 (file)
@@ -153,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',
@@ -431,7 +437,7 @@ 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);
@@ -439,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);
@@ -447,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);
@@ -631,32 +637,6 @@ sub os_list_description {
     };
 }
 
-# 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 {
     my $dev = shift;
 
@@ -846,7 +826,7 @@ sub parse_drive {
     return undef if !$res->{file};
 
     return undef if $res->{cache} &&
-       $res->{cache} !~ m/^(off|none|writethrough|writeback)$/;
+       $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+$/;
@@ -894,7 +874,7 @@ sub print_drivedevice_full {
 
     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=device-$drive->{interface}$drive->{index}$pciaddr";
+      $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
     }
 
     elsif ($drive->{interface} eq 'scsi') {
@@ -984,7 +964,7 @@ sub parse_net {
 
        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 $mac = uc($3) || PVE::Tools::random_ether_addr();
            $res->{model} = $model;
            $res->{macaddr} = $mac;
        } elsif ($kvp =~ m/^bridge=(\S+)$/) {
@@ -1215,9 +1195,11 @@ sub lock_config {
 
     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 {
@@ -1257,7 +1239,7 @@ sub touch_config {
 }
 
 sub create_disks {
-    my ($storecfg, $vmid, $settings) = @_;
+    my ($storecfg, $vmid, $settings, $conf, $default_storage) = @_;
 
     my $vollist = [];
 
@@ -1270,7 +1252,7 @@ sub create_disks {
            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 $fmt = $disk->{format} || $defformat;
@@ -1294,6 +1276,7 @@ sub create_disks {
                    die "image '$path' does not exists\n";
                }
            }
+           PVE::QemuServer::vm_deviceadd($storecfg, $conf, $vmid, $ds, $disk) if defined($conf);
        });
     };
 
@@ -1311,26 +1294,8 @@ 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);
 
@@ -1345,15 +1310,19 @@ sub destroy_vm {
        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);
+       return if !$path || !$owner || ($owner != $vmid);
 
        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 {
@@ -1811,6 +1780,8 @@ sub vmstatus {
     my $list = vzlist();
     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);
 
@@ -1833,12 +1804,13 @@ sub vmstatus {
        }
 
        $d->{cpus} = ($conf->{sockets} || 1) * ($conf->{cores} || 1);
+       $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;
@@ -1861,7 +1833,6 @@ sub vmstatus {
        $d->{netin} += $netdev->{$dev}->{transmit};
     }
 
-    my $cpucount = $cpuinfo->{cpus} || 1;
     my $ctime = gettimeofday;
 
     foreach my $vmid (keys %$list) {
@@ -1887,8 +1858,6 @@ sub vmstatus {
 
        my $used = $pstat->{utime} + $pstat->{stime};
 
-       my $vcpus = $d->{cpus} > $cpucount ? $cpucount : $d->{cpus};
-
        $d->{uptime} = int(($uptime - $pstat->{starttime})/$cpuinfo->{user_hz});
 
        if ($pstat->{vsize}) {
@@ -1901,7 +1870,6 @@ sub vmstatus {
                time => $ctime,
                used => $used,
                cpu => 0,
-               relcpu => 0,
            };
            next;
        }
@@ -1911,17 +1879,14 @@ sub vmstatus {
        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,
                used => $used,
                cpu => $d->{cpu},
-               relcpu => $d->{relcpu},
            };
        } else {
            $d->{cpu} = $old->{cpu};
-           $d->{relcpu} = $old->{relcpu};
        }
     }
 
@@ -1945,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+)$/) {
@@ -1986,7 +1951,8 @@ sub config_to_command {
     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
@@ -2098,12 +2064,14 @@ sub config_to_command {
     #my $soundhw = $conf->{soundhw} || $defaults->{soundhw};
     #push @$cmd, '-soundhw', 'es1370';
     #push @$cmd, '-soundhw', $soundhw if $soundhw;
-
-    push @$cmd, '-device', 'virtio-balloon-pci,id=balloon0' if $conf->{balloon};
+    $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};
     }
 
@@ -2113,17 +2081,17 @@ 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;
+          $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);
        $tmp .= ",boot=on" if $conf->{bootdisk} && ($conf->{bootdisk} eq $ds);
@@ -2169,7 +2137,8 @@ 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";
        }
     }
 
@@ -2190,7 +2159,7 @@ 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;
     }
 
@@ -2212,29 +2181,6 @@ sub pidfile_name {
     return "${var_run_tmpdir}/$vmid.pid";
 }
 
-sub random_ether_addr {
-
-    my $rand = Digest::SHA1::sha1_hex(rand(), time());
-
-    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
-       }
-       $ss = sprintf("%02X", $ss);
-
-       if (!$i) {
-           $mac .= "$ss";
-       } else {
-           $mac .= ":$ss";
-       }
-    }
-
-    return $mac;
-}
-
 sub next_migrate_port {
 
     for (my $p = 60000; $p < 60010; $p++) {
@@ -2254,6 +2200,97 @@ sub next_migrate_port {
     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};
+    
+    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 vm_devicedel {
+    my ($vmid, $conf, $deviceid) = @_;
+
+    return if !check_running ($vmid) || !$conf->{hotplug};
+
+    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 "";
+
+    }
+
+    #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) = @_;
 
@@ -2262,13 +2299,7 @@ sub vm_start {
 
        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;
@@ -2303,14 +2334,8 @@ sub vm_start {
        PVE::Storage::activate_volumes($storecfg, $vollist);
 
        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) {
 
@@ -2319,20 +2344,23 @@ 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 =
            $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};
@@ -2372,14 +2400,12 @@ sub __read_avail {
 }
 
 sub vm_monitor_command {
-    my ($vmid, $cmdstr, $nolog, $nocheck) = @_;
+    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, $nocheck);
+       die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
 
        my $sname = monitor_socket($vmid);
 
@@ -2466,14 +2492,42 @@ sub vm_reset {
 
        check_lock($conf) if !$skiplock;
 
-       syslog("info", "VM $vmid sending 'reset'");
+       vm_monitor_command($vmid, "system_reset");
+    });
+}
 
-       vm_monitor_command($vmid, "system_reset", 1);
+sub get_vm_volumes {
+    my ($conf) = @_;
+
+    my $vollist = [];
+    foreach_drive($conf, sub {
+       my ($ds, $drive) = @_;
+
+       my ($sid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1);
+       return if !$sid;
+
+       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) = @_;
+
+    $timeout = 60 if !$timeout;
 
     lock_config($vmid, sub {
 
@@ -2481,41 +2535,48 @@ sub vm_shutdown {
 
        check_lock($conf) if !$skiplock;
 
-       syslog("info", "VM $vmid sending 'shutdown'");
+       vm_monitor_command($vmid, "system_powerdown");
+
+       my $pid = check_running($vmid);
+
+       if ($pid && $timeout) {
+           print "waiting until VM $vmid stopps (PID $pid)\n";
+
+           my $count = 0;
+           while (($count < $timeout) && check_running($vmid)) {
+               $count++;
+               sleep 1;
+           }
+
+           die "shutdown failed - got timeout\n" if check_running($vmid);
+       }
 
-       vm_monitor_command($vmid, "system_powerdown", 1);
+       vm_stop_cleanup($storecfg, $vmid, $conf);
     });
 }
 
 # 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 ($vmid, $skiplock, $nocheck) = @_;
+    my ($storecfg, $vmid, $skiplock, $nocheck, $timeout) = @_;
+
+    $timeout = 60 if !$timeout;
 
     lock_config($vmid, sub {
 
        my $pid = check_running($vmid, $nocheck);
+       return if !$pid;
 
-       if (!$pid) {
-           syslog('info', "VM $vmid already stopped");
-           return;
-       }
-
+       my $conf;
        if (!$nocheck) {
-           my $conf = load_config($vmid);
+           $conf = load_config($vmid);
            check_lock($conf) if !$skiplock;
        }
 
-       syslog("info", "VM $vmid stopping");
-
-       eval { vm_monitor_command($vmid, "quit", 1, $nocheck); };
-
+       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, $nocheck)) {
                $count++;
@@ -2523,16 +2584,16 @@ sub vm_stop {
            }
 
            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, $nocheck)) {
@@ -2541,12 +2602,13 @@ sub vm_stop {
        }
 
        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 {
@@ -2558,9 +2620,7 @@ sub vm_suspend {
 
        check_lock($conf) if !$skiplock;
 
-       syslog("info", "VM $vmid suspend");
-
-       vm_monitor_command($vmid, "stop", 1);
+       vm_monitor_command($vmid, "stop");
     });
 }
 
@@ -2573,14 +2633,12 @@ sub vm_resume {
 
        check_lock($conf) if !$skiplock;
 
-       syslog("info", "VM $vmid resume");
-
-       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 {
 
@@ -2588,9 +2646,7 @@ sub vm_cad {
 
        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");
     });
 }
 
@@ -2603,78 +2659,71 @@ sub vm_destroy {
 
        check_lock($conf) if !$skiplock;
 
-       syslog("info", "VM $vmid destroy called (removing all data)");
-
-       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;
@@ -2683,7 +2732,7 @@ sub vm_stopall {
        while (($try < $maxtries) && $count) {
            $try++;
            sleep $wt;
-
+       
            $vzlist = vzlist();
            $count = 0;
            foreach my $vmid (keys %$vzlist) {
@@ -2693,24 +2742,34 @@ sub vm_stopall {
            last if !$count;
        }
 
-       return if !$count;
+       if ($count) {
 
-       foreach my $vmid (keys %$vzlist) {
-           next if !$vzlist->{$vmid}->{pid};
+           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};
+               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
@@ -2799,12 +2858,27 @@ sub print_pci_addr {
 
     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})) {
@@ -2818,7 +2892,192 @@ sub print_pci_addr {
 sub vm_balloonset {
     my ($vmid, $value) = @_;
 
-    vm_monitor_command($vmid, "balloon $value", 1);
+    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;