X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FQemuServer.pm;h=49356f2558a95610bb9a4c3e311458c940ee578f;hb=cd11416f7a5dbc3204aafdd89d372cd867a7d08;hp=ccd65655e7775a31095398043478ecb26d15c9d1;hpb=95a4b4a98b96e3d5275cbbab51716c9d37318306;p=qemu-server.git diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index ccd65655..49356f25 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1,6 +1,7 @@ package PVE::QemuServer; use strict; +use warnings; use POSIX; use IO::Handle; use IO::Select; @@ -21,7 +22,7 @@ use PVE::SafeSyslog; use Storable qw(dclone); use PVE::Exception qw(raise raise_param_exc); use PVE::Storage; -use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline); +use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach); use PVE::JSONSchema qw(get_standard_option); use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file); use PVE::INotify; @@ -233,7 +234,7 @@ my $confdesc = { optional => 1, type => 'string', description => "scsi controller model", - enum => [qw(lsi virtio-scsi-pci megasas)], + enum => [qw(lsi lsi53c810 virtio-scsi-pci megasas pvscsi)], default => 'lsi', }, description => { @@ -244,7 +245,7 @@ my $confdesc = { ostype => { optional => 1, type => 'string', - enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 l24 l26)], + enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 l24 l26 solaris)], description => < Microsoft Windows 7 win8 => Microsoft Windows 8/2012 l24 => Linux 2.4 Kernel l26 => Linux 2.6/3.X Kernel +solaris => solaris/opensolaris/openindiania kernel -other|l24|l26 ... no special behaviour +other|l24|l26|solaris ... no special behaviour wxp|w2k|w2k3|w2k8|wvista|win7|win8 ... use --localtime switch EODESC }, @@ -298,6 +300,13 @@ EODESC minimum => 1, default => 1, }, + maxcpus => { + optional => 1, + type => 'integer', + description => "Maximum cpus for hotplug.", + minimum => 1, + default => 1, + }, acpi => { optional => 1, type => 'boolean', @@ -335,8 +344,8 @@ EODESC vga => { optional => 1, type => 'string', - description => "Select VGA type. If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and 'cirrur' for other OS types. Option 'qxl' enables the SPICE display sever.", - enum => [qw(std cirrus vmware qxl)], + description => "Select VGA type. If you want to use high resolution modes (>= 1280x1024x16) then you should use option 'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and 'cirrur' for other OS types. Option 'qxl' enables the SPICE display sever. You can also run without any graphic card using a serial devive as terminal.", + enum => [qw(std cirrus vmware qxl serial0 serial1 serial2 serial3 qxl2 qxl3 qxl4)], }, watchdog => { optional => 1, @@ -403,7 +412,7 @@ EODESCR optional => 1, description => "Emulated CPU type.", type => 'string', - enum => [ qw(486 athlon pentium pentium2 pentium3 coreduo core2duo kvm32 kvm64 qemu32 qemu64 phenom Conroe Penryn Nehalem Westmere SandyBridge Haswell Opteron_G1 Opteron_G2 Opteron_G3 Opteron_G4 Opteron_G5 host) ], + enum => [ qw(486 athlon pentium pentium2 pentium3 coreduo core2duo kvm32 kvm64 qemu32 qemu64 phenom Conroe Penryn Nehalem Westmere SandyBridge Haswell Broadwell Opteron_G1 Opteron_G2 Opteron_G3 Opteron_G4 Opteron_G5 host) ], default => 'kvm64', }, parent => get_standard_option('pve-snapshot-name', { @@ -428,6 +437,13 @@ EODESCR maxLength => 40, optional => 1, }, + smbios1 => { + description => "Specify SMBIOS type 1 fields.", + type => 'string', format => 'pve-qm-smbios1', + typetext => "[manufacturer=str][,product=str][,version=str][,serial=str] [,uuid=uuid][,sku=str][,family=str]", + maxLength => 256, + optional => 1, + }, }; # what about other qemu settings ? @@ -458,18 +474,18 @@ my $MAX_SATA_DISKS = 6; my $MAX_USB_DEVICES = 5; my $MAX_NETS = 32; my $MAX_UNUSED_DISKS = 8; -my $MAX_HOSTPCI_DEVICES = 2; +my $MAX_HOSTPCI_DEVICES = 4; my $MAX_SERIAL_PORTS = 4; my $MAX_PARALLEL_PORTS = 3; my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000', 'pcnet', 'virtio', - 'ne2k_isa', 'i82551', 'i82557b', 'i82559er']; + 'ne2k_isa', 'i82551', 'i82557b', 'i82559er', 'vmxnet3']; my $nic_model_list_txt = join(' ', sort @$nic_model_list); my $netdesc = { optional => 1, type => 'string', format => 'pve-qm-net', - typetext => "MODEL=XX:XX:XX:XX:XX:XX [,bridge=][,rate=][,tag=]", + typetext => "MODEL=XX:XX:XX:XX:XX:XX [,bridge=][,queues=][,rate=][,tag=][,firewall=0|1]", description => < 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]', + 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] [,discard=ignore|on]', 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); @@ -511,7 +527,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|unsafe|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,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|directsync] [,format=f] [,backup=yes|no] [,rerror=ignore|report|stop] [,werror=enospc|ignore|report|stop] [,aio=native|threads] [,discard=ignore|on]', 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); @@ -519,7 +535,7 @@ PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc); 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]', + 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] [,discard=ignore|on]', 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); @@ -527,7 +543,7 @@ PVE::JSONSchema::register_standard_option("pve-qm-sata", $satadesc); 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]', + 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] [,discard=ignore|on]', description => "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . ").", }; PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc); @@ -556,7 +572,7 @@ PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc); my $hostpcidesc = { optional => 1, type => 'string', format => 'pve-qm-hostpci', - typetext => "HOSTPCIDEVICE", + typetext => "[host=]HOSTPCIDEVICE [,driver=kvm|vfio] [,rombar=on|off] [,pcie=0|1] [,x-vga=on|off]", description => < 1, type => 'string', - pattern => '/dev/ttyS\d+', + pattern => '(/dev/ttyS\d+|socket)', description => < 1, type => 'string', - pattern => '/dev/parport\d+', + pattern => '/dev/parport\d+|/dev/usb/lp\d+', description => <{werror} && $res->{werror} !~ m/^(enospc|ignore|report|stop)$/; return undef if $res->{backup} && $res->{backup} !~ m/^(yes|no)$/; return undef if $res->{aio} && $res->{aio} !~ m/^(native|threads)$/; - + return undef if $res->{discard} && $res->{discard} !~ m/^(ignore|on)$/; 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_max} && $res->{mbps_max} !~ m/^\d+(\.\d+)?$/; return undef if $res->{mbps_rd} && $res->{mbps_rd} !~ m/^\d+(\.\d+)?$/; + return undef if $res->{mbps_rd_max} && $res->{mbps_rd_max} !~ m/^\d+(\.\d+)?$/; return undef if $res->{mbps_wr} && $res->{mbps_wr} !~ m/^\d+(\.\d+)?$/; + return undef if $res->{mbps_wr_max} && $res->{mbps_wr_max} !~ m/^\d+(\.\d+)?$/; return undef if $res->{iops_rd} && $res->{iops}; return undef if $res->{iops_wr} && $res->{iops}; + + return undef if $res->{iops} && $res->{iops} !~ m/^\d+$/; + return undef if $res->{iops_max} && $res->{iops_max} !~ m/^\d+$/; return undef if $res->{iops_rd} && $res->{iops_rd} !~ m/^\d+$/; + return undef if $res->{iops_rd_max} && $res->{iops_rd_max} !~ m/^\d+$/; return undef if $res->{iops_wr} && $res->{iops_wr} !~ m/^\d+$/; + return undef if $res->{iops_wr_max} && $res->{iops_wr_max} !~ m/^\d+$/; if ($res->{size}) { @@ -960,13 +986,13 @@ sub parse_drive { return $res; } -my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio iops iops_rd iops_wr); +my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio discard iops iops_rd iops_wr iops_max iops_rd_max iops_wr_max); sub print_drive { my ($vmid, $drive) = @_; my $opts = ''; - foreach my $o (@qemu_drive_options, 'mbps', 'mbps_rd', 'mbps_wr', 'backup') { + foreach my $o (@qemu_drive_options, 'mbps', 'mbps_rd', 'mbps_wr', 'mbps_max', 'mbps_rd_max', 'mbps_wr_max', 'backup') { $opts .= ",$o=$drive->{$o}" if $drive->{$o}; } @@ -1038,6 +1064,23 @@ sub path_is_scsi { return $res; } +sub machine_type_is_q35 { + my ($conf) = @_; + + return $conf->{machine} && ($conf->{machine} =~ m/q35/) ? 1 : 0; +} + +sub print_tabletdevice_full { + my ($conf) = @_; + + my $q35 = machine_type_is_q35($conf); + + # we use uhci for old VMs because tablet driver was buggy in older qemu + my $usbbus = $q35 ? "ehci" : "uhci"; + + return "usb-tablet,id=tablet,bus=$usbbus.0,port=1"; +} + sub print_drivedevice_full { my ($storecfg, $conf, $vmid, $drive, $bridges) = @_; @@ -1048,7 +1091,7 @@ sub print_drivedevice_full { 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 = ($conf->{scsihw} && $conf->{scsihw} ne 'lsi') ? 256 : 7; + $maxdev = ($conf->{scsihw} && ($conf->{scsihw} !~ m/^lsi/)) ? 256 : 7; my $controller = int($drive->{index} / $maxdev); my $unit = $drive->{index} % $maxdev; my $devicetype = 'hd'; @@ -1075,8 +1118,8 @@ sub print_drivedevice_full { } } - 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'; + if (!$conf->{scsihw} || ($conf->{scsihw} =~ m/^lsi/)){ + $device = "scsi-$devicetype,bus=scsihw$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}"; } 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}"; } @@ -1104,6 +1147,20 @@ sub print_drivedevice_full { return $device; } +sub get_initiator_name { + my $initiator; + + my $fh = IO::File->new('/etc/iscsi/initiatorname.iscsi') || return undef; + while (defined(my $line = <$fh>)) { + next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/; + $initiator = $1; + last; + } + $fh->close(); + + return $initiator; +} + sub print_drive_full { my ($storecfg, $vmid, $drive) = @_; @@ -1155,6 +1212,11 @@ sub print_netdevice_full { my $extra = ($bootorder !~ m/n/) ? "romfile=," : ''; my $pciaddr = print_pci_addr("$netid", $bridges); my $tmpstr = "$device,${extra}mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid"; + if ($net->{queues} && $net->{queues} > 1 && $net->{model} eq 'virtio'){ + #Consider we have N queues, the number of vectors needed is 2*N + 2 (plus one config interrupt and control vq) + my $vectors = $net->{queues} * 2 + 2; + $tmpstr .= ",vectors=$vectors,mq=on"; + } $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex} ; return $tmpstr; } @@ -1180,11 +1242,17 @@ sub print_netdev_full { my $vmname = $conf->{name} || "vm$vmid"; + my $netdev = ""; + if ($net->{bridge}) { - return "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/pve-bridge$vhostparam"; + $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/pve-bridge,downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam"; } else { - return "type=user,id=$netid,hostname=$vmname"; + $netdev = "type=user,id=$netid,hostname=$vmname"; } + + $netdev .= ",queues=$net->{queues}" if ($net->{queues} && $net->{model} eq 'virtio'); + + return $netdev; } sub drive_is_cdrom { @@ -1199,14 +1267,37 @@ sub parse_hostpci { return undef if !$value; + + my @list = split(/,/, $value); + my $found; + my $res = {}; + foreach my $kv (@list) { - if ($value =~ m/^[a-f0-9]{2}:[a-f0-9]{2}\.[a-f0-9]$/) { - $res->{pciid} = $value; - } else { - return undef; + if ($kv =~ m/^(host=)?([a-f0-9]{2}:[a-f0-9]{2})(\.([a-f0-9]))?$/) { + $found = 1; + if(defined($4)){ + push @{$res->{pciid}}, { id => $2 , function => $4}; + + }else{ + my $pcidevices = lspci($2); + $res->{pciid} = $pcidevices->{$2}; + } + } elsif ($kv =~ m/^driver=(kvm|vfio)$/) { + $res->{driver} = $1; + } elsif ($kv =~ m/^rombar=(on|off)$/) { + $res->{rombar} = $1; + } elsif ($kv =~ m/^x-vga=(on|off)$/) { + $res->{'x-vga'} = $1; + } elsif ($kv =~ m/^pcie=(\d+)$/) { + $res->{pcie} = 1 if $1 == 1; + } else { + warn "unknown hostpci setting '$kv'\n"; + } } + return undef if !$found; + return $res; } @@ -1218,17 +1309,21 @@ sub parse_net { 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) { + if ($kvp =~ m/^(ne2k_pci|e1000|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er|vmxnet3)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i) { my $model = lc($1); my $mac = defined($3) ? uc($3) : PVE::Tools::random_ether_addr(); $res->{model} = $model; $res->{macaddr} = $mac; } elsif ($kvp =~ m/^bridge=(\S+)$/) { $res->{bridge} = $1; + } elsif ($kvp =~ m/^queues=(\d+)$/) { + $res->{queues} = $1; } elsif ($kvp =~ m/^rate=(\d+(\.\d+)?)$/) { $res->{rate} = $1; } elsif ($kvp =~ m/^tag=(\d+)$/) { $res->{tag} = $1; + } elsif ($kvp =~ m/^firewall=(\d+)$/) { + $res->{firewall} = $1; } else { return undef; } @@ -1248,6 +1343,7 @@ sub print_net { $res .= ",bridge=$net->{bridge}" if $net->{bridge}; $res .= ",rate=$net->{rate}" if $net->{rate}; $res .= ",tag=$net->{tag}" if $net->{tag}; + $res .= ",firewall=$net->{firewall}" if $net->{firewall}; return $res; } @@ -1283,6 +1379,58 @@ sub add_unused_volume { return $key; } +my $valid_smbios1_options = { + manufacturer => '\S+', + product => '\S+', + version => '\S+', + serial => '\S+', + uuid => '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}', + sku => '\S+', + family => '\S+', +}; + +# smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str] +sub parse_smbios1 { + my ($data) = @_; + + my $res = {}; + + foreach my $kvp (split(/,/, $data)) { + return undef if $kvp !~ m/^(\S+)=(.+)$/; + my ($k, $v) = split(/=/, $kvp); + return undef if !defined($k) || !defined($v); + return undef if !$valid_smbios1_options->{$k}; + return undef if $v !~ m/^$valid_smbios1_options->{$k}$/; + $res->{$k} = $v; + } + + return $res; +} + +sub print_smbios1 { + my ($smbios1) = @_; + + my $data = ''; + foreach my $k (keys %$smbios1) { + next if !defined($smbios1->{$k}); + next if !$valid_smbios1_options->{$k}; + $data .= ',' if $data; + $data .= "$k=$smbios1->{$k}"; + } + return $data; +} + +PVE::JSONSchema::register_format('pve-qm-smbios1', \&verify_smbios1); +sub verify_smbios1 { + my ($value, $noerr) = @_; + + return $value if parse_smbios1($value); + + return undef if $noerr; + + die "unable to parse smbios (type 1) options\n"; +} + PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk); sub verify_bootdisk { my ($value, $noerr) = @_; @@ -1714,6 +1862,10 @@ sub write_vm_config { delete $conf->{smp}; } + if ($conf->{maxcpus} && $conf->{sockets}) { + delete $conf->{sockets}; + } + my $used_volids = {}; my $cleanup_config = sub { @@ -1833,6 +1985,7 @@ sub check_local_resources { $loc_res = 1 if $conf->{hostpci}; # old syntax foreach my $k (keys %$conf) { + next if $k =~ m/^usb/ && ($conf->{$k} eq 'spice'); $loc_res = 1 if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/; } @@ -2231,6 +2384,14 @@ sub foreach_volid { } } +sub vga_conf_has_spice { + my ($vga) = @_; + + return 0 if !$vga || $vga !~ m/^qxl([234])?$/; + + return $1 || 1; +} + sub config_to_command { my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_; @@ -2254,6 +2415,8 @@ sub config_to_command { my $have_ovz = -f '/proc/vz/vestat'; + my $q35 = machine_type_is_q35($conf); + push @$cmd, '/usr/bin/kvm'; push @$cmd, '-id', $vmid; @@ -2271,21 +2434,35 @@ sub config_to_command { push @$cmd, '-daemonize'; - $pciaddr = print_pci_addr("piix3", $bridges); - push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2"; + if ($conf->{smbios1}) { + push @$cmd, '-smbios', "type=1,$conf->{smbios1}"; + } - my $use_usb2 = 0; - for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) { - next if !$conf->{"usb$i"}; - $use_usb2 = 1; + if ($q35) { + # the q35 chipset support native usb2, so we enable usb controller + # by default for this machine type + push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg'; + } else { + $pciaddr = print_pci_addr("piix3", $bridges); + push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2"; + + 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 @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2; } - # include usb device config - push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2; my $vga = $conf->{vga}; + + my $qxlnum = vga_conf_has_spice($vga); + $vga = 'qxl' if $qxlnum; + if (!$vga) { - if ($conf->{ostype} && ($conf->{ostype} eq 'win8' || - $conf->{ostype} eq 'win7' || + if ($conf->{ostype} && ($conf->{ostype} eq 'win8' || + $conf->{ostype} eq 'win7' || $conf->{ostype} eq 'w2k8')) { $vga = 'std'; } else { @@ -2299,17 +2476,53 @@ sub config_to_command { $tablet = $conf->{tablet}; } else { $tablet = $defaults->{tablet}; - $tablet = 0 if $vga eq 'qxl'; # disable for spice because it is not needed + $tablet = 0 if $qxlnum; # disable for spice because it is not needed + $tablet = 0 if $vga =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card) } - push @$devices, '-device', 'usb-tablet,id=tablet,bus=uhci.0,port=1' if $tablet; - + push @$devices, '-device', print_tabletdevice_full($conf) if $tablet; + # host pci devices 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", $bridges); - push @$devices, '-device', "pci-assign,host=$d->{pciid},id=hostpci$i$pciaddr"; + my $d = parse_hostpci($conf->{"hostpci$i"}); + next if !$d; + + my $pcie = $d->{pcie}; + if($pcie){ + die "q35 machine model is not enabled" if !$q35; + $pciaddr = print_pcie_addr("hostpci$i"); + }else{ + $pciaddr = print_pci_addr("hostpci$i", $bridges); + } + + my $rombar = $d->{rombar} && $d->{rombar} eq 'off' ? ",rombar=0" : ""; + my $driver = $d->{driver} && $d->{driver} eq 'vfio' ? "vfio-pci" : "pci-assign"; + my $xvga = $d->{'x-vga'} && $d->{'x-vga'} eq 'on' ? ",x-vga=on" : ""; + if ($xvga && $xvga ne '') { + push @$cpuFlags, 'kvm=off'; + $vga = 'none'; + } + $driver = "vfio-pci" if $xvga ne ''; + my $pcidevices = $d->{pciid}; + my $multifunction = 1 if @$pcidevices > 1; + + my $j=0; + foreach my $pcidevice (@$pcidevices) { + + my $id = "hostpci$i"; + $id .= ".$j" if $multifunction; + my $addr = $pciaddr; + $addr .= ".$j" if $multifunction; + my $devicestr = "$driver,host=$pcidevice->{id}.$pcidevice->{function},id=$id$addr"; + + if($j == 0){ + $devicestr .= "$rombar$xvga"; + $devicestr .= ",multifunction=on" if $multifunction; + } + + push @$devices, '-device', $devicestr; + $j++; + } } # usb devices @@ -2330,9 +2543,15 @@ sub config_to_command { # serial devices for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) { if (my $path = $conf->{"serial$i"}) { - die "no such serial device\n" if ! -c $path; - push @$devices, '-chardev', "tty,id=serial$i,path=$path"; - push @$devices, '-device', "isa-serial,chardev=serial$i"; + if ($path eq 'socket') { + my $socket = "/var/run/qemu-server/${vmid}.serial$i"; + push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server,nowait"; + push @$devices, '-device', "isa-serial,chardev=serial$i"; + } else { + die "no such serial device\n" if ! -c $path; + push @$devices, '-chardev', "tty,id=serial$i,path=$path"; + push @$devices, '-device', "isa-serial,chardev=serial$i"; + } } } @@ -2340,7 +2559,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 @$devices, '-chardev', "parport,id=parallel$i,path=$path"; + my $devtype = $path =~ m!^/dev/usb/lp! ? 'tty' : 'parport'; + push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path"; push @$devices, '-device', "isa-parallel,chardev=parallel$i"; } } @@ -2354,7 +2574,13 @@ sub config_to_command { $sockets = $conf->{sockets} if $conf->{sockets}; my $cores = $conf->{cores} || 1; - push @$cmd, '-smp', "sockets=$sockets,cores=$cores"; + my $maxcpus = $conf->{maxcpus} if $conf->{maxcpus}; + + if ($maxcpus) { + push @$cmd, '-smp', "cpus=$cores,maxcpus=$maxcpus"; + } else { + push @$cmd, '-smp', "sockets=$sockets,cores=$cores"; + } push @$cmd, '-nodefaults'; @@ -2373,7 +2599,7 @@ sub config_to_command { push @$cmd, '-no-reboot' if defined($conf->{reboot}) && $conf->{reboot} == 0; - push @$cmd, '-vga', $vga if $vga; # for kvm 77 and later + push @$cmd, '-vga', $vga if $vga && $vga !~ m/^serial\d+$/; # for kvm 77 and later # time drift fix my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf}; @@ -2382,7 +2608,7 @@ sub config_to_command { my $useLocaltime = $conf->{localtime}; if (my $ost = $conf->{ostype}) { - # other, wxp, w2k, w2k3, w2k8, wvista, win7, win8, l24, l26 + # other, wxp, w2k, w2k3, w2k8, wvista, win7, win8, l24, l26, solaris if ($ost =~ m/^w/) { # windows $useLocaltime = 1 if !defined($conf->{localtime}); @@ -2428,12 +2654,18 @@ sub config_to_command { my $cpu = $nokvm ? "qemu64" : "kvm64"; $cpu = $conf->{cpu} if $conf->{cpu}; - push @$cpuFlags , '+x2apic' if !$nokvm; + push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64'; + + push @$cpuFlags , '+x2apic' if !$nokvm && $conf->{ostype} ne 'solaris'; + + push @$cpuFlags , '-x2apic' if $conf->{ostype} eq 'solaris'; push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32'; $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags); + # Note: enforce needs kernel 3.10, so we do not use it for now + # push @$cmd, '-cpu', "$cpu,enforce"; push @$cmd, '-cpu', $cpu; push @$cmd, '-S' if $conf->{freeze}; @@ -2455,16 +2687,31 @@ sub config_to_command { push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0'; } - if ($vga eq 'qxl') { + my $spice_port; + + if ($qxlnum) { + if ($qxlnum > 1) { + if ($conf->{ostype} && $conf->{ostype} =~ m/^w/){ + for(my $i = 1; $i < $qxlnum; $i++){ + my $pciaddr = print_pci_addr("vga$i", $bridges); + push @$cmd, '-device', "qxl,id=vga$i,ram_size=67108864,vram_size=33554432$pciaddr"; + } + } else { + # assume other OS works like Linux + push @$cmd, '-global', 'qxl-vga.ram_size=134217728'; + push @$cmd, '-global', 'qxl-vga.vram_size=67108864'; + } + } + my $pciaddr = print_pci_addr("spice", $bridges); - my $port = PVE::Tools::next_unused_port(61000, 61099); + $spice_port = PVE::Tools::next_spice_port(); - push @$cmd, '-spice', "tls-port=$port,addr=127.0.0.1,tls-ciphers=DES-CBC3-SHA,seamless-migration=on"; + push @$devices, '-spice', "tls-port=${spice_port},addr=127.0.0.1,tls-ciphers=DES-CBC3-SHA,seamless-migration=on"; - push @$cmd, '-device', "virtio-serial,id=spice$pciaddr"; - push @$cmd, '-chardev', "spicevmc,id=vdagent,name=vdagent"; - push @$cmd, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0"; + push @$devices, '-device', "virtio-serial,id=spice$pciaddr"; + push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent"; + push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0"; } # enable balloon by default, unless explicitly disabled @@ -2486,6 +2733,11 @@ sub config_to_command { my $ahcicontroller = {}; my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : $defaults->{scsihw}; + # Add iscsi initiator name if available + if (my $initiator = get_initiator_name()) { + push @$devices, '-iscsi', "initiator-name=$initiator"; + } + foreach_drive($conf, sub { my ($ds, $drive) = @_; @@ -2509,7 +2761,7 @@ sub config_to_command { if ($drive->{interface} eq 'scsi') { - my $maxdev = ($scsihw ne 'lsi') ? 256 : 7; + my $maxdev = ($scsihw !~ m/^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}; @@ -2523,8 +2775,9 @@ sub config_to_command { $ahcicontroller->{$controller}=1; } - push @$devices, '-drive',print_drive_full($storecfg, $vmid, $drive); - push @$devices, '-device',print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges); + my $drive_cmd = print_drive_full($storecfg, $vmid, $drive); + push @$devices, '-drive',$drive_cmd; + push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges); }); push @$cmd, '-m', $conf->{memory} || $defaults->{memory}; @@ -2548,13 +2801,14 @@ sub config_to_command { 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; + if (!$q35) { + # add pci 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; + } } - # hack: virtio with fairsched is unreliable, so we do not use fairsched # when the VM uses virtio devices. if (!$use_virtio && $have_ovz) { @@ -2582,7 +2836,7 @@ sub config_to_command { push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags); - return wantarray ? ($cmd, $vollist) : $cmd; + return wantarray ? ($cmd, $vollist, $spice_port) : $cmd; } sub vnc_socket { @@ -2593,7 +2847,7 @@ sub vnc_socket { sub spice_port { my ($vmid) = @_; - my $res = vm_mon_cmd_nocheck($vmid, 'query-spice'); + my $res = vm_mon_cmd($vmid, 'query-spice'); return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n"; } @@ -2634,9 +2888,10 @@ sub vm_deviceplug { return 1 if !check_running($vmid); + my $q35 = machine_type_is_q35($conf); + if ($deviceid eq 'tablet') { - my $devicefull = "usb-tablet,id=tablet,bus=uhci.0,port=1"; - qemu_deviceadd($vmid, $devicefull); + qemu_deviceadd($vmid, print_tabletdevice_full($conf)); return 1; } @@ -2666,7 +2921,7 @@ sub vm_deviceplug { } if ($deviceid =~ m/^(scsi)(\d+)$/) { - return 1 if ($conf->{scsihw} && $conf->{scsihw} ne 'lsi'); #virtio-scsi not yet support hotplug + return 1 if ($conf->{scsihw} && ($conf->{scsihw} !~ m/^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, $conf, $vmid, $device); @@ -2686,7 +2941,8 @@ sub vm_deviceplug { } } - if ($deviceid =~ m/^(pci\.)(\d+)$/) { + + if (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) { my $bridgeid = $2; my $pciaddr = print_pci_addr($deviceid); my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr"; @@ -2811,7 +3067,7 @@ sub qemu_devicedelverify { sub qemu_findorcreatescsihw { my ($storecfg, $conf, $vmid, $device) = @_; - my $maxdev = ($conf->{scsihw} && $conf->{scsihw} ne 'lsi') ? 256 : 7; + my $maxdev = ($conf->{scsihw} && ($conf->{scsihw} !~ m/^lsi/)) ? 256 : 7; my $controller = int($device->{index} / $maxdev); my $scsihwid="scsihw$controller"; my $devices_list = vm_devices_list($vmid); @@ -2859,6 +3115,29 @@ sub qemu_netdevdel { return 1; } +sub qemu_cpu_hotplug { + my ($vmid, $conf, $cores) = @_; + + die "new cores config is not defined" if !$cores; + die "you can't add more cores than maxcpus" + if $conf->{maxcpus} && ($cores > $conf->{maxcpus}); + return if !check_running($vmid); + + my $currentcores = $conf->{cores} if $conf->{cores}; + die "current cores is not defined" if !$currentcores; + die "maxcpus is not defined" if !$conf->{maxcpus}; + raise_param_exc({ 'cores' => "online cpu unplug is not yet possible" }) + if($cores < $currentcores); + + my $currentrunningcores = vm_mon_cmd($vmid, "query-cpus"); + raise_param_exc({ 'cores' => "cores number if running vm is different than configuration" }) + if scalar (@{$currentrunningcores}) != $currentcores; + + for(my $i = $currentcores; $i < $cores; $i++) { + vm_mon_cmd($vmid, "cpu-add", id => int($i)); + } +} + sub qemu_block_set_io_throttle { my ($vmid, $deviceid, $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr) = @_; @@ -3026,8 +3305,32 @@ sub qga_unfreezefs { #need to impplement call to qemu-ga } +sub set_migration_caps { + my ($vmid) = @_; + + my $cap_ref = []; + + my $enabled_cap = { + "auto-converge" => 1, + "xbzrle" => 0, + "x-rdma-pin-all" => 0, + "zero-blocks" => 0, + }; + + my $supported_capabilities = vm_mon_cmd_nocheck($vmid, "query-migrate-capabilities"); + + for my $supported_capability (@$supported_capabilities) { + push @$cap_ref, { + capability => $supported_capability->{capability}, + state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false, + }; + } + + vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => $cap_ref); +} + sub vm_start { - my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused, $forcemachine) = @_; + my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused, $forcemachine, $spice_ticket) = @_; lock_config($vmid, sub { my $conf = load_config($vmid, $migratedfrom); @@ -3043,14 +3346,20 @@ sub vm_start { # set environment variable useful inside network script $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom; - my ($cmd, $vollist) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine); + my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine); my $migrate_port = 0; - + my $migrate_uri; if ($statefile) { if ($statefile eq 'tcp') { + my $localip = "localhost"; + my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter.cfg'); + if ($datacenterconf->{migration_unsecure}) { + my $nodename = PVE::INotify::nodename(); + $localip = PVE::Cluster::remote_node_ip($nodename, 1); + } $migrate_port = PVE::Tools::next_migrate_port(); - my $migrate_uri = "tcp:localhost:${migrate_port}"; + $migrate_uri = "tcp:${localip}:${migrate_port}"; push @$cmd, '-incoming', $migrate_uri; push @$cmd, '-S'; } else { @@ -3064,11 +3373,22 @@ sub vm_start { for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) { my $d = parse_hostpci($conf->{"hostpci$i"}); next if !$d; - my $info = pci_device_info("0000:$d->{pciid}"); - die "IOMMU not present\n" if !check_iommu_support(); - die "no pci device info for device '$d->{pciid}'\n" if !$info; - die "can't unbind pci device '$d->{pciid}'\n" if !pci_dev_bind_to_stub($info); - die "can't reset pci device '$d->{pciid}'\n" if !pci_dev_reset($info); + my $pcidevices = $d->{pciid}; + foreach my $pcidevice (@$pcidevices) { + my $pciid = $pcidevice->{id}.".".$pcidevice->{function}; + + my $info = pci_device_info("0000:$pciid"); + die "IOMMU not present\n" if !check_iommu_support(); + die "no pci device info for device '$pciid'\n" if !$info; + + if ($d->{driver} && $d->{driver} eq "vfio") { + die "can't unbind/bind pci group to vfio '$pciid'\n" if !pci_dev_group_bind_to_vfio($pciid); + } else { + die "can't unbind/bind to stub pci device '$pciid'\n" if !pci_dev_bind_to_stub($info); + } + + die "can't reset pci device '$pciid'\n" if $info->{has_fl_reset} and !pci_dev_reset($info); + } } PVE::Storage::activate_volumes($storecfg, $vollist); @@ -3078,29 +3398,29 @@ sub vm_start { my $err = $@; die "start failed: $err" if $err; - print "migration listens on port $migrate_port\n" if $migrate_port; + print "migration listens on $migrate_uri\n" if $migrate_uri; if ($statefile && $statefile ne 'tcp') { eval { vm_mon_cmd_nocheck($vmid, "cont"); }; warn $@ if $@; } - if($migratedfrom) { - my $capabilities = {}; - $capabilities->{capability} = "xbzrle"; - $capabilities->{state} = JSON::true; - eval { vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => [$capabilities]); }; - if($conf->{vga} eq 'qxl'){ - my $spice_port = PVE::QemuServer::spice_port($vmid); - print "spice listens on port $spice_port\n" if $spice_port; - if($spiceticket){ - PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "set_password", protocol => 'spice', password => $spiceticket); - PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "expire_password", protocol => 'spice', time => "+5"); + if ($migratedfrom) { + + eval { + PVE::QemuServer::set_migration_caps($vmid); + }; + warn $@ if $@; + + if ($spice_port) { + print "spice listens on port $spice_port\n"; + if ($spice_ticket) { + PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "set_password", protocol => 'spice', password => $spice_ticket); + PVE::QemuServer::vm_mon_cmd_nocheck($vmid, "expire_password", protocol => 'spice', time => "+30"); } } - } - else{ + } else { if (!$statefile && (!defined($conf->{balloon}) || $conf->{balloon})) { vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024) @@ -3462,6 +3782,61 @@ sub pci_dev_bind_to_stub { return -d $testdir; } +sub pci_dev_bind_to_vfio { + my ($dev) = @_; + + my $name = $dev->{name}; + + my $vfio_basedir = "$pcisysfs/drivers/vfio-pci"; + + if (!-d $vfio_basedir) { + system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null"); + } + die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir; + + my $testdir = "$vfio_basedir/$name"; + return 1 if -d $testdir; + + my $data = "$dev->{vendor} $dev->{product}"; + return undef if !file_write("$vfio_basedir/new_id", $data); + + my $fn = "$pcisysfs/devices/$name/driver/unbind"; + if (!file_write($fn, $name)) { + return undef if -f $fn; + } + + $fn = "$vfio_basedir/bind"; + if (! -d $testdir) { + return undef if !file_write($fn, $name); + } + + return -d $testdir; +} + +sub pci_dev_group_bind_to_vfio { + my ($pciid) = @_; + + my $vfio_basedir = "$pcisysfs/drivers/vfio-pci"; + + if (!-d $vfio_basedir) { + system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null"); + } + die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir; + + # get IOMMU group devices + opendir(my $D, "$pcisysfs/devices/0000:$pciid/iommu_group/devices/") || die "Cannot open iommu_group: $!\n"; + my @devs = grep /^0000:/, readdir($D); + closedir($D); + + foreach my $pciid (@devs) { + $pciid =~ m/^([:\.\da-f]+)$/ or die "PCI ID $pciid not valid!\n"; + my $info = pci_device_info($1); + pci_dev_bind_to_vfio($info) || die "Cannot bind $pciid to vfio\n"; + } + + return 1; +} + sub print_pci_addr { my ($id, $bridges) = @_; @@ -3490,6 +3865,11 @@ sub print_pci_addr { net3 => { bus => 0, addr => 21 }, net4 => { bus => 0, addr => 22 }, net5 => { bus => 0, addr => 23 }, + vga1 => { bus => 0, addr => 24 }, + vga2 => { bus => 0, addr => 25 }, + vga3 => { bus => 0, addr => 26 }, + hostpci2 => { bus => 0, addr => 27 }, + hostpci3 => { bus => 0, addr => 28 }, #addr29 : usb-host (pve-usb.cfg) 'pci.1' => { bus => 0, addr => 30 }, 'pci.2' => { bus => 0, addr => 31 }, @@ -3541,6 +3921,26 @@ sub print_pci_addr { } +sub print_pcie_addr { + my ($id) = @_; + + my $res = ''; + my $devices = { + hostpci0 => { bus => "ich9-pcie-port-1", addr => 0 }, + hostpci1 => { bus => "ich9-pcie-port-2", addr => 0 }, + hostpci2 => { bus => "ich9-pcie-port-3", addr => 0 }, + hostpci3 => { bus => "ich9-pcie-port-4", addr => 0 }, + }; + + if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) { + my $addr = sprintf("0x%x", $devices->{$id}->{addr}); + my $bus = $devices->{$id}->{bus}; + $res = ",bus=$bus,addr=$addr"; + } + return $res; + +} + # vzdump restore implementaion sub tar_archive_read_firstfile { @@ -3971,7 +4371,7 @@ sub restore_vma_archive { my $write_zeros = 1; # fixme: what other storages types initialize volumes with zero? - if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' || + if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' || $scfg->{type} eq 'glusterfs' || $scfg->{type} eq 'sheepdog' || $scfg->{type} eq 'rbd') { $write_zeros = 0; } @@ -4014,6 +4414,10 @@ sub restore_vma_archive { my ($dev_id, $size, $devname) = ($1, $2, $3); $devinfo->{$devname} = { size => $size, dev_id => $dev_id }; } elsif ($line =~ m/^CTIME: /) { + # we correctly received the vma config, so we can disable + # the timeout now for disk allocation (set to 10 minutes, so + # that we always timeout if something goes wrong) + alarm(600); &$print_devmap(); print $fifofh "done\n"; my $tmp = $oldtimeout || 0; @@ -4550,10 +4954,12 @@ sub snapshot_delete { die "snapshot '$snapname' does not exist\n" if !defined($snap); # remove parent refs - &$unlink_parent($conf, $snap->{parent}); - foreach my $sn (keys %{$conf->{snapshots}}) { - next if $sn eq $snapname; - &$unlink_parent($conf->{snapshots}->{$sn}, $snap->{parent}); + if (!$prepare) { + &$unlink_parent($conf, $snap->{parent}); + foreach my $sn (keys %{$conf->{snapshots}}) { + next if $sn eq $snapname; + &$unlink_parent($conf->{snapshots}->{$sn}, $snap->{parent}); + } } if ($remove_drive) { @@ -4683,7 +5089,7 @@ sub qemu_img_convert { my $dst_path = PVE::Storage::path($storecfg, $dst_volid); my $cmd = []; - push @$cmd, '/usr/bin/qemu-img', 'convert', '-t', 'writeback', '-p', '-C'; + push @$cmd, '/usr/bin/qemu-img', 'convert', '-t', 'writeback', '-p', '-n'; push @$cmd, '-s', $snapname if($snapname && $src_format eq "qcow2"); push @$cmd, '-f', $src_format, '-O', $dst_format, $src_path, $dst_path; @@ -4805,7 +5211,7 @@ sub clone_disk { if (!$full) { print "create linked clone of drive $drivename ($drive->{file})\n"; - $newvolid = PVE::Storage::vdisk_clone($storecfg, $drive->{file}, $newvmid); + $newvolid = PVE::Storage::vdisk_clone($storecfg, $drive->{file}, $newvmid, $snapname); push @$newvollist, $newvolid; } else { my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file}); @@ -4860,21 +5266,17 @@ sub get_current_qemu_machine { return $current || $default || 'pc'; } -sub read_x509_subject_spice { - my ($filename) = @_; +sub lspci { + + my $devices = {}; - # read x509 subject - my $bio = Net::SSLeay::BIO_new_file($filename, 'r'); - my $x509 = Net::SSLeay::PEM_read_bio_X509($bio); - Net::SSLeay::BIO_free($bio); - my $nameobj = Net::SSLeay::X509_get_subject_name($x509); - my $subject = Net::SSLeay::X509_NAME_oneline($nameobj); - Net::SSLeay::X509_free($x509); - - # remote-viewer wants comma as seperator (not '/') - $subject =~ s!^/!!; - $subject =~ s!/(\w+=)!,$1!g; + dir_glob_foreach("$pcisysfs/devices", '[a-f0-9]{4}:([a-f0-9]{2}:[a-f0-9]{2})\.([0-9])', sub { + my (undef, $id, $function) = @_; + my $res = { id => $id, function => $function}; + push @{$devices->{$id}}, $res; + }); - return $subject; + return $devices; } + 1;