]> git.proxmox.com Git - qemu-server.git/blobdiff - PVE/QemuServer.pm
add qemu_volume_snapshot_delete
[qemu-server.git] / PVE / QemuServer.pm
index af03bf43162554273f1749134ea9a14ee528afe9..8aee7a1e1ab4ce719c5e959f8a2f68fdf7aaaa90 100644 (file)
@@ -213,6 +213,13 @@ my $confdesc = {
        type => 'string', format => 'dns-name',
        description => "Set a name for the VM. Only used on the configuration web interface.",
     },
+    scsihw => {
+       optional => 1,
+       type => 'string',
+       description => "scsi controller model",
+       enum => [qw(lsi virtio-scsi-pci megasas)],
+       default => 'lsi',
+    },
     description => {
        optional => 1,
        type => 'string',
@@ -251,7 +258,7 @@ EODESC
        optional => 1,
        type => 'string', format => 'pve-qm-bootdisk',
        description => "Enable booting from specified disk.",
-       pattern => '(ide|scsi|virtio)\d+',
+       pattern => '(ide|sata|scsi|virtio)\d+',
     },
     smp => {
        optional => 1,
@@ -280,6 +287,12 @@ EODESC
        description => "Enable/disable ACPI.",
        default => 1,
     },
+    agent => {
+       optional => 1,
+       type => 'boolean',
+       description => "Enable/disable Qemu GuestAgent.",
+       default => 0,
+    },
     kvm => {
        optional => 1,
        type => 'boolean',
@@ -395,10 +408,10 @@ while (my ($k, $v) = each %$confdesc) {
 
 my $MAX_IDE_DISKS = 4;
 my $MAX_SCSI_DISKS = 14;
-my $MAX_VIRTIO_DISKS = 6;
+my $MAX_VIRTIO_DISKS = 16;
 my $MAX_SATA_DISKS = 6;
 my $MAX_USB_DEVICES = 5;
-my $MAX_NETS = 6;
+my $MAX_NETS = 32;
 my $MAX_UNUSED_DISKS = 8;
 my $MAX_HOSTPCI_DEVICES = 2;
 my $MAX_SERIAL_PORTS = 4;
@@ -447,7 +460,7 @@ my $idedesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-drive',
     typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
-    description => "Use volume as IDE hard disk or CD-ROM (n is 0 to 3).",
+    description => "Use volume as IDE hard disk or CD-ROM (n is 0 to " .($MAX_IDE_DISKS -1) . ").",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-ide", $idedesc);
 
@@ -455,7 +468,7 @@ my $scsidesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-drive',
     typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
-    description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to 13).",
+    description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to " . ($MAX_SCSI_DISKS - 1) . ").",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc);
 
@@ -463,7 +476,7 @@ my $satadesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-drive',
     typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
-    description => "Use volume as SATA hard disk or CD-ROM (n is 0 to 5).",
+    description => "Use volume as SATA hard disk or CD-ROM (n is 0 to " . ($MAX_SATA_DISKS - 1). ").",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-sata", $satadesc);
 
@@ -471,7 +484,7 @@ my $virtiodesc = {
     optional => 1,
     type => 'string', format => 'pve-qm-drive',
     typetext => '[volume=]volume,] [,media=cdrom|disk] [,cyls=c,heads=h,secs=s[,trans=t]] [,snapshot=on|off] [,cache=none|writethrough|writeback|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads]',
-    description => "Use volume as VIRTIO hard disk (n is 0 to 5).",
+    description => "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . ").",
 };
 PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
 
@@ -660,45 +673,6 @@ sub os_list_description {
     };
 }
 
-sub disk_devive_info {
-    my $dev = shift;
-
-    die "unknown disk device format '$dev'" if $dev !~ m/^(ide|scsi|virtio)(\d+)$/;
-
-    my $bus = $1;
-    my $index = $2;
-    my $maxdev = 1024;
-
-    if ($bus eq 'ide') {
-       $maxdev = 2;
-    } elsif ($bus eq 'scsi') {
-       $maxdev = 7;
-    }
-
-    my $controller = int($index / $maxdev);
-    my $unit = $index % $maxdev;
-
-
-    return { bus => $bus, desc => uc($bus) . " $controller:$unit",
-            controller => $controller, unit => $unit, index => $index };
-
-}
-
-sub qemu_drive_name {
-    my ($dev, $media) = @_;
-
-    my $info = disk_devive_info($dev);
-    my $mediastr = '';
-
-    if (($info->{bus} eq 'ide') || ($info->{bus} eq 'scsi')) {
-       $mediastr = ($media eq 'cdrom') ? "-cd" : "-hd";
-       return sprintf("%s%i%s%i", $info->{bus}, $info->{controller},
-                      $mediastr, $info->{unit});
-    } else {
-       return sprintf("%s%i", $info->{bus}, $info->{index});
-    }
-}
-
 my $cdrom_path;
 
 sub get_cdrom_path {
@@ -806,6 +780,40 @@ sub create_conf_nolock {
     PVE::Tools::file_set_contents($filename, $data);
 }
 
+my $parse_size = sub {
+    my ($value) = @_;
+
+    return undef if $value !~ m/^(\d+(\.\d+)?)([KMG])?$/;
+    my ($size, $unit) = ($1, $3);
+    if ($unit) {
+       if ($unit eq 'K') {
+           $size = $size * 1024;
+       } elsif ($unit eq 'M') {
+           $size = $size * 1024 * 1024;
+       } elsif ($unit eq 'G') {
+           $size = $size * 1024 * 1024 * 1024;
+       }
+    }
+    return int($size);
+};
+
+my $format_size = sub {
+    my ($size) = @_;
+
+    $size = int($size);
+
+    my $kb = int($size/1024);
+    return $size if $kb*1024 != $size;
+
+    my $mb = int($kb/1024);
+    return "${kb}K" if $mb*1024 != $kb;
+
+    my $gb = int($mb/1024);
+    return "${mb}M" if $gb*1024 != $mb;
+
+    return "${gb}G";
+};
+
 # ideX = [volume=]volume-id[,media=d][,cyls=c,heads=h,secs=s[,trans=t]]
 #        [,snapshot=on|off][,cache=on|off][,format=f][,backup=yes|no]
 #        [,rerror=ignore|report|stop][,werror=enospc|ignore|report|stop]
@@ -830,13 +838,18 @@ sub parse_drive {
     foreach my $p (split (/,/, $data)) {
        next if $p =~ m/^\s*$/;
 
-       if ($p =~ m/^(file|volume|cyls|heads|secs|trans|media|snapshot|cache|format|rerror|werror|backup|aio|bps|bps_rd|bps_wr|iops|iops_rd|iops_wr)=(.+)$/) {
+       if ($p =~ m/^(file|volume|cyls|heads|secs|trans|media|snapshot|cache|format|rerror|werror|backup|aio|bps|mbps|bps_rd|mbps_rd|bps_wr|mbps_wr|iops|iops_rd|iops_wr|size)=(.+)$/) {
            my ($k, $v) = ($1, $2);
 
            $k = 'file' if $k eq 'volume';
 
            return undef if defined $res->{$k};
 
+           if ($k eq 'bps' || $k eq 'bps_rd' || $k eq 'bps_wr') {
+               return undef if !$v || $v !~ m/^\d+/;
+               $k = "m$k";
+               $v = sprintf("%.3f", $v / (1024*1024));
+           }
            $res->{$k} = $v;
        } else {
            if (!$res->{file} && $p !~ m/=/) {
@@ -863,19 +876,25 @@ sub parse_drive {
     return undef if $res->{backup} && $res->{backup} !~ m/^(yes|no)$/;
     return undef if $res->{aio} && $res->{aio} !~ m/^(native|threads)$/;
 
-    return undef if $res->{bps_rd} && $res->{bps};
-    return undef if $res->{bps_wr} && $res->{bps};
+    
+    return undef if $res->{mbps_rd} && $res->{mbps};
+    return undef if $res->{mbps_wr} && $res->{mbps};
+
+    return undef if $res->{mbps} && $res->{mbps} !~ m/^\d+(\.\d+)?$/;
+    return undef if $res->{mbps_rd} && $res->{mbps_rd} !~ m/^\d+(\.\d+)?$/;
+    return undef if $res->{mbps_wr} && $res->{mbps_wr} !~ m/^\d+(\.\d+)?$/;
+
     return undef if $res->{iops_rd} && $res->{iops};
     return undef if $res->{iops_wr} && $res->{iops};
-
-    return undef if $res->{bps} && $res->{bps} !~ m/^\d+$/;
-    return undef if $res->{bps_rd} && $res->{bps_rd} !~ m/^\d+$/;
-    return undef if $res->{bps_wr} && $res->{bps_wr} !~ m/^\d+$/;
     return undef if $res->{iops} && $res->{iops} !~ m/^\d+$/;
     return undef if $res->{iops_rd} && $res->{iops_rd} !~ m/^\d+$/;
     return undef if $res->{iops_wr} && $res->{iops_wr} !~ m/^\d+$/;
 
 
+    if ($res->{size}) {
+       return undef if !defined($res->{size} = &$parse_size($res->{size})); 
+    }
+
     if ($res->{media} && ($res->{media} eq 'cdrom')) {
        return undef if $res->{snapshot} || $res->{trans} || $res->{format};
        return undef if $res->{heads} || $res->{secs} || $res->{cyls};
@@ -890,16 +909,20 @@ sub parse_drive {
     return $res;
 }
 
-my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio bps bps_rd bps_wr iops iops_rd iops_wr);
+my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio iops iops_rd iops_wr);
 
 sub print_drive {
     my ($vmid, $drive) = @_;
 
     my $opts = '';
-    foreach my $o (@qemu_drive_options, 'backup') {
+    foreach my $o (@qemu_drive_options, 'mbps', 'mbps_rd', 'mbps_wr', 'backup') {
        $opts .= ",$o=$drive->{$o}" if $drive->{$o};
     }
 
+    if ($drive->{size}) {
+       $opts .= ",size=" . &$format_size($drive->{size});
+    }
+
     return "$drive->{file}$opts";
 }
 
@@ -962,16 +985,16 @@ sub path_is_scsi {
 }
 
 sub print_drivedevice_full {
-    my ($storecfg, $vmid, $drive) = @_;
+    my ($storecfg, $conf, $vmid, $drive, $bridges) = @_;
 
     my $device = '';
     my $maxdev = 0;
 
     if ($drive->{interface} eq 'virtio') {
-       my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}");
+       my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}", $bridges);
        $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
     } elsif ($drive->{interface} eq 'scsi') {
-       $maxdev = 7;
+       $maxdev = ($conf->{scsihw} && $conf->{scsihw} ne 'lsi') ? 256 : 7;
        my $controller = int($drive->{index} / $maxdev);
        my $unit = $drive->{index} % $maxdev;
        my $devicetype = 'hd';
@@ -984,10 +1007,21 @@ sub print_drivedevice_full {
               } else {
                   $path = PVE::Storage::path($storecfg, $drive->{file});
               }
-             $devicetype = 'block' if path_is_scsi($path);
+
+             if($path =~ m/^iscsi\:\/\//){
+                $devicetype = 'generic';
+             }
+             else {
+                $devicetype = 'block' if path_is_scsi($path);
+             }
          }
 
-       $device = "scsi-$devicetype,bus=lsi$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
+        if (!$conf->{scsihw} || $conf->{scsihw} eq 'lsi'){
+            $device = "scsi-$devicetype,bus=scsihw$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}" if !$conf->{scsihw} || $conf->{scsihw} eq 'lsi';
+        } else {
+            $device = "scsi-$devicetype,bus=scsihw$controller.0,channel=0,scsi-id=0,lun=$drive->{index},drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
+        }
+
     } elsif ($drive->{interface} eq 'ide'){
        $maxdev = 2;
        my $controller = int($drive->{index} / $maxdev);
@@ -1020,6 +1054,11 @@ sub print_drive_full {
        $opts .= ",$o=$drive->{$o}" if $drive->{$o};
     }
 
+    foreach my $o (qw(bps bps_rd bps_wr)) {
+       my $v = $drive->{"m$o"};
+       $opts .= ",$o=" . int($v*1024*1024) if $v;
+    }
+
     # use linux-aio by default (qemu default is threads)
     $opts .= ",aio=native" if !$drive->{aio};
 
@@ -1044,7 +1083,7 @@ sub print_drive_full {
 }
 
 sub print_netdevice_full {
-    my ($vmid, $conf, $net, $netid) = @_;
+    my ($vmid, $conf, $net, $netid, $bridges) = @_;
 
     my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
 
@@ -1056,7 +1095,7 @@ sub print_netdevice_full {
     # qemu > 0.15 always try to boot from network - we disable that by
     # not loading the pxe rom file
     my $extra = ($bootorder !~ m/n/) ? "romfile=," : '';
-    my $pciaddr = print_pci_addr("$netid");
+    my $pciaddr = print_pci_addr("$netid", $bridges);
     my $tmpstr = "$device,${extra}mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
     $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex} ;
     return $tmpstr;
@@ -1489,52 +1528,10 @@ sub destroy_vm {
     warn $@ if $@;
 }
 
-# fixme: remove?
-sub load_diskinfo_old {
-    my ($storecfg, $vmid, $conf) = @_;
-
-    my $info = {};
-    my $res = {};
-    my $vollist;
-
-    foreach_drive($conf, sub {
-       my ($ds, $di) = @_;
-
-       $res->{$ds} = $di;
-
-       return if drive_is_cdrom($di);
-
-       if ($di->{file} =~ m|^/dev/.+|) {
-           $info->{$di->{file}}->{size} = PVE::Storage::file_size_info($di->{file});
-       } else {
-           push @$vollist, $di->{file};
-       }
-    });
-
-    eval {
-       my $dl = PVE::Storage::vdisk_list($storecfg, undef, $vmid, $vollist);
-
-       PVE::Storage::foreach_volid($dl, sub {
-           my ($volid, $sid, $volname, $d) = @_;
-           $info->{$volid} = $d;
-       });
-    };
-    warn $@ if $@;
-
-    foreach my $ds (keys %$res) {
-       my $di = $res->{$ds};
-
-       $res->{$ds}->{disksize} = $info->{$di->{file}} ?
-           $info->{$di->{file}}->{size} / (1024*1024) : 0;
-    }
-
-    return $res;
-}
-
 sub load_config {
-    my ($vmid) = @_;
+    my ($vmid, $node) = @_;
 
-    my $cfspath = cfs_config_path($vmid);
+    my $cfspath = cfs_config_path($vmid, $node);
 
     my $conf = PVE::Cluster::cfs_read_file($cfspath);
 
@@ -1790,9 +1787,9 @@ sub check_cmdline {
 }
 
 sub check_running {
-    my ($vmid, $nocheck) = @_;
+    my ($vmid, $nocheck, $node) = @_;
 
-    my $filename = config_file($vmid);
+    my $filename = config_file($vmid, $node);
 
     die "unable to find configuration file for VM $vmid - no such machine\n"
        if !$nocheck && ! -f $filename;
@@ -1840,8 +1837,6 @@ sub vzlist {
     return $vzlist;
 }
 
-my $storage_timeout_hash = {};
-
 sub disksize {
     my ($storecfg, $conf) = @_;
 
@@ -1859,42 +1854,7 @@ sub disksize {
     my $volid = $drive->{file};
     return undef if !$volid;
 
-    my $path;
-    my $storeid;
-    my $timeoutid;
-
-    if ($volid =~ m|^/|) {
-       $path = $timeoutid = $volid;
-    } else {
-       eval {
-           $storeid = $timeoutid = PVE::Storage::parse_volume_id($volid);
-       };
-       if (my $err = $@) {
-           warn $err;
-           return undef;
-       }
-    }
-
-    my $last_timeout = $storage_timeout_hash->{$timeoutid};
-    if ($last_timeout) {
-       if ((time() - $last_timeout) < 30) {
-           # skip storage with errors
-           return undef ;
-       }
-       delete $storage_timeout_hash->{$timeoutid};
-    }
-
-    my ($size, $format, $used);
-
-    ($size, $format, $used) = PVE::Storage::volume_size_info($storecfg, $volid, 1);
-
-    if (!defined($format)) {
-       # got timeout
-       $storage_timeout_hash->{$timeoutid} = time();
-       return undef;
-    }
-
-    return wantarray ? ($size, $used) : $size;
+    return $drive->{size};
 }
 
 my $last_proc_pid_stat;
@@ -1926,9 +1886,9 @@ sub vmstatus {
        # fixme: better status?
        $d->{status} = $list->{$vmid}->{pid} ? 'running' : 'stopped';
 
-       my ($size, $used) = disksize($storecfg, $conf);
-       if (defined($size) && defined($used)) {
-           $d->{disk} = $used;
+       my $size = disksize($storecfg, $conf);
+       if (defined($size)) {
+           $d->{disk} = 0; # no info available
            $d->{maxdisk} = $size;
        } else {
            $d->{disk} = 0;
@@ -2010,7 +1970,7 @@ sub vmstatus {
        }
     }
 
-    return $res if !$full; 
+    return $res if !$full;
 
     my $qmpclient = PVE::QMPClient->new();
 
@@ -2073,7 +2033,9 @@ sub config_to_command {
     my ($storecfg, $vmid, $conf, $defaults, $migrate_uri) = @_;
 
     my $cmd = [];
+    my $devices = [];
     my $pciaddr = '';
+    my $bridges = {};
     my $kvmver = kvm_user_version();
     my $vernum = 0; # unknown
     if ($kvmver =~ m/^(\d+)\.(\d+)$/) {
@@ -2105,21 +2067,23 @@ sub config_to_command {
 
     push @$cmd, '-incoming', $migrate_uri if $migrate_uri;
 
+    push @$cmd, '-S' if $migrate_uri;
+
     my $use_usb2 = 0;
     for (my $i = 0; $i < $MAX_USB_DEVICES; $i++)  {
        next if !$conf->{"usb$i"};
        $use_usb2 = 1;
     }
     # include usb device config
-    push @$cmd, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
+    push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
 
     # enable absolute mouse coordinates (needed by vnc)
     my $tablet = defined($conf->{tablet}) ? $conf->{tablet} : $defaults->{tablet};
     if ($tablet) {
        if ($use_usb2) {
-           push @$cmd, '-device', 'usb-tablet,bus=ehci.0,port=6';
+           push @$devices, '-device', 'usb-tablet,bus=ehci.0,port=6';
        } else {
-           push @$cmd, '-usbdevice', 'tablet';
+           push @$devices, '-usbdevice', 'tablet';
        }
     }
 
@@ -2127,8 +2091,8 @@ sub config_to_command {
     for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++)  {
           my $d = parse_hostpci($conf->{"hostpci$i"});
           next if !$d;
-         $pciaddr = print_pci_addr("hostpci$i");
-          push @$cmd, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i$pciaddr";
+         $pciaddr = print_pci_addr("hostpci$i", $bridges);
+          push @$devices, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i$pciaddr";
     }
 
     # usb devices
@@ -2136,9 +2100,9 @@ sub config_to_command {
        my $d = parse_usb_device($conf->{"usb$i"});
        next if !$d;
        if ($d->{vendorid} && $d->{productid}) {
-           push @$cmd, '-device', "usb-host,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
+           push @$devices, '-device', "usb-host,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
        } elsif (defined($d->{hostbus}) && defined($d->{hostport})) {
-           push @$cmd, '-device', "usb-host,hostbus=$d->{hostbus},hostport=$d->{hostport}";
+           push @$devices, '-device', "usb-host,hostbus=$d->{hostbus},hostport=$d->{hostport}";
        }
     }
 
@@ -2146,8 +2110,8 @@ sub config_to_command {
     for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++)  {
        if (my $path = $conf->{"serial$i"}) {
            die "no such serial device\n" if ! -c $path;
-           push @$cmd, '-chardev', "tty,id=serial$i,path=$path";
-           push @$cmd, '-device', "isa-serial,chardev=serial$i";
+           push @$devices, '-chardev', "tty,id=serial$i,path=$path";
+           push @$devices, '-device', "isa-serial,chardev=serial$i";
        }
     }
 
@@ -2155,8 +2119,8 @@ sub config_to_command {
     for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++)  {
        if (my $path = $conf->{"parallel$i"}) {
            die "no such parallel device\n" if ! -c $path;
-           push @$cmd, '-chardev', "parport,id=parallel$i,path=$path";
-           push @$cmd, '-device', "isa-parallel,chardev=parallel$i";
+           push @$devices, '-chardev', "parport,id=parallel$i,path=$path";
+           push @$devices, '-device', "isa-parallel,chardev=parallel$i";
        }
     }
 
@@ -2252,20 +2216,30 @@ sub config_to_command {
     #my $soundhw = $conf->{soundhw} || $defaults->{soundhw};
     #push @$cmd, '-soundhw', 'es1370';
     #push @$cmd, '-soundhw', $soundhw if $soundhw;
-    $pciaddr = print_pci_addr("balloon0");
-    push @$cmd, '-device', "virtio-balloon-pci,id=balloon0$pciaddr" if $conf->{balloon};
+
+    if($conf->{agent}) {
+       my $qgasocket = qga_socket($vmid);
+       my $pciaddr = print_pci_addr("qga0", $bridges);
+       push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0";
+       push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
+       push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
+    }
+
+    $pciaddr = print_pci_addr("balloon0", $bridges);
+    push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr" if $conf->{balloon};
 
     if ($conf->{watchdog}) {
        my $wdopts = parse_watchdog($conf->{watchdog});
-       $pciaddr = print_pci_addr("watchdog");
+       $pciaddr = print_pci_addr("watchdog", $bridges);
        my $watchdog = $wdopts->{model} || 'i6300esb';
-       push @$cmd, '-device', "$watchdog$pciaddr";
-       push @$cmd, '-watchdog-action', $wdopts->{action} if $wdopts->{action};
+       push @$devices, '-device', "$watchdog$pciaddr";
+       push @$devices, '-watchdog-action', $wdopts->{action} if $wdopts->{action};
     }
 
     my $vollist = [];
     my $scsicontroller = {};
     my $ahcicontroller = {};
+    my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : $defaults->{scsihw};
 
     foreach_drive($conf, sub {
        my ($ds, $drive) = @_;
@@ -2289,22 +2263,23 @@ sub config_to_command {
        }
 
         if ($drive->{interface} eq 'scsi') {
-           my $maxdev = 7;
-           my $controller = int($drive->{index} / $maxdev);
-           $pciaddr = print_pci_addr("lsi$controller");
-           push @$cmd, '-device', "lsi,id=lsi$controller$pciaddr" if !$scsicontroller->{$controller};
-           $scsicontroller->{$controller}=1;
+
+           my $maxdev = ($scsihw ne 'lsi') ? 256 : 7;
+           my $controller = int($drive->{index} / $maxdev);
+           $pciaddr = print_pci_addr("scsihw$controller", $bridges);
+           push @$devices, '-device', "$scsihw,id=scsihw$controller$pciaddr" if !$scsicontroller->{$controller};
+           $scsicontroller->{$controller}=1;
         }
 
         if ($drive->{interface} eq 'sata') {
            my $controller = int($drive->{index} / $MAX_SATA_DISKS);
-           $pciaddr = print_pci_addr("ahci$controller");
-           push @$cmd, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
+           $pciaddr = print_pci_addr("ahci$controller", $bridges);
+           push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
            $ahcicontroller->{$controller}=1;
         }
 
-       push @$cmd, '-drive',print_drive_full($storecfg, $vmid, $drive);
-       push @$cmd, '-device',print_drivedevice_full($storecfg,$vmid, $drive);
+       push @$devices, '-drive',print_drive_full($storecfg, $vmid, $drive);
+       push @$devices, '-device',print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges);
     });
 
     push @$cmd, '-m', $conf->{memory} || $defaults->{memory};
@@ -2322,10 +2297,16 @@ sub config_to_command {
          }
 
          my $netdevfull = print_netdev_full($vmid,$conf,$d,"net$i");
-         push @$cmd, '-netdev', $netdevfull;
+         push @$devices, '-netdev', $netdevfull;
 
-         my $netdevicefull = print_netdevice_full($vmid,$conf,$d,"net$i");
-         push @$cmd, '-device', $netdevicefull;
+         my $netdevicefull = print_netdevice_full($vmid,$conf,$d,"net$i",$bridges);
+         push @$devices, '-device', $netdevicefull;
+    }
+
+    #bridges
+    while (my ($k, $v) = each %$bridges) {
+       $pciaddr = print_pci_addr("pci.$k");
+       unshift @$devices, '-device', "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr" if $k > 0;
     }
 
 
@@ -2348,6 +2329,7 @@ sub config_to_command {
        push @$cmd, @$aa;
     }
 
+    push @$cmd, @$devices;
     return wantarray ? ($cmd, $vollist) : $cmd;
 }
 
@@ -2361,6 +2343,11 @@ sub qmp_socket {
     return "${var_run_tmpdir}/$vmid.qmp";
 }
 
+sub qga_socket {
+    my ($vmid) = @_;
+    return "${var_run_tmpdir}/$vmid.qga";
+}
+
 sub pidfile_name {
     my ($vmid) = @_;
     return "${var_run_tmpdir}/$vmid.pid";
@@ -2409,9 +2396,11 @@ sub vm_deviceplug {
     my $devices_list = vm_devices_list($vmid);
     return 1 if defined($devices_list->{$deviceid});
 
+    qemu_bridgeadd($storecfg, $conf, $vmid, $deviceid); #add bridge if we need it for the device
+
     if ($deviceid =~ m/^(virtio)(\d+)$/) {
         return undef if !qemu_driveadd($storecfg, $vmid, $device);
-        my $devicefull = print_drivedevice_full($storecfg, $vmid, $device);
+        my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device);
         qemu_deviceadd($vmid, $devicefull);
         if(!qemu_deviceaddverify($vmid, $deviceid)) {
            qemu_drivedel($vmid, $deviceid);
@@ -2419,17 +2408,19 @@ sub vm_deviceplug {
         }
     }
 
-    if ($deviceid =~ m/^(lsi)(\d+)$/) {
+    if ($deviceid =~ m/^(scsihw)(\d+)$/) {
+        my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : "lsi";
         my $pciaddr = print_pci_addr($deviceid);
-        my $devicefull = "lsi,id=$deviceid$pciaddr";
+        my $devicefull = "$scsihw,id=$deviceid$pciaddr";
         qemu_deviceadd($vmid, $devicefull);
         return undef if(!qemu_deviceaddverify($vmid, $deviceid));
     }
 
     if ($deviceid =~ m/^(scsi)(\d+)$/) {
-        return undef if !qemu_findorcreatelsi($storecfg,$conf, $vmid, $device);
+        return 1 if ($conf->{scsihw} && $conf->{scsihw} ne 'lsi'); #virtio-scsi not yet support hotplug
+        return undef if !qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device);
         return undef if !qemu_driveadd($storecfg, $vmid, $device);
-        my $devicefull = print_drivedevice_full($storecfg, $vmid, $device);
+        my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device);
         if(!qemu_deviceadd($vmid, $devicefull)) {
            qemu_drivedel($vmid, $deviceid);
            return undef;
@@ -2446,6 +2437,14 @@ sub vm_deviceplug {
         }
     }
 
+    if ($deviceid =~ m/^(pci\.)(\d+)$/) {
+       my $bridgeid = $2;
+       my $pciaddr = print_pci_addr($deviceid);
+       my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
+       qemu_deviceadd($vmid, $devicefull);
+       return undef if !qemu_deviceaddverify($vmid, $deviceid);
+    }
+
     return 1;
 }
 
@@ -2559,16 +2558,36 @@ sub qemu_devicedelverify {
     return undef;
 }
 
-sub qemu_findorcreatelsi {
+sub qemu_findorcreatescsihw {
     my ($storecfg, $conf, $vmid, $device) = @_;
 
-    my $maxdev = 7;
+    my $maxdev = ($conf->{scsihw} && $conf->{scsihw} ne 'lsi') ? 256 : 7;
     my $controller = int($device->{index} / $maxdev);
-    my $lsiid="lsi$controller";
+    my $scsihwid="scsihw$controller";
     my $devices_list = vm_devices_list($vmid);
 
-    if(!defined($devices_list->{$lsiid})) {
-       return undef if !vm_deviceplug($storecfg, $conf, $vmid, $lsiid);
+    if(!defined($devices_list->{$scsihwid})) {
+       return undef if !vm_deviceplug($storecfg, $conf, $vmid, $scsihwid);
+    }
+    return 1;
+}
+
+sub qemu_bridgeadd {
+    my ($storecfg, $conf, $vmid, $device) = @_;
+
+    my $bridges = {};
+    my $bridgeid = undef;
+    print_pci_addr($device, $bridges);
+
+    while (my ($k, $v) = each %$bridges) {
+       $bridgeid = $k;
+    }
+    return if $bridgeid < 1;
+    my $bridge = "pci.$bridgeid";
+    my $devices_list = vm_devices_list($vmid);
+
+    if(!defined($devices_list->{$bridge})) {
+       return undef if !vm_deviceplug($storecfg, $conf, $vmid, $bridge);
     }
     return 1;
 }
@@ -2613,15 +2632,196 @@ sub qemu_block_set_io_throttle {
 
 }
 
+# old code, only used to shutdown old VM after update
+sub __read_avail {
+    my ($fh, $timeout) = @_;
+
+    my $sel = new IO::Select;
+    $sel->add($fh);
+
+    my $res = '';
+    my $buf;
+
+    my @ready;
+    while (scalar (@ready = $sel->can_read($timeout))) {
+       my $count;
+       if ($count = $fh->sysread($buf, 8192)) {
+           if ($buf =~ /^(.*)\(qemu\) $/s) {
+               $res .= $1;
+               last;
+           } else {
+               $res .= $buf;
+           }
+       } else {
+           if (!defined($count)) {
+               die "$!\n";
+           }
+           last;
+       }
+    }
+
+    die "monitor read timeout\n" if !scalar(@ready);
+
+    return $res;
+}
+
+# old code, only used to shutdown old VM after update
+sub vm_monitor_command {
+    my ($vmid, $cmdstr, $nocheck) = @_;
+
+    my $res;
+
+    eval {
+       die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
+
+       my $sname = "${var_run_tmpdir}/$vmid.mon";
+
+       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
+       # is set)
+       if ($cmdstr =~ m/^(info\s+migrate|migrate\s)/) {
+           $timeout = 60*60; # 1 hour
+       }
+
+       # read banner;
+       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);
+
+       if (!scalar(my @ready = $sel->can_write($timeout))) {
+           die "monitor write error - timeout";
+       }
+
+       my $fullcmd = "$cmdstr\r";
+
+       # syslog('info', "VM $vmid monitor command: $cmdstr");
+
+       my $b;
+       if (!($b = $sock->syswrite($fullcmd)) || ($b != length($fullcmd))) {
+           die "monitor write error - $!";
+       }
+
+       return if ($cmdstr eq 'q') || ($cmdstr eq 'quit');
+
+       $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);
+
+           shift @lines if $lines[0] !~ m/^unknown command/; # skip echo
+
+           $res = join("\n", @lines);
+           $res .= "\n";
+       }
+    };
+
+    my $err = $@;
+
+    if ($err) {
+       syslog("err", "VM $vmid monitor command failed - $err");
+       die $err;
+    }
+
+    return $res;
+}
+
+sub qemu_block_resize {
+    my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
+
+    my $running = PVE::QemuServer::check_running($vmid);
+
+    return if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
+
+    return if !$running;
+
+    vm_mon_cmd($vmid, "block_resize", device => $deviceid, size => int($size));
+
+}
+
+sub qemu_volume_snapshot {
+    my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
+
+    my $running = PVE::QemuServer::check_running($vmid);
+
+    return if !PVE::Storage::volume_snapshot($storecfg, $volid, $snap, $running);
+
+    return if !$running;
+
+    vm_mon_cmd($vmid, "snapshot-drive", device => $deviceid, name => $snap);
+
+}
+
+sub qemu_volume_snapshot_delete {
+    my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
+
+     #need to implement statefile location
+    my $statefile="/tmp/$vmid-$snap";
+
+    unlink $statefile if -e $statefile;
+
+    my $running = PVE::QemuServer::check_running($vmid);
+
+    return if !PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
+
+    return if !$running;
+
+    #need to split delvm monitor command like savevm
+
+}
+
+sub qemu_snapshot_start {
+    my ($vmid, $snap) = @_;
+
+    #need to implement statefile location
+    my $statefile="/tmp/$vmid-$snap";
+
+    vm_mon_cmd($vmid, "snapshot-start", statefile => $statefile);
+
+}
+
+sub qemu_snapshot_end {
+    my ($vmid) = @_;
+
+    vm_mon_cmd($vmid, "snapshot-end");
+
+}
+
+sub qga_freezefs {
+    my ($vmid) = @_;
+
+    #need to impplement call to qemu-ga
+}
+
+sub qga_unfreezefs {
+    my ($vmid) = @_;
+
+    #need to impplement call to qemu-ga
+}
+
 sub vm_start {
-    my ($storecfg, $vmid, $statefile, $skiplock) = @_;
+    my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom) = @_;
 
     lock_config($vmid, sub {
-       my $conf = load_config($vmid);
+       my $conf = load_config($vmid, $migratedfrom);
 
        check_lock($conf) if !$skiplock;
 
-       die "VM $vmid already running\n" if check_running($vmid);
+       die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
 
        my $migrate_uri;
        my $migrate_port = 0;
@@ -2641,6 +2841,9 @@ sub vm_start {
 
        my $defaults = load_defaults();
 
+       # set environment variable useful inside network script
+       $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
+
        my ($cmd, $vollist) = config_to_command($storecfg, $vmid, $conf, $defaults, $migrate_uri);
        # host pci devices
         for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++)  {
@@ -2685,6 +2888,13 @@ sub vm_start {
            eval { vm_mon_cmd($vmid, "migrate_set_downtime", value => $migrate_downtime); };
        }
 
+       if($migratedfrom) {
+           my $capabilities = {};
+           $capabilities->{capability} =  "xbzrle";
+           $capabilities->{state} = JSON::true;
+           eval { PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); };
+       }
+
        vm_balloonset($vmid, $conf->{balloon}) if $conf->{balloon};
 
     });
@@ -2709,13 +2919,26 @@ sub vm_qmp_command {
 
     my $res;
 
+    my $timeout;
+    if ($cmd->{arguments} && $cmd->{arguments}->{timeout}) {
+       $timeout = $cmd->{arguments}->{timeout};
+       delete $cmd->{arguments}->{timeout};
+    }
     eval {
        die "VM $vmid not running\n" if !check_running($vmid, $nocheck);
-
-       my $qmpclient = PVE::QMPClient->new();
-
-       $res = $qmpclient->cmd($vmid, $cmd);
-
+       my $sname = PVE::QemuServer::qmp_socket($vmid);
+       if (-e $sname) {
+           my $qmpclient = PVE::QMPClient->new();
+
+           $res = $qmpclient->cmd($vmid, $cmd, $timeout);
+       } elsif (-e "${var_run_tmpdir}/$vmid.mon") {
+           die "can't execute complex command on old monitor - stop/start your vm to fix the problem\n"
+               if scalar(%{$cmd->{arguments}});
+           vm_monitor_command($vmid, $cmd->{execute}, $nocheck);
+       } else {
+           die "unable to open monitor socket\n";
+       }
     };
     if (my $err = $@) {
        syslog("err", "VM $vmid qmp command failed - $err");
@@ -2730,7 +2953,7 @@ sub vm_human_monitor_command {
 
     my $res;
 
-    my $cmd = { 
+    my $cmd = {
        execute => 'human-monitor-command',
        arguments => { 'command-line' => $cmdline},
     };
@@ -2793,7 +3016,7 @@ sub vm_stop_cleanup {
            PVE::Storage::deactivate_volumes($storecfg, $vollist);
        }
 
-       foreach my $ext (qw(mon pid vnc)) {
+       foreach my $ext (qw(mon qmp pid vnc qga)) {
            unlink "/var/run/qemu-server/${vmid}.$ext";
        }
     };
@@ -2804,12 +3027,18 @@ sub vm_stop_cleanup {
 # We need that when migration VMs to other nodes (files already moved)
 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
 sub vm_stop {
-    my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
-
-    $timeout = 60 if !defined($timeout);
+    my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
 
     $force = 1 if !defined($force) && !$shutdown;
 
+    if ($migratedfrom){
+       my $pid = check_running($vmid, $nocheck, $migratedfrom);
+       kill 15, $pid if $pid;
+       my $conf = load_config($vmid, $migratedfrom);
+       vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive);
+       return;
+    }
+
     lock_config($vmid, sub {
 
        my $pid = check_running($vmid, $nocheck);
@@ -2819,8 +3048,14 @@ sub vm_stop {
        if (!$nocheck) {
            $conf = load_config($vmid);
            check_lock($conf) if !$skiplock;
+           if (!defined($timeout) && $shutdown && $conf->{startup}) {
+               my $opts = parse_startup($conf->{startup});
+               $timeout = $opts->{down} if $opts->{down};
+           }
        }
 
+       $timeout = 60 if !defined($timeout);
+
        eval {
            if ($shutdown) {
                $nocheck ? vm_mon_cmd_nocheck($vmid, "system_powerdown") : vm_mon_cmd($vmid, "system_powerdown");
@@ -2909,7 +3144,7 @@ sub vm_sendkey {
     lock_config($vmid, sub {
 
        my $conf = load_config($vmid);
-       
+
        # there is no qmp command, so we use the human monitor command
        vm_human_monitor_command($vmid, "sendkey $key");
     });
@@ -3015,7 +3250,7 @@ sub pci_dev_bind_to_stub {
 }
 
 sub print_pci_addr {
-    my ($id) = @_;
+    my ($id, $bridges) = @_;
 
     my $res = '';
     my $devices = {
@@ -3023,9 +3258,10 @@ sub print_pci_addr {
        #addr2 : first videocard
        balloon0 => { bus => 0, addr => 3 },
        watchdog => { bus => 0, addr => 4 },
-       lsi0 => { bus => 0, addr => 5 },
-       lsi1 => { bus => 0, addr => 6 },
+       scsihw0 => { bus => 0, addr => 5 },
+       scsihw1 => { bus => 0, addr => 6 },
        ahci0 => { bus => 0, addr => 7 },
+       qga0 => { bus => 0, addr => 8 },
        virtio0 => { bus => 0, addr => 10 },
        virtio1 => { bus => 0, addr => 11 },
        virtio2 => { bus => 0, addr => 12 },
@@ -3041,11 +3277,51 @@ sub print_pci_addr {
        net4 => { bus => 0, addr => 22 },
        net5 => { bus => 0, addr => 23 },
        #addr29 : usb-host (pve-usb.cfg)
+       'pci.1' => { bus => 0, addr => 30 },
+       'pci.2' => { bus => 0, addr => 31 },
+       'net6' => { bus => 1, addr => 1 },
+       'net7' => { bus => 1, addr => 2 },
+       'net8' => { bus => 1, addr => 3 },
+       'net9' => { bus => 1, addr => 4 },
+       'net10' => { bus => 1, addr => 5 },
+       'net11' => { bus => 1, addr => 6 },
+       'net12' => { bus => 1, addr => 7 },
+       'net13' => { bus => 1, addr => 8 },
+       'net14' => { bus => 1, addr => 9 },
+       'net15' => { bus => 1, addr => 10 },
+       'net16' => { bus => 1, addr => 11 },
+       'net17' => { bus => 1, addr => 12 },
+       'net18' => { bus => 1, addr => 13 },
+       'net19' => { bus => 1, addr => 14 },
+       'net20' => { bus => 1, addr => 15 },
+       'net21' => { bus => 1, addr => 16 },
+       'net22' => { bus => 1, addr => 17 },
+       'net23' => { bus => 1, addr => 18 },
+       'net24' => { bus => 1, addr => 19 },
+       'net25' => { bus => 1, addr => 20 },
+       'net26' => { bus => 1, addr => 21 },
+       'net27' => { bus => 1, addr => 22 },
+       'net28' => { bus => 1, addr => 23 },
+       'net29' => { bus => 1, addr => 24 },
+       'net30' => { bus => 1, addr => 25 },
+       'net31' => { bus => 1, addr => 26 },
+       'virtio6' => { bus => 2, addr => 1 },
+       'virtio7' => { bus => 2, addr => 2 },
+       'virtio8' => { bus => 2, addr => 3 },
+       'virtio9' => { bus => 2, addr => 4 },
+       'virtio10' => { bus => 2, addr => 5 },
+       'virtio11' => { bus => 2, addr => 6 },
+       'virtio12' => { bus => 2, addr => 7 },
+       'virtio13' => { bus => 2, addr => 8 },
+       'virtio14' => { bus => 2, addr => 9 },
+       'virtio15' => { bus => 2, addr => 10 },
     };
 
     if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) {
           my $addr = sprintf("0x%x", $devices->{$id}->{addr});
-          $res = ",bus=pci.$devices->{$id}->{bus},addr=$addr";
+          my $bus = $devices->{$id}->{bus};
+          $res = ",bus=pci.$bus,addr=$addr";
+          $bridges->{$bus} = 1 if $bridges;
     }
     return $res;