use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
use PVE::INotify;
use PVE::ProcFSTools;
+use PVE::QemuConfig;
use PVE::QMPClient;
use PVE::RPCEnvironment;
use Time::HiRes qw(gettimeofday);
+use File::Copy qw(copy);
+use URI::Escape;
my $qemu_snap_storage = {rbd => 1, sheepdog => 1};
my $pcisysfs = "/sys/bus/pci";
+my $cpudesc = {
+ cputype => {
+ 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 IvyBridge Haswell Haswell-noTSX Broadwell Broadwell-noTSX Opteron_G1 Opteron_G2 Opteron_G3 Opteron_G4 Opteron_G5 host) ],
+ default => 'kvm64',
+ default_key => 1,
+ },
+ hidden => {
+ description => "Do not identify as a KVM virtual machine.",
+ type => 'boolean',
+ optional => 1,
+ default => 0
+ },
+};
+
my $confdesc = {
onboot => {
optional => 1,
numa => {
optional => 1,
type => 'boolean',
- description => "Enable/disable Numa.",
+ description => "Enable/disable NUMA.",
default => 0,
},
vcpus => {
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. You can also run without any graphic card using a serial devive as terminal.",
+ description => "Select the VGA type. If you want to use high resolution" .
+ " modes (>= 1280x1024x16) then you should use the options " .
+ "'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and " .
+ "'cirrus' for other OS types. The 'qxl' option enables the SPICE " .
+ "display sever. For win* OS you can select how many independent " .
+ "displays you want, Linux guests can add displays them self. " .
+ "You can also run without any graphic card, using a serial device" .
+ " as terminal.",
enum => [qw(std cirrus vmware qxl serial0 serial1 serial2 serial3 qxl2 qxl3 qxl4)],
},
watchdog => {
optional => 1,
type => 'string', format => 'pve-qm-watchdog',
typetext => '[[model=]i6300esb|ib700] [,[action=]reset|shutdown|poweroff|pause|debug|none]',
- description => "Create a virtual hardware watchdog device. Once enabled (by a guest action), the watchdog must be periodically polled by an agent inside the guest or else the guest will be restarted (or execute the action specified)",
+ description => "Create a virtual hardware watchdog device. Once enabled" .
+ " (by a guest action), the watchdog must be periodically polled " .
+ "by an agent inside the guest or else the watchdog will reset " .
+ "the guest (or execute the respective action specified)",
},
startdate => {
optional => 1,
optional => 1,
type => 'boolean',
default => 1,
- description => "Enable/disable the usb tablet device. This device is usually needed to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with normal VNC clients. If you're running lots of console-only guests on one host, you may consider disabling this to save some context switches. This is turned of by default if you use spice (vga=qxl).",
+ description => "Enable/disable the USB tablet device. This device is " .
+ "usually needed to allow absolute mouse positioning with VNC. " .
+ "Else the mouse runs out of sync with normal VNC clients. " .
+ "If you're running lots of console-only guests on one host, " .
+ "you may consider disabling this to save some context switches. " .
+ "This is turned off by default if you use spice (-vga=qxl).",
},
migrate_speed => {
optional => 1,
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 IvyBridge Haswell Broadwell Opteron_G1 Opteron_G2 Opteron_G3 Opteron_G4 Opteron_G5 host) ],
- default => 'kvm64',
+ format => $cpudesc,
},
parent => get_standard_option('pve-snapshot-name', {
optional => 1,
machine => {
description => "Specific the Qemu machine type.",
type => 'string',
- pattern => '(pc|pc(-i440fx)?-\d+\.\d+|q35|pc-q35-\d+\.\d+)',
+ pattern => '(pc|pc(-i440fx)?-\d+\.\d+(\.pxe)?|q35|pc-q35-\d+\.\d+(\.pxe)?)',
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,
},
description => "Sets the protection flag of the VM. This will prevent the remove operation.",
default => 0,
},
+ bios => {
+ optional => 1,
+ type => 'string',
+ enum => [ qw(seabios ovmf) ],
+ description => "Select BIOS implementation.",
+ default => 'seabios',
+ },
};
# what about other qemu settings ?
my $netdesc = {
optional => 1,
type => 'string', format => 'pve-qm-net',
- typetext => "MODEL=XX:XX:XX:XX:XX:XX [,bridge=<dev>][,queues=<nbqueues>][,rate=<mbps>] [,tag=<vlanid>][,firewall=0|1],link_down=0|1]",
+ typetext => "MODEL=XX:XX:XX:XX:XX:XX [,bridge=<dev>][,queues=<nbqueues>][,rate=<mbps>] [,tag=<vlanid>][,trunks=<vlanid[;vlanid]>][,firewall=0|1],link_down=0|1]",
description => <<EODESCR,
Specify network devices.
my $drivename_hash;
+my %drivedesc_base = (
+ volume => { alias => 'file' },
+ file => {
+ type => 'pve-volume-id',
+ default_key => 1,
+ format_description => 'volume',
+ description => "The drive's backing volume.",
+ },
+ media => {
+ type => 'string',
+ format_description => 'cdrom|disk',
+ enum => [qw(cdrom disk)],
+ description => "The drive's media type.",
+ default => 'disk',
+ optional => 1
+ },
+ cyls => {
+ type => 'integer',
+ format_description => 'count',
+ description => "Force the drive's physical geometry to have a specific cylinder count.",
+ optional => 1
+ },
+ heads => {
+ type => 'integer',
+ format_description => 'count',
+ description => "Force the drive's physical geometry to have a specific head count.",
+ optional => 1
+ },
+ secs => {
+ type => 'integer',
+ format_description => 'count',
+ description => "Force the drive's physical geometry to have a specific sector count.",
+ optional => 1
+ },
+ trans => {
+ type => 'string',
+ format_description => 'none|lba|auto',
+ enum => [qw(none lba auto)],
+ description => "Force disk geometry bios translation mode.",
+ optional => 1,
+ },
+ snapshot => {
+ type => 'boolean',
+ format_description => 'on|off',
+ description => "Whether the drive should be included when making snapshots.",
+ optional => 1,
+ },
+ cache => {
+ type => 'string',
+ format_description => 'none|writethrough|writeback|unsafe|directsync',
+ enum => [qw(none writethrough writeback unsafe directsync)],
+ description => "The drive's cache mode",
+ optional => 1,
+ },
+ format => {
+ type => 'string',
+ format_description => 'drive format',
+ enum => [qw(raw cow qcow qed qcow2 vmdk cloop)],
+ description => "The drive's backing file's data format.",
+ optional => 1,
+ },
+ size => {
+ type => 'string',
+ format => 'disk-size',
+ description => "Disk size. This is purely informational and has no effect.",
+ optional => 1,
+ },
+ backup => {
+ type => 'boolean',
+ format_description => 'on|off',
+ description => "Whether the drive should be included when making backups.",
+ optional => 1,
+ },
+ werror => {
+ type => 'string',
+ format_description => 'enospc|ignore|report|stop',
+ enum => [qw(enospc ignore report stop)],
+ description => 'Write error action.',
+ optional => 1,
+ },
+ aio => {
+ type => 'string',
+ format_description => 'native|threads',
+ enum => [qw(native threads)],
+ description => 'AIO type to use.',
+ optional => 1,
+ },
+ discard => {
+ type => 'string',
+ format_description => 'ignore|on',
+ enum => [qw(ignore on)],
+ description => 'Controls whether to pass discard/trim requests to the underlying storage.',
+ optional => 1,
+ },
+ detect_zeroes => {
+ type => 'boolean',
+ description => 'Controls whether to detect and try to optimize writes of zeroes.',
+ optional => 1,
+ },
+ serial => {
+ type => 'string',
+ format => 'urlencoded',
+ format_description => 'serial',
+ description => "The drive's reported serial number, url-encoded.",
+ optional => 1,
+ }
+);
+
+my %rerror_fmt = (
+ rerror => {
+ type => 'string',
+ format_description => 'ignore|report|stop',
+ enum => [qw(ignore report stop)],
+ description => 'Read error action.',
+ optional => 1,
+ },
+);
+
+my %iothread_fmt = ( iothread => {
+ type => 'boolean',
+ format_description => 'off|on',
+ description => "Whether to use iothreads for this drive",
+ optional => 1,
+});
+
+my %model_fmt = (
+ model => {
+ type => 'string',
+ format => 'urlencoded',
+ format_description => 'model',
+ description => "The drive's reported model name, url-encoded.",
+ optional => 1,
+ },
+);
+
+my %queues_fmt = (
+ queues => {
+ type => 'integer',
+ format_description => 'nbqueues',
+ description => "Number of queues.",
+ minimum => 2,
+ optional => 1
+ }
+);
+
+my $add_throttle_desc = sub {
+ my ($key, $type, $what, $size, $longsize) = @_;
+ $drivedesc_base{$key} = {
+ type => $type,
+ format_description => $size,
+ description => "Maximum $what speed in $longsize per second.",
+ optional => 1,
+ };
+};
+# throughput: (leaky bucket)
+$add_throttle_desc->('bps', 'integer', 'r/w speed', 'bps', 'bytes');
+$add_throttle_desc->('bps_rd', 'integer', 'read speed', 'bps', 'bytes');
+$add_throttle_desc->('bps_wr', 'integer', 'write speed', 'bps', 'bytes');
+$add_throttle_desc->('mbps', 'float', 'r/w speed', 'mbps', 'megabytes');
+$add_throttle_desc->('mbps_rd', 'float', 'read speed', 'mbps', 'megabytes');
+$add_throttle_desc->('mbps_wr', 'float', 'write speed', 'mbps', 'megabytes');
+$add_throttle_desc->('iops', 'integer', 'r/w I/O', 'iops', 'operations');
+$add_throttle_desc->('iops_rd', 'integer', 'read I/O', 'iops', 'operations');
+$add_throttle_desc->('iops_wr', 'integer', 'write I/O', 'iops', 'operations');
+
+# pools: (pool of IO before throttling starts taking effect)
+$add_throttle_desc->('mbps_max', 'float', 'unthrottled r/w pool', 'mbps', 'megabytes');
+$add_throttle_desc->('mbps_rd_max', 'float', 'unthrottled read pool', 'mbps', 'megabytes');
+$add_throttle_desc->('mbps_wr_max', 'float', 'unthrottled write pool', 'mbps', 'megabytes');
+$add_throttle_desc->('iops_max', 'integer', 'unthrottled r/w I/O pool', 'iops', 'operations');
+$add_throttle_desc->('iops_rd_max', 'integer', 'unthrottled read I/O pool', 'iops', 'operations');
+$add_throttle_desc->('iops_wr_max', 'integer', 'unthrottled write I/O pool', 'iops', 'operations');
+
+my $ide_fmt = {
+ %drivedesc_base,
+ %rerror_fmt,
+ %model_fmt,
+};
+
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] [,discard=ignore|on]',
+ type => 'string', format => $ide_fmt,
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);
+my $scsi_fmt = {
+ %drivedesc_base,
+ %iothread_fmt,
+ %queues_fmt,
+};
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] [,discard=ignore|on] [,iothread=on] [,queues=<nbqueues>]',
+ type => 'string', format => $scsi_fmt,
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);
+my $sata_fmt = {
+ %drivedesc_base,
+ %rerror_fmt,
+};
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] [,discard=ignore|on]',
+ type => 'string', format => $sata_fmt,
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);
+my $virtio_fmt = {
+ %drivedesc_base,
+ %iothread_fmt,
+ %rerror_fmt,
+};
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] [,discard=ignore|on] [,iothread=on]',
+ type => 'string', format => $virtio_fmt,
description => "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . ").",
};
PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
+my $alldrive_fmt = {
+ %drivedesc_base,
+ %rerror_fmt,
+ %iothread_fmt,
+ %model_fmt,
+ %queues_fmt,
+};
+
+my $usbformat = {
+ host => {
+ default_key => 1,
+ type => 'string', format => 'pve-qm-usb-device',
+ format_description => 'HOSTUSBDEVICE|spice',
+ description => 'The Host USB device or port or the value spice',
+ },
+ usb3 => {
+ optional => 1,
+ type => 'boolean',
+ format_description => 'yes|no',
+ description => 'Specifies whether if given host option is a USB3 device or port',
+ },
+};
+
my $usbdesc = {
optional => 1,
- type => 'string', format => 'pve-qm-usb-device',
- typetext => 'host=HOSTUSBDEVICE|spice',
+ type => 'string', format => $usbformat,
description => <<EODESCR,
Configure an USB device (n is 0 to 4). This can be used to
pass-through usb devices to the guest. HOSTUSBDEVICE syntax is:
'bus-port(.port)*' (decimal numbers) or
-'vendor_id:product_id' (hexadeciaml numbers)
+'vendor_id:product_id' (hexadeciaml numbers) or
+'spice'
You can use the 'lsusb -t' command to list existing usb devices.
The value 'spice' can be used to add a usb redirection devices for spice.
+The 'usb3' option determines whether the device is a USB3 device or not (this does currently not work reliably with spice redirection and is then ignored).
+
EODESCR
};
PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
my $hostpcidesc = {
optional => 1,
type => 'string', format => 'pve-qm-hostpci',
- typetext => "[host=]HOSTPCIDEVICE [,driver=kvm|vfio] [,rombar=on|off] [,pcie=0|1] [,x-vga=on|off]",
+ typetext => "[host=]HOSTPCIDEVICE [,rombar=on|off] [,pcie=0|1] [,x-vga=on|off]",
description => <<EODESCR,
Map host pci devices. HOSTPCIDEVICE syntax is:
description => <<EODESCR,
Create a serial device inside the VM (n is 0 to 3), and pass through a host serial device (i.e. /dev/ttyS0), or create a unix socket on the host side (use 'qm terminal' to open a terminal connection).
-Note: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
+Note: If you pass through a host serial device, it is no longer possible to migrate such machines - use with special care.
Experimental: user reported problems with this option.
EODESCR
$kvm_user_version = 'unknown';
- my $tmp = `kvm -help 2>/dev/null`;
+ my $code = sub {
+ my $line = shift;
+ if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
+ $kvm_user_version = $2;
+ }
+ };
- if ($tmp =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)[,\s]/) {
- $kvm_user_version = $2;
- }
+ eval { run_command("kvm -version", outfunc => $code); };
+ warn $@ if $@;
return $kvm_user_version;
my $kernel_has_vhost_net = -c '/dev/vhost-net';
-sub disknames {
+sub valid_drive_names {
# order is important - used to autoselect boot disk
return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))),
(map { "scsi$_" } (0 .. ($MAX_SCSI_DISKS - 1))),
(map { "sata$_" } (0 .. ($MAX_SATA_DISKS - 1))));
}
-sub valid_drivename {
+sub is_valid_drivename {
my $dev = shift;
return defined($drivename_hash->{$dev});
$drive->{media} = 'cdrom' if !$drive->{media} && $drive->{file} =~ m/^(cdrom|none)$/;
}
-sub create_conf_nolock {
- my ($vmid, $settings) = @_;
-
- my $filename = config_file($vmid);
-
- die "configuration file '$filename' already exists\n" if -f $filename;
-
- my $defaults = load_defaults();
-
- $settings->{name} = "vm$vmid" if !$settings->{name};
- $settings->{memory} = $defaults->{memory} if !$settings->{memory};
-
- my $data = '';
- foreach my $opt (keys %$settings) {
- next if !$confdesc->{$opt};
-
- my $value = $settings->{$opt};
- next if !$value;
-
- $data .= "$opt: $value\n";
- }
-
- PVE::Tools::file_set_contents($filename, $data);
-}
-
sub parse_hotplug_features {
my ($data) = @_;
die "unable to parse hotplug option\n";
}
-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]
-# [,aio=native|threads][,discard=ignore|on][,iothread=on]
+# [,aio=native|threads][,discard=ignore|on][,detect_zeroes=on|off]
+# [,iothread=on][,serial=serial][,model=model]
sub parse_drive {
my ($key, $data) = @_;
- my $res = {};
+ my ($interface, $index);
- # $key may be undefined - used to verify JSON parameters
- if (!defined($key)) {
- $res->{interface} = 'unknown'; # should not harm when used to verify parameters
- $res->{index} = 0;
- } elsif ($key =~ m/^([^\d]+)(\d+)$/) {
- $res->{interface} = $1;
- $res->{index} = $2;
+ if ($key =~ m/^([^\d]+)(\d+)$/) {
+ $interface = $1;
+ $index = $2;
} else {
return undef;
}
- 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|mbps|mbps_max|bps_rd|mbps_rd|mbps_rd_max|bps_wr|mbps_wr|mbps_wr_max|iops|iops_max|iops_rd|iops_rd_max|iops_wr|iops_wr_max|size|discard|iothread|queues)=(.+)$/) {
- 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/=/) {
- $res->{file} = $p;
- } else {
- return undef;
+ my $desc = $key =~ /^unused\d+$/ ? $alldrive_fmt
+ : $confdesc->{$key}->{format};
+ if (!$desc) {
+ warn "invalid drive key: $key\n";
+ return undef;
+ }
+ my $res = eval { PVE::JSONSchema::parse_property_string($desc, $data) };
+ return undef if !$res;
+ $res->{interface} = $interface;
+ $res->{index} = $index;
+
+ my $error = 0;
+ foreach my $opt (qw(bps bps_rd bps_wr)) {
+ if (my $bps = defined(delete $res->{$opt})) {
+ if (defined($res->{"m$opt"})) {
+ warn "both $opt and m$opt specified\n";
+ ++$error;
+ next;
}
+ $res->{"m$opt"} = sprintf("%.3f", $bps / (1024*1024.0));
}
}
-
- return undef if !$res->{file};
-
- return undef if $res->{cache} &&
- $res->{cache} !~ m/^(off|none|writethrough|writeback|unsafe|directsync)$/;
- 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+$/;
- return undef if $res->{secs} && $res->{secs} !~ m/^\d+$/;
- return undef if $res->{media} && $res->{media} !~ m/^(disk|cdrom)$/;
- return undef if $res->{trans} && $res->{trans} !~ m/^(none|lba|auto)$/;
- return undef if $res->{format} && $res->{format} !~ m/^(raw|cow|qcow|qed|qcow2|vmdk|cloop)$/;
- return undef if $res->{rerror} && $res->{rerror} !~ m/^(ignore|report|stop)$/;
- return undef if $res->{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->{iothread} && $res->{iothread} !~ m/^(on)$/;
- return undef if $res->{queues} && ($res->{queues} !~ m/^\d+$/ || $res->{queues} < 2);
+ return undef if $error;
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}) {
- 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};
return undef if $res->{interface} eq 'virtio';
}
- # rerror does not work with scsi drives
- if ($res->{rerror}) {
- return undef if $res->{interface} eq 'scsi';
+ if (my $size = $res->{size}) {
+ return undef if !defined($res->{size} = PVE::JSONSchema::parse_size($size));
}
return $res;
}
-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', 'mbps_max', 'mbps_rd_max', 'mbps_wr_max', 'backup', 'iothread', 'queues') {
- $opts .= ",$o=$drive->{$o}" if $drive->{$o};
- }
-
- if ($drive->{size}) {
- $opts .= ",size=" . &$format_size($drive->{size});
- }
-
- return "$drive->{file}$opts";
+ my $data = { %$drive };
+ delete $data->{$_} for qw(index interface);
+ return PVE::JSONSchema::print_property_string($data, $alldrive_fmt);
}
sub scsi_inquiry {
my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive);
my $unit = $drive->{index} % $maxdev;
my $devicetype = 'hd';
- my $path = '';
- if (drive_is_cdrom($drive)) {
- $devicetype = 'cd';
+ my $path = '';
+ if (drive_is_cdrom($drive)) {
+ $devicetype = 'cd';
} else {
- if ($drive->{file} =~ m|^/|) {
- $path = $drive->{file};
- } else {
- $path = PVE::Storage::path($storecfg, $drive->{file});
- }
-
- if($path =~ m/^iscsi\:\/\//){
- $devicetype = 'generic';
- } else {
- if (my $info = path_is_scsi($path)) {
- if ($info->{type} == 0) {
- $devicetype = 'block';
- } elsif ($info->{type} == 1) { # tape
- $devicetype = 'generic';
- }
- }
- }
- }
+ if ($drive->{file} =~ m|^/|) {
+ $path = $drive->{file};
+ if (my $info = path_is_scsi($path)) {
+ if ($info->{type} == 0) {
+ $devicetype = 'block';
+ } elsif ($info->{type} == 1) { # tape
+ $devicetype = 'generic';
+ }
+ }
+ } else {
+ $path = PVE::Storage::path($storecfg, $drive->{file});
+ }
- if (!$conf->{scsihw} || ($conf->{scsihw} =~ m/^lsi/)){
- $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
- } else {
- $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0,lun=$drive->{index},drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
- }
+ if($path =~ m/^iscsi\:\/\//){
+ $devicetype = 'generic';
+ }
+ }
+
+ if (!$conf->{scsihw} || ($conf->{scsihw} =~ m/^lsi/)){
+ $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
+ } else {
+ $device = "scsi-$devicetype,bus=$controller_prefix$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 $devicetype = ($drive->{media} && $drive->{media} eq 'cdrom') ? "cd" : "hd";
$device = "ide-$devicetype,bus=ide.$controller,unit=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
+ if ($devicetype eq 'hd' && (my $model = $drive->{model})) {
+ $model = URI::Escape::uri_unescape($model);
+ $device .= ",model=$model";
+ }
} elsif ($drive->{interface} eq 'sata'){
my $controller = int($drive->{index} / $MAX_SATA_DISKS);
my $unit = $drive->{index} % $MAX_SATA_DISKS;
$format = qemu_img_format($scfg, $volname);
} else {
$path = $volid;
+ $format = "raw";
}
}
my $opts = '';
+ 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);
foreach my $o (@qemu_drive_options) {
- next if $o eq 'bootindex';
$opts .= ",$o=$drive->{$o}" if $drive->{$o};
}
+ if (my $serial = $drive->{serial}) {
+ $serial = URI::Escape::uri_unescape($serial);
+ $opts .= ",serial=$serial";
+ }
$opts .= ",format=$format" if $format && !$drive->{format};
}
}
- my $detectzeroes = $drive->{discard} ? "unmap" : "on";
- $opts .= ",detect-zeroes=$detectzeroes" if !drive_is_cdrom($drive);
+ if (!drive_is_cdrom($drive)) {
+ my $detectzeroes;
+ if (defined($drive->{detect_zeroes}) && !$drive->{detect_zeroes}) {
+ $detectzeroes = 'off';
+ } elsif ($drive->{discard}) {
+ $detectzeroes = $drive->{discard} eq 'on' ? 'unmap' : 'on';
+ } else {
+ # This used to be our default with discard not being specified:
+ $detectzeroes = 'on';
+ }
+ $opts .= ",detect-zeroes=$detectzeroes" if $detectzeroes;
+ }
my $pathinfo = $path ? "file=$path," : '';
}
sub print_netdevice_full {
- my ($vmid, $conf, $net, $netid, $bridges) = @_;
+ my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files) = @_;
my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
$tmpstr .= ",vectors=$vectors,mq=on";
}
$tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex} ;
+
+ if ($use_old_bios_files) {
+ my $romfile;
+ if ($device eq 'virtio-net-pci') {
+ $romfile = 'pxe-virtio.rom';
+ } elsif ($device eq 'e1000') {
+ $romfile = 'pxe-e1000.rom';
+ } elsif ($device eq 'ne2k') {
+ $romfile = 'pxe-ne2k_pci.rom';
+ } elsif ($device eq 'pcnet') {
+ $romfile = 'pxe-pcnet.rom';
+ } elsif ($device eq 'rtl8139') {
+ $romfile = 'pxe-rtl8139.rom';
+ }
+ $tmpstr .= ",romfile=$romfile" if $romfile;
+ }
+
return $tmpstr;
}
sub print_netdev_full {
- my ($vmid, $conf, $net, $netid) = @_;
+ my ($vmid, $conf, $net, $netid, $hotplug) = @_;
my $i = '';
if ($netid =~ m/^net(\d+)$/) {
my $vmname = $conf->{name} || "vm$vmid";
my $netdev = "";
+ my $script = $hotplug ? "pve-bridge-hotplug" : "pve-bridge";
if ($net->{bridge}) {
- $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/pve-bridge,downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
+ $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script,downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
} else {
$netdev = "type=user,id=$netid,hostname=$vmname";
}
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->{rate} = $1;
} elsif ($kvp =~ m/^tag=(\d+)$/) {
$res->{tag} = $1;
+ } elsif ($kvp =~ m/^trunks=([0-9;]+)$/) {
+ $res->{trunks} = $1;
} elsif ($kvp =~ m/^firewall=([01])$/) {
$res->{firewall} = $1;
} elsif ($kvp =~ m/^link_down=([01])$/) {
$res .= ",bridge=$net->{bridge}" if $net->{bridge};
$res .= ",rate=$net->{rate}" if $net->{rate};
$res .= ",tag=$net->{tag}" if $net->{tag};
+ $res .= ",trunks=$net->{trunks}" if $net->{trunks};
$res .= ",firewall=1" if $net->{firewall};
$res .= ",link_down=1" if $net->{link_down};
$res .= ",queues=$net->{queues}" if $net->{queues};
}
}
-sub add_unused_volume {
- my ($config, $volid) = @_;
-
- my $key;
- for (my $ind = $MAX_UNUSED_DISKS - 1; $ind >= 0; $ind--) {
- my $test = "unused$ind";
- if (my $vid = $config->{$test}) {
- return if $vid eq $volid; # do not add duplicates
- } else {
- $key = $test;
- }
- }
-
- die "To many unused volume - please delete them first.\n" if !$key;
-
- $config->{$key} = $volid;
-
- return $key;
-}
-
sub vm_is_volid_owner {
my ($storecfg, $vmid, $volid) = @_;
if (!drive_is_cdrom($drive)) {
my $volid = $drive->{file};
if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
- add_unused_volume($conf, $volid, $vmid);
+ PVE::QemuConfig->add_unused_volume($conf, $volid, $vmid);
}
}
}
return $changes;
}
-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]
+my $smbios1_desc = {
+ uuid => {
+ type => 'string',
+ pattern => '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
+ format_description => 'UUID',
+ optional => 1,
+ },
+ version => {
+ type => 'string',
+ pattern => '\S+',
+ format_description => 'str',
+ optional => 1,
+ },
+ serial => {
+ type => 'string',
+ pattern => '\S+',
+ format_description => 'str',
+ optional => 1,
+ },
+ manufacturer => {
+ type => 'string',
+ pattern => '\S+',
+ format_description => 'name',
+ optional => 1,
+ },
+ product => {
+ type => 'string',
+ pattern => '\S+',
+ format_description => 'name',
+ optional => 1,
+ },
+ sku => {
+ type => 'string',
+ pattern => '\S+',
+ format_description => 'str',
+ optional => 1,
+ },
+ family => {
+ type => 'string',
+ pattern => '\S+',
+ format_description => 'str',
+ optional => 1,
+ },
};
-# 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;
- }
-
+ my $res = eval { PVE::JSONSchema::parse_property_string($smbios1_desc, $data) };
+ warn $@ if $@;
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;
+ return PVE::JSONSchema::print_property_string($smbios1, $smbios1_desc);
}
-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-smbios1', $smbios1_desc);
PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk);
sub verify_bootdisk {
my ($value, $noerr) = @_;
- return $value if valid_drivename($value);
+ return $value if is_valid_drivename($value);
return undef if $noerr;
die "unable to parse network options\n";
}
-PVE::JSONSchema::register_format('pve-qm-drive', \&verify_drive);
-sub verify_drive {
- my ($value, $noerr) = @_;
-
- return $value if parse_drive(undef, $value);
-
- return undef if $noerr;
-
- die "unable to parse drive options\n";
-}
-
PVE::JSONSchema::register_format('pve-qm-hostpci', \&verify_hostpci);
sub verify_hostpci {
my ($value, $noerr) = @_;
return undef if !$value;
- my @dl = split(/,/, $value);
- my $found;
-
my $res = {};
- foreach my $v (@dl) {
- if ($v =~ m/^host=(0x)?([0-9A-Fa-f]{4}):(0x)?([0-9A-Fa-f]{4})$/) {
- $found = 1;
- $res->{vendorid} = $2;
- $res->{productid} = $4;
- } elsif ($v =~ m/^host=(\d+)\-(\d+(\.\d+)*)$/) {
- $found = 1;
- $res->{hostbus} = $1;
- $res->{hostport} = $2;
- } elsif ($v =~ m/^spice$/) {
- $found = 1;
- $res->{spice} = 1;
- } else {
- return undef;
- }
+ if ($value =~ m/^(0x)?([0-9A-Fa-f]{4}):(0x)?([0-9A-Fa-f]{4})$/) {
+ $res->{vendorid} = $2;
+ $res->{productid} = $4;
+ } elsif ($value =~ m/^(\d+)\-(\d+(\.\d+)*)$/) {
+ $res->{hostbus} = $1;
+ $res->{hostport} = $2;
+ } elsif ($value =~ m/^spice$/i) {
+ $res->{spice} = 1;
+ } else {
+ return undef;
}
- return undef if !$found;
return $res;
}
}
}
-sub lock_config_full {
- my ($vmid, $timeout, $code, @param) = @_;
+sub check_iommu_support{
+ #fixme : need to check IOMMU support
+ #http://www.linux-kvm.org/page/How_to_assign_devices_with_VT-d_in_KVM
- my $filename = config_file_lock($vmid);
+ my $iommu=1;
+ return $iommu;
- my $res = lock_file($filename, $timeout, $code, @param);
+}
- die $@ if $@;
+sub touch_config {
+ my ($vmid) = @_;
- return $res;
+ my $conf = PVE::QemuConfig->config_file($vmid);
+ utime undef, undef, $conf;
}
-sub lock_config_mode {
- my ($vmid, $timeout, $shared, $code, @param) = @_;
+sub destroy_vm {
+ my ($storecfg, $vmid, $keep_empty_config, $skiplock) = @_;
- my $filename = config_file_lock($vmid);
+ my $conffile = PVE::QemuConfig->config_file($vmid);
- my $res = lock_file_full($filename, $timeout, $shared, $code, @param);
+ my $conf = PVE::QemuConfig->load_config($vmid);
- die $@ if $@;
+ PVE::QemuConfig->check_lock($conf) if !$skiplock;
- return $res;
-}
+ # only remove disks owned by this VM
+ foreach_drive($conf, sub {
+ my ($ds, $drive) = @_;
-sub lock_config {
- my ($vmid, $code, @param) = @_;
+ return if drive_is_cdrom($drive);
- return lock_config_full($vmid, 10, $code, @param);
-}
+ my $volid = $drive->{file};
-sub cfs_config_path {
- my ($vmid, $node) = @_;
+ return if !$volid || $volid =~ m|^/|;
- $node = $nodename if !$node;
- return "nodes/$node/qemu-server/$vmid.conf";
-}
-
-sub check_iommu_support{
- #fixme : need to check IOMMU support
- #http://www.linux-kvm.org/page/How_to_assign_devices_with_VT-d_in_KVM
-
- my $iommu=1;
- return $iommu;
-
-}
-
-sub config_file {
- my ($vmid, $node) = @_;
-
- my $cfspath = cfs_config_path($vmid, $node);
- return "/etc/pve/$cfspath";
-}
-
-sub config_file_lock {
- my ($vmid) = @_;
-
- return "$lock_dir/lock-$vmid.conf";
-}
-
-sub touch_config {
- my ($vmid) = @_;
-
- my $conf = config_file($vmid);
- utime undef, undef, $conf;
-}
-
-sub destroy_vm {
- my ($storecfg, $vmid, $keep_empty_config) = @_;
-
- my $conffile = config_file($vmid);
-
- my $conf = load_config($vmid);
-
- check_lock($conf);
-
- # only remove disks owned by this VM
- foreach_drive($conf, sub {
- my ($ds, $drive) = @_;
-
- return if drive_is_cdrom($drive);
-
- my $volid = $drive->{file};
-
- return if !$volid || $volid =~ m|^/|;
-
- my ($path, $owner) = PVE::Storage::path($storecfg, $volid);
- return if !$path || !$owner || ($owner != $vmid);
+ my ($path, $owner) = PVE::Storage::path($storecfg, $volid);
+ return if !$path || !$owner || ($owner != $vmid);
PVE::Storage::vdisk_free($storecfg, $volid);
});
warn $@ if $@;
}
-sub load_config {
- my ($vmid, $node) = @_;
-
- my $cfspath = cfs_config_path($vmid, $node);
-
- my $conf = PVE::Cluster::cfs_read_file($cfspath);
-
- die "no such VM ('$vmid')\n" if !defined($conf);
-
- return $conf;
-}
-
sub parse_vm_config {
my ($filename, $raw) = @_;
$cref->{$key} = $value;
- if (!$snapname && valid_drivename($key)) {
+ if (!$snapname && is_valid_drivename($key)) {
my $drive = parse_drive($key, $value);
$used_volids->{$drive->{file}} = 1 if $drive && $drive->{file};
}
return $raw;
}
-sub update_config_nolock {
- my ($vmid, $conf, $skiplock) = @_;
-
- check_lock($conf) if !$skiplock;
-
- my $cfspath = cfs_config_path($vmid);
-
- PVE::Cluster::cfs_write_file($cfspath, $conf);
-}
-
-sub update_config {
- my ($vmid, $conf, $skiplock) = @_;
-
- lock_config($vmid, &update_config_nolock, $conf, $skiplock);
-}
-
sub load_defaults {
my $res = {};
foreach my $k (keys %$conf) {
next if $k =~ m/^usb/ && ($conf->{$k} eq 'spice');
+ # sockets are safe: they will recreated be on the target side post-migrate
+ next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
$loc_res = 1 if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
}
return $nodehash
}
-sub check_lock {
- my ($conf) = @_;
-
- die "VM is locked ($conf->{lock})\n" if $conf->{lock};
-}
-
sub check_cmdline {
my ($pidfile, $pid) = @_;
sub check_running {
my ($vmid, $nocheck, $node) = @_;
- my $filename = config_file($vmid, $node);
+ my $filename = PVE::QemuConfig->config_file($vmid, $node);
die "unable to find configuration file for VM $vmid - no such machine\n"
if !$nocheck && ! -f $filename;
my $bootdisk = $conf->{bootdisk};
return undef if !$bootdisk;
- return undef if !valid_drivename($bootdisk);
+ return undef if !is_valid_drivename($bootdisk);
return undef if !$conf->{$bootdisk};
foreach my $vmid (keys %$list) {
next if $opt_vmid && ($vmid ne $opt_vmid);
- my $cfspath = cfs_config_path($vmid);
+ my $cfspath = PVE::QemuConfig->cfs_config_path($vmid);
my $conf = PVE::Cluster::cfs_read_file($cfspath) || {};
my $d = {};
$d->{diskread} = 0;
$d->{diskwrite} = 0;
- $d->{template} = is_template($conf);
+ $d->{template} = PVE::QemuConfig->is_template($conf);
$res->{$vmid} = $d;
}
sub foreach_drive {
my ($conf, $func) = @_;
- foreach my $ds (keys %$conf) {
- next if !valid_drivename($ds);
+ foreach my $ds (valid_drive_names()) {
+ next if !defined($conf->{$ds});
my $drive = parse_drive($ds, $conf->{$ds});
next if !$drive;
my $bridges = {};
my $kvmver = kvm_user_version();
my $vernum = 0; # unknown
+ my $ostype = $conf->{ostype};
if ($kvmver =~ m/^(\d+)\.(\d+)$/) {
$vernum = $1*1000000+$2*1000;
} elsif ($kvmver =~ m/^(\d+)\.(\d+)\.(\d+)$/) {
my $q35 = machine_type_is_q35($conf);
my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
my $machine_type = $forcemachine || $conf->{machine};
+ my $use_old_bios_files = undef;
+ ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
my $cpuunits = defined($conf->{cpuunits}) ?
$conf->{cpuunits} : $defaults->{cpuunits};
push @$cmd, '--scope';
push @$cmd, '--slice', "qemu";
push @$cmd, '--unit', $vmid;
+ # set KillMode=none, so that systemd don't kill those scopes
+ # at shutdown (pve-manager service should stop the VMs instead)
+ push @$cmd, '-p', "KillMode=none";
push @$cmd, '-p', "CPUShares=$cpuunits";
if ($conf->{cpulimit}) {
my $cpulimit = int($conf->{cpulimit} * 100);
push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait";
push @$cmd, '-mon', "chardev=qmp,mode=control";
- my $socket = vnc_socket($vmid);
- push @$cmd, '-vnc', "unix:$socket,x509,password";
push @$cmd, '-pidfile' , pidfile_name($vmid);
push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
}
+ if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
+ my $ovmfvar = "OVMF_VARS-pure-efi.fd";
+ my $ovmfvar_src = "/usr/share/kvm/$ovmfvar";
+ my $ovmfvar_dst = "/tmp/$vmid-$ovmfvar";
+ PVE::Tools::file_copy($ovmfvar_src, $ovmfvar_dst, 256*1024);
+ push @$cmd, '-drive', "if=pflash,format=raw,readonly,file=/usr/share/kvm/OVMF-pure-efi.fd";
+ push @$cmd, '-drive', "if=pflash,format=raw,file=$ovmfvar_dst";
+ }
+
if ($q35) {
# the q35 chipset support native usb2, so we enable usb controller
# by default for this machine type
my $use_usb2 = 0;
for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
next if !$conf->{"usb$i"};
+ my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) };
+ next if !$d || $d->{usb3}; # do not add usb2 controller if we have only usb3 devices
$use_usb2 = 1;
}
# include usb device config
push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2;
}
+ # add usb3 controller if needed
+
+ my $use_usb3 = 0;
+ for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
+ next if !$conf->{"usb$i"};
+ my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) };
+ next if !$d || !$d->{usb3};
+ $use_usb3 = 1;
+ }
+
+ $pciaddr = print_pci_addr("xhci", $bridges);
+ push @$devices, '-device', "nec-usb-xhci,id=xhci$pciaddr" if $use_usb3;
+
my $vga = $conf->{vga};
my $qxlnum = vga_conf_has_spice($vga);
push @$devices, '-device', print_tabletdevice_full($conf) if $tablet;
+ my $kvm_off = 0;
# host pci devices
for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
my $d = parse_hostpci($conf->{"hostpci$i"});
}
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';
+ $kvm_off = 1;
$vga = 'none';
+ if ($ostype eq 'win7' || $ostype eq 'win8' || $ostype eq 'w2k8') {
+ push @$cpuFlags , 'hv_vendor_id=proxmox';
+ }
+ if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
+ $xvga = "";
+ }
}
- $driver = "vfio-pci" if $xvga ne '';
my $pcidevices = $d->{pciid};
my $multifunction = 1 if @$pcidevices > 1;
$id .= ".$j" if $multifunction;
my $addr = $pciaddr;
$addr .= ".$j" if $multifunction;
- my $devicestr = "$driver,host=$pcidevice->{id}.$pcidevice->{function},id=$id$addr";
+ my $devicestr = "vfio-pci,host=$pcidevice->{id}.$pcidevice->{function},id=$id$addr";
if($j == 0){
$devicestr .= "$rombar$xvga";
# usb devices
for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
- my $d = parse_usb_device($conf->{"usb$i"});
+ next if !$conf->{"usb$i"};
+ my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) };
next if !$d;
- if ($d->{vendorid} && $d->{productid}) {
- push @$devices, '-device', "usb-host,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
- } elsif (defined($d->{hostbus}) && defined($d->{hostport})) {
- push @$devices, '-device', "usb-host,hostbus=$d->{hostbus},hostport=$d->{hostport}";
- } elsif ($d->{spice}) {
- # usb redir support for spice
- push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir";
- push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=ehci.0";
+
+ # if it is a usb3 device, attach it to the xhci controller, else omit the bus option
+ my $usbbus = '';
+ if (defined($d->{usb3}) && $d->{usb3}) {
+ $usbbus = ',bus=xhci.0';
+ }
+
+ if (defined($d->{host})) {
+ $d = parse_usb_device($d->{host});
+ if (defined($d->{vendorid}) && defined($d->{productid})) {
+ push @$devices, '-device', "usb-host$usbbus,vendorid=0x$d->{vendorid},productid=0x$d->{productid}";
+ } elsif (defined($d->{hostbus}) && defined($d->{hostport})) {
+ push @$devices, '-device', "usb-host$usbbus,hostbus=$d->{hostbus},hostport=$d->{hostport}";
+ } elsif (defined($d->{spice}) && $d->{spice}) {
+ # usb redir support for spice, currently no usb3
+ push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir";
+ push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=ehci.0";
+ }
}
}
push @$cmd, '-vga', $vga if $vga && $vga !~ m/^serial\d+$/; # for kvm 77 and later
+ if ($vga && $vga !~ m/^serial\d+$/ && $vga ne 'none'){
+ my $socket = vnc_socket($vmid);
+ push @$cmd, '-vnc', "unix:$socket,x509,password";
+ } else {
+ push @$cmd, '-nographic';
+ }
+
# time drift fix
my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
my $useLocaltime = $conf->{localtime};
- if (my $ost = $conf->{ostype}) {
+ if ($ostype) {
# other, wxp, w2k, w2k3, w2k8, wvista, win7, win8, l24, l26, solaris
- if ($ost =~ m/^w/) { # windows
+ if ($ostype =~ m/^w/) { # windows
$useLocaltime = 1 if !defined($conf->{localtime});
# use time drift fix when acpi is enabled
}
}
- if ($ost eq 'win7' || $ost eq 'win8' || $ost eq 'w2k8' ||
- $ost eq 'wvista') {
+ if ($ostype eq 'win7' || $ostype eq 'win8' || $ostype eq 'w2k8' ||
+ $ostype eq 'wvista') {
push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
push @$cmd, '-no-hpet';
if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
}
}
- if ($ost eq 'win7' || $ost eq 'win8') {
+ if ($ostype eq 'win7' || $ostype eq 'win8') {
push @$cpuFlags , 'hv_relaxed' if !$nokvm;
}
}
}
my $cpu = $nokvm ? "qemu64" : "kvm64";
- $cpu = $conf->{cpu} if $conf->{cpu};
+ if (my $cputype = $conf->{cpu}) {
+ my $cpuconf = PVE::JSONSchema::parse_property_string($cpudesc, $cputype)
+ or die "Cannot parse cpu description: $cputype\n";
+ $cpu = $cpuconf->{cputype};
+ $kvm_off = 1 if $cpuconf->{hidden};
+ }
push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64';
push @$cpuFlags, 'enforce' if $cpu ne 'host' && !$nokvm;
+ push @$cpuFlags, 'kvm=off' if $kvm_off;
+
$cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
push @$cmd, '-cpu', $cpu;
#if dimm_memory is not aligned to dimm map
if($current_size > $memory) {
$conf->{memory} = $current_size;
- update_config_nolock($vmid, $conf, 1);
+ PVE::QemuConfig->write_config($vmid, $conf);
}
});
}
my $netdevfull = print_netdev_full($vmid,$conf,$d,"net$i");
push @$devices, '-netdev', $netdevfull;
- my $netdevicefull = print_netdevice_full($vmid,$conf,$d,"net$i",$bridges);
+ my $netdevicefull = print_netdevice_full($vmid, $conf, $d, "net$i", $bridges, $use_old_bios_files);
push @$devices, '-device', $netdevicefull;
}
} elsif ($deviceid =~ m/^(net)(\d+)$/) {
return undef if !qemu_netdevadd($vmid, $conf, $device, $deviceid);
- my $netdevicefull = print_netdevice_full($vmid, $conf, $device, $deviceid);
+
+ my $machine_type = PVE::QemuServer::qemu_machine_pxe($vmid, $conf);
+ my $use_old_bios_files = undef;
+ ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
+
+ my $netdevicefull = print_netdevice_full($vmid, $conf, $device, $deviceid, undef, $use_old_bios_files);
qemu_deviceadd($vmid, $netdevicefull);
eval { qemu_deviceaddverify($vmid, $deviceid); };
if (my $err = $@) {
my $devices_list = vm_devices_list($vmid);
foreach my $opt (keys %{$devices_list}) {
- if (PVE::QemuServer::valid_drivename($opt)) {
+ if (PVE::QemuServer::is_valid_drivename($opt)) {
my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt});
if($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
return 1;
sub qemu_netdevadd {
my ($vmid, $conf, $device, $deviceid) = @_;
- my $netdev = print_netdev_full($vmid, $conf, $device, $deviceid);
+ my $netdev = print_netdev_full($vmid, $conf, $device, $deviceid, 1);
my %options = split(/[=,]/, $netdev);
vm_mon_cmd($vmid, "netdev_add", %options);
}
#update conf after each succesful module hotplug
$conf->{memory} = $current_size;
- update_config_nolock($vmid, $conf, 1);
+ PVE::QemuConfig->write_config($vmid, $conf);
});
} else {
$conf->{memory} = $current_size;
eval { qemu_objectdel($vmid, "mem-$name"); };
- update_config_nolock($vmid, $conf, 1);
+ PVE::QemuConfig->write_config($vmid, $conf);
});
}
}
}
sub qemu_block_set_io_throttle {
- my ($vmid, $deviceid, $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr) = @_;
+ my ($vmid, $deviceid,
+ $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
+ $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max) = @_;
return if !check_running($vmid) ;
- vm_mon_cmd($vmid, "block_set_io_throttle", device => $deviceid, bps => int($bps), bps_rd => int($bps_rd), bps_wr => int($bps_wr), iops => int($iops), iops_rd => int($iops_rd), iops_wr => int($iops_wr));
+ vm_mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
+ bps => int($bps),
+ bps_rd => int($bps_rd),
+ bps_wr => int($bps_wr),
+ iops => int($iops),
+ iops_rd => int($iops_rd),
+ iops_wr => int($iops_wr),
+ bps_max => int($bps_max),
+ bps_rd_max => int($bps_rd_max),
+ bps_wr_max => int($bps_wr_max),
+ iops_max => int($iops_max),
+ iops_rd_max => int($iops_rd_max),
+ iops_wr_max => int($iops_wr_max)
+ );
}
my $enabled_cap = {
"auto-converge" => 1,
- "xbzrle" => 0,
+ "xbzrle" => 1,
"x-rdma-pin-all" => 0,
"zero-blocks" => 0,
+ "compress" => 0
};
my $supported_capabilities = vm_mon_cmd_nocheck($vmid, "query-migrate-capabilities");
}
if ($changes) {
- update_config_nolock($vmid, $conf, 1);
- $conf = load_config($vmid); # update/reload
+ PVE::QemuConfig->write_config($vmid, $conf);
+ $conf = PVE::QemuConfig->load_config($vmid); # update/reload
}
my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
} elsif ($opt =~ m/^net(\d+)$/) {
die "skip\n" if !$hotplug_features->{network};
vm_deviceunplug($vmid, $conf, $opt);
- } elsif (valid_drivename($opt)) {
+ } elsif (is_valid_drivename($opt)) {
die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
vm_deviceunplug($vmid, $conf, $opt);
vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
# save new config if hotplug was successful
delete $conf->{$opt};
vmconfig_undelete_pending_option($conf, $opt);
- update_config_nolock($vmid, $conf, 1);
- $conf = load_config($vmid); # update/reload
+ PVE::QemuConfig->write_config($vmid, $conf);
+ $conf = PVE::QemuConfig->load_config($vmid); # update/reload
}
}
# some changes can be done without hotplug
vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
$vmid, $opt, $value);
- } elsif (valid_drivename($opt)) {
+ } elsif (is_valid_drivename($opt)) {
# some changes can be done without hotplug
vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
$vmid, $opt, $value, 1);
# save new config if hotplug was successful
$conf->{$opt} = $value;
delete $conf->{pending}->{$opt};
- update_config_nolock($vmid, $conf, 1);
- $conf = load_config($vmid); # update/reload
+ PVE::QemuConfig->write_config($vmid, $conf);
+ $conf = PVE::QemuConfig->load_config($vmid); # update/reload
}
}
}
$rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
# check if the disk is really unused
- my $used_paths = PVE::QemuServer::get_used_paths($vmid, $storecfg, $conf, 1, $key);
- my $path = PVE::Storage::path($storecfg, $volid);
die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
- if $used_paths->{$path};
+ if is_volume_in_use($storecfg, $conf, $key, $volid);
PVE::Storage::vdisk_free($storecfg, $volid);
return 1;
} else {
my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
while (my ($opt, $force) = each %$pending_delete_hash) {
die "internal error" if $opt =~ m/^unused/;
- $conf = load_config($vmid); # update/reload
+ $conf = PVE::QemuConfig->load_config($vmid); # update/reload
if (!defined($conf->{$opt})) {
vmconfig_undelete_pending_option($conf, $opt);
- update_config_nolock($vmid, $conf, 1);
- } elsif (valid_drivename($opt)) {
+ PVE::QemuConfig->write_config($vmid, $conf);
+ } elsif (is_valid_drivename($opt)) {
vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
vmconfig_undelete_pending_option($conf, $opt);
delete $conf->{$opt};
- update_config_nolock($vmid, $conf, 1);
+ PVE::QemuConfig->write_config($vmid, $conf);
} else {
vmconfig_undelete_pending_option($conf, $opt);
delete $conf->{$opt};
- update_config_nolock($vmid, $conf, 1);
+ PVE::QemuConfig->write_config($vmid, $conf);
}
}
- $conf = load_config($vmid); # update/reload
+ $conf = PVE::QemuConfig->load_config($vmid); # update/reload
foreach my $opt (keys %{$conf->{pending}}) { # add/change
- $conf = load_config($vmid); # update/reload
+ $conf = PVE::QemuConfig->load_config($vmid); # update/reload
if (defined($conf->{$opt}) && ($conf->{$opt} eq $conf->{pending}->{$opt})) {
# skip if nothing changed
- } elsif (valid_drivename($opt)) {
+ } elsif (is_valid_drivename($opt)) {
vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
if defined($conf->{$opt});
$conf->{$opt} = $conf->{pending}->{$opt};
}
delete $conf->{pending}->{$opt};
- update_config_nolock($vmid, $conf, 1);
+ PVE::QemuConfig->write_config($vmid, $conf);
}
}
die "internal error" if $opt !~ m/net(\d+)/;
my $iface = "tap${vmid}i$1";
- if (&$safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
- PVE::Network::tap_rate_limit($iface, $newnet->{rate});
- }
-
if (&$safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
&$safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
+ &$safe_string_ne($oldnet->{trunks}, $newnet->{trunks}) ||
&$safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
PVE::Network::tap_unplug($iface);
- PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall});
+ PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
+ } elsif (&$safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
+ # Rate can be applied on its own but any change above needs to
+ # include the rate in tap_plug since OVS resets everything.
+ PVE::Network::tap_rate_limit($iface, $newnet->{rate});
}
if (&$safe_string_ne($oldnet->{link_down}, $newnet->{link_down})) {
die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
# hotplug new disks
+ PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive);
}
sub vm_start {
- my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused, $forcemachine, $spice_ticket) = @_;
+ my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused,
+ $forcemachine, $spice_ticket) = @_;
- lock_config($vmid, sub {
- my $conf = load_config($vmid, $migratedfrom);
+ PVE::QemuConfig->lock_config($vmid, sub {
+ my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom);
- die "you can't start a vm if it's a template\n" if is_template($conf);
+ die "you can't start a vm if it's a template\n" if PVE::QemuConfig->is_template($conf);
- check_lock($conf) if !$skiplock;
+ PVE::QemuConfig->check_lock($conf) if !$skiplock;
die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
if (!$statefile && scalar(keys %{$conf->{pending}})) {
vmconfig_apply_pending($vmid, $conf, $storecfg);
- $conf = load_config($vmid); # update/reload
+ $conf = PVE::QemuConfig->load_config($vmid); # update/reload
}
my $defaults = load_defaults();
my $nodename = PVE::INotify::nodename();
if ($datacenterconf->{migration_unsecure}) {
$localip = PVE::Cluster::remote_node_ip($nodename, 1);
+ $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
}
my $pfamily = PVE::Tools::get_host_address_family($nodename);
$migrate_port = PVE::Tools::next_migrate_port($pfamily);
- $migrate_uri = "tcp:[${localip}]:${migrate_port}";
+ $migrate_uri = "tcp:${localip}:${migrate_port}";
push @$cmd, '-incoming', $migrate_uri;
push @$cmd, '-S';
} else {
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 unbind/bind pci group to vfio '$pciid'\n" if !pci_dev_group_bind_to_vfio($pciid);
die "can't reset pci device '$pciid'\n" if $info->{has_fl_reset} and !pci_dev_reset($info);
}
}
eval { run_command($cmd, timeout => $statefile ? undef : 30,
umask => 0077); };
- my $err = $@;
- die "start failed: $err" if $err;
+
+ if (my $err = $@) {
+ # deactivate volumes if start fails
+ eval { PVE::Storage::deactivate_volumes($storecfg, $vollist); };
+ die "start failed: $err";
+ }
print "migration listens on $migrate_uri\n" if $migrate_uri;
sub vm_commandline {
my ($storecfg, $vmid) = @_;
- my $conf = load_config($vmid);
+ my $conf = PVE::QemuConfig->load_config($vmid);
my $defaults = load_defaults();
sub vm_reset {
my ($vmid, $skiplock) = @_;
- lock_config($vmid, sub {
+ PVE::QemuConfig->lock_config($vmid, sub {
- my $conf = load_config($vmid);
+ my $conf = PVE::QemuConfig->load_config($vmid);
- check_lock($conf) if !$skiplock;
+ PVE::QemuConfig->check_lock($conf) if !$skiplock;
vm_mon_cmd($vmid, "system_reset");
});
if ($migratedfrom){
my $pid = check_running($vmid, $nocheck, $migratedfrom);
kill 15, $pid if $pid;
- my $conf = load_config($vmid, $migratedfrom);
+ my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom);
vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 0);
return;
}
- lock_config($vmid, sub {
+ PVE::QemuConfig->lock_config($vmid, sub {
my $pid = check_running($vmid, $nocheck);
return if !$pid;
my $conf;
if (!$nocheck) {
- $conf = load_config($vmid);
- check_lock($conf) if !$skiplock;
+ $conf = PVE::QemuConfig->load_config($vmid);
+ PVE::QemuConfig->check_lock($conf) if !$skiplock;
if (!defined($timeout) && $shutdown && $conf->{startup}) {
my $opts = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
$timeout = $opts->{down} if $opts->{down};
sub vm_suspend {
my ($vmid, $skiplock) = @_;
- lock_config($vmid, sub {
+ PVE::QemuConfig->lock_config($vmid, sub {
- my $conf = load_config($vmid);
+ my $conf = PVE::QemuConfig->load_config($vmid);
- check_lock($conf) if !($skiplock || ($conf->{lock} && $conf->{lock} eq 'backup'));
+ PVE::QemuConfig->check_lock($conf)
+ if !($skiplock || PVE::QemuConfig->has_lock($conf, 'backup'));
vm_mon_cmd($vmid, "stop");
});
}
sub vm_resume {
- my ($vmid, $skiplock) = @_;
+ my ($vmid, $skiplock, $nocheck) = @_;
+
+ PVE::QemuConfig->lock_config($vmid, sub {
- lock_config($vmid, sub {
+ if (!$nocheck) {
- my $conf = load_config($vmid);
+ my $conf = PVE::QemuConfig->load_config($vmid);
- check_lock($conf) if !($skiplock || ($conf->{lock} && $conf->{lock} eq 'backup'));
+ PVE::QemuConfig->check_lock($conf)
+ if !($skiplock || PVE::QemuConfig->has_lock($conf, 'backup'));
- vm_mon_cmd($vmid, "cont");
+ vm_mon_cmd($vmid, "cont");
+
+ } else {
+ vm_mon_cmd_nocheck($vmid, "cont");
+ }
});
}
sub vm_sendkey {
my ($vmid, $skiplock, $key) = @_;
- lock_config($vmid, sub {
+ PVE::QemuConfig->lock_config($vmid, sub {
- my $conf = load_config($vmid);
+ my $conf = PVE::QemuConfig->load_config($vmid);
# there is no qmp command, so we use the human monitor command
vm_human_monitor_command($vmid, "sendkey $key");
sub vm_destroy {
my ($storecfg, $vmid, $skiplock) = @_;
- lock_config($vmid, sub {
+ PVE::QemuConfig->lock_config($vmid, sub {
- my $conf = load_config($vmid);
-
- check_lock($conf) if !$skiplock;
+ my $conf = PVE::QemuConfig->load_config($vmid);
if (!check_running($vmid)) {
- destroy_vm($storecfg, $vmid);
+ destroy_vm($storecfg, $vmid, undef, $skiplock);
} else {
die "VM $vmid is running - destroy failed\n";
}
return file_write($fn, "1");
}
-sub pci_dev_bind_to_stub {
- my ($dev) = @_;
-
- my $name = $dev->{name};
-
- my $testdir = "$pcisysfs/drivers/pci-stub/$name";
- return 1 if -d $testdir;
-
- my $data = "$dev->{vendor} $dev->{product}";
- return undef if !file_write("$pcisysfs/drivers/pci-stub/new_id", $data);
-
- my $fn = "$pcisysfs/devices/$name/driver/unbind";
- if (!file_write($fn, $name)) {
- return undef if -f $fn;
- }
-
- $fn = "$pcisysfs/drivers/pci-stub/bind";
- if (! -d $testdir) {
- return undef if !file_write($fn, $name);
- }
-
- return -d $testdir;
-}
-
sub pci_dev_bind_to_vfio {
my ($dev) = @_;
'net29' => { bus => 1, addr => 24 },
'net30' => { bus => 1, addr => 25 },
'net31' => { bus => 1, addr => 26 },
+ 'xhci' => { bus => 1, addr => 27 },
'virtio6' => { bus => 2, addr => 1 },
'virtio7' => { bus => 2, addr => 2 },
'virtio8' => { bus => 2, addr => 3 },
} elsif ($line =~ m/^((ide|scsi|virtio|sata)\d+):\s*(\S+)\s*$/) {
my $virtdev = $1;
my $value = $3;
- if ($line =~ m/backup=no/) {
+ my $di = parse_drive($virtdev, $value);
+ if (defined($di->{backup}) && !$di->{backup}) {
print $outfd "#$line";
- } elsif ($virtdev && $map->{$virtdev}) {
- my $di = parse_drive($virtdev, $value);
+ } elsif ($map->{$virtdev}) {
delete $di->{format}; # format can change on restore
$di->{file} = $map->{$virtdev};
$value = print_drive($vmid, $di);
return $volid_hash;
}
-sub get_used_paths {
- my ($vmid, $storecfg, $conf, $scan_snapshots, $skip_drive) = @_;
+sub is_volume_in_use {
+ my ($storecfg, $conf, $skip_drive, $volid) = @_;
- my $used_path = {};
+ my $path = PVE::Storage::path($storecfg, $volid);
my $scan_config = sub {
my ($cref, $snapname) = @_;
foreach my $key (keys %$cref) {
my $value = $cref->{$key};
- if (valid_drivename($key)) {
+ if (is_valid_drivename($key)) {
next if $skip_drive && $key eq $skip_drive;
my $drive = parse_drive($key, $value);
next if !$drive || !$drive->{file} || drive_is_cdrom($drive);
+ return 1 if $volid eq $drive->{file};
if ($drive->{file} =~ m!^/!) {
- $used_path->{$drive->{file}}++; # = 1;
+ return 1 if $drive->{file} eq $path;
} else {
my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1);
next if !$storeid;
my $scfg = PVE::Storage::storage_config($storecfg, $storeid, 1);
next if !$scfg;
- my $path = PVE::Storage::path($storecfg, $drive->{file}, $snapname);
- $used_path->{$path}++; # = 1;
+ return 1 if $path eq PVE::Storage::path($storecfg, $drive->{file}, $snapname);
}
}
}
+
+ return 0;
};
- &$scan_config($conf);
+ return 1 if &$scan_config($conf);
undef $skip_drive;
- if ($scan_snapshots) {
- foreach my $snapname (keys %{$conf->{snapshots}}) {
- &$scan_config($conf->{snapshots}->{$snapname}, $snapname);
- }
+ foreach my $snapname (keys %{$conf->{snapshots}}) {
+ return 1 if &$scan_config($conf->{snapshots}->{$snapname}, $snapname);
}
- return $used_path;
+ return 0;
}
sub update_disksize {
# update size info
foreach my $opt (keys %$conf) {
- if (valid_drivename($opt)) {
+ if (is_valid_drivename($opt)) {
my $drive = parse_drive($opt, $conf->{$opt});
my $volid = $drive->{file};
next if !$volid;
next if !$path; # just to be sure
next if $usedpath->{$path};
$changes = 1;
- add_unused_volume($conf, $volid);
+ PVE::QemuConfig->add_unused_volume($conf, $volid);
$usedpath->{$path} = 1; # avoid to add more than once (aliases)
}
my $updatefn = sub {
my ($vmid) = @_;
- my $conf = load_config($vmid);
+ my $conf = PVE::QemuConfig->load_config($vmid);
- check_lock($conf);
+ PVE::QemuConfig->check_lock($conf);
my $vm_volids = {};
foreach my $volid (keys %$volid_hash) {
my $changes = update_disksize($vmid, $conf, $vm_volids);
- update_config_nolock($vmid, $conf, 1) if $changes;
+ PVE::QemuConfig->write_config($vmid, $conf) if $changes;
};
if (defined($vmid)) {
if ($nolock) {
&$updatefn($vmid);
} else {
- lock_config($vmid, $updatefn, $vmid);
+ PVE::QemuConfig->lock_config($vmid, $updatefn, $vmid);
}
} else {
my $vmlist = config_list();
if ($nolock) {
&$updatefn($vmid);
} else {
- lock_config($vmid, $updatefn, $vmid);
+ PVE::QemuConfig->lock_config($vmid, $updatefn, $vmid);
}
}
}
my $rpcenv = PVE::RPCEnvironment::get();
- my $conffile = config_file($vmid);
+ my $conffile = PVE::QemuConfig->config_file($vmid);
my $tmpfn = "$conffile.$$.tmp";
# Note: $oldconf is undef if VM does not exists
- my $oldconf = PVE::Cluster::cfs_read_file(cfs_config_path($vmid));
+ my $cfs_path = PVE::QemuConfig->cfs_config_path($vmid);
+ my $oldconf = PVE::Cluster::cfs_read_file($cfs_path);
my $print_devmap = sub {
my $virtdev_hash = {};
my $fh = IO::File->new($cfgfn, "r") ||
"unable to read qemu-server.conf - $!\n";
+ my $fwcfgfn = "$tmpdir/qemu-server.fw";
+ if (-f $fwcfgfn) {
+ my $pve_firewall_dir = '/etc/pve/firewall';
+ mkdir $pve_firewall_dir; # make sure the dir exists
+ PVE::Tools::file_copy($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
+ }
+
while (defined(my $line = <$fh>)) {
if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
PVE::Storage::vdisk_free($cfg, $volid);
}
});
+
+ # delete vmstate files
+ # since after the restore we have no snapshots anymore
+ foreach my $snapname (keys %{$oldconf->{snapshots}}) {
+ my $snap = $oldconf->{snapshots}->{$snapname};
+ if ($snap->{vmstate}) {
+ eval { PVE::Storage::vdisk_free($cfg, $snap->{vmstate}); };
+ if (my $err = $@) {
+ warn $err;
+ }
+ }
+ }
}
my $map = {};
PVE::Storage::activate_volumes($cfg,[$volid]);
my $write_zeros = 1;
- # fixme: what other storages types initialize volumes with zero?
- if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' || $scfg->{type} eq 'glusterfs' ||
- $scfg->{type} eq 'sheepdog' || $scfg->{type} eq 'rbd') {
+ if (PVE::Storage::volume_has_feature($cfg, 'sparseinit', $volid)) {
$write_zeros = 0;
}
my $storecfg = cfs_read_file('storage.cfg');
# destroy existing data - keep empty config
- my $vmcfgfn = config_file($vmid);
+ my $vmcfgfn = PVE::QemuConfig->config_file($vmid);
destroy_vm($storecfg, $vmid, 1) if -f $vmcfgfn;
my $tocmd = "/usr/lib/qemu-server/qmextract";
local $ENV{VZDUMP_VMID} = $vmid;
local $ENV{VZDUMP_USER} = $user;
- my $conffile = config_file($vmid);
+ my $conffile = PVE::QemuConfig->config_file($vmid);
my $tmpfn = "$conffile.$$.tmp";
# disable interrupts (always do cleanups)
warn $@ if $@;
};
-
-# Internal snapshots
-
-# NOTE: Snapshot create/delete involves several non-atomic
-# action, and can take a long time.
-# So we try to avoid locking the file and use 'lock' variable
-# inside the config file instead.
-
-my $snapshot_copy_config = sub {
- my ($source, $dest) = @_;
-
- foreach my $k (keys %$source) {
- next if $k eq 'snapshots';
- next if $k eq 'snapstate';
- next if $k eq 'snaptime';
- next if $k eq 'vmstate';
- next if $k eq 'lock';
- next if $k eq 'digest';
- next if $k eq 'description';
- next if $k =~ m/^unused\d+$/;
-
- $dest->{$k} = $source->{$k};
- }
-};
-
-my $snapshot_apply_config = sub {
- my ($conf, $snap) = @_;
-
- # copy snapshot list
- my $newconf = {
- snapshots => $conf->{snapshots},
- };
-
- # keep description and list of unused disks
- foreach my $k (keys %$conf) {
- next if !($k =~ m/^unused\d+$/ || $k eq 'description');
- $newconf->{$k} = $conf->{$k};
- }
-
- &$snapshot_copy_config($snap, $newconf);
-
- return $newconf;
-};
-
sub foreach_writable_storage {
my ($conf, $func) = @_;
my $sidhash = {};
foreach my $ds (keys %$conf) {
- next if !valid_drivename($ds);
+ next if !is_valid_drivename($ds);
my $drive = parse_drive($ds, $conf->{$ds});
next if !$drive;
}
}
-my $alloc_vmstate_volid = sub {
- my ($storecfg, $vmid, $conf, $snapname) = @_;
-
- # Note: we try to be smart when selecting a $target storage
-
- my $target;
-
- # search shared storage first
- foreach_writable_storage($conf, sub {
- my ($sid) = @_;
- my $scfg = PVE::Storage::storage_config($storecfg, $sid);
- return if !$scfg->{shared};
-
- $target = $sid if !$target || $scfg->{path}; # prefer file based storage
- });
-
- if (!$target) {
- # now search local storage
- foreach_writable_storage($conf, sub {
- my ($sid) = @_;
- my $scfg = PVE::Storage::storage_config($storecfg, $sid);
- return if $scfg->{shared};
-
- $target = $sid if !$target || $scfg->{path}; # prefer file based storage;
- });
- }
-
- $target = 'local' if !$target;
-
- my $driver_state_size = 500; # assume 32MB is enough to safe all driver state;
- # we abort live save after $conf->{memory}, so we need at max twice that space
- my $size = $conf->{memory}*2 + $driver_state_size;
-
- my $name = "vm-$vmid-state-$snapname";
- my $scfg = PVE::Storage::storage_config($storecfg, $target);
- $name .= ".raw" if $scfg->{path}; # add filename extension for file base storage
- my $volid = PVE::Storage::vdisk_alloc($storecfg, $target, $vmid, 'raw', $name, $size*1024);
-
- return $volid;
-};
-
-my $snapshot_prepare = sub {
- my ($vmid, $snapname, $save_vmstate, $comment) = @_;
-
- my $snap;
-
- my $updatefn = sub {
-
- my $conf = load_config($vmid);
-
- die "you can't take a snapshot if it's a template\n"
- if is_template($conf);
-
- check_lock($conf);
-
- $conf->{lock} = 'snapshot';
-
- die "snapshot name '$snapname' already used\n"
- if defined($conf->{snapshots}->{$snapname});
-
- my $storecfg = PVE::Storage::config();
- die "snapshot feature is not available" if !has_feature('snapshot', $conf, $storecfg);
-
- $snap = $conf->{snapshots}->{$snapname} = {};
-
- if ($save_vmstate && check_running($vmid)) {
- $snap->{vmstate} = &$alloc_vmstate_volid($storecfg, $vmid, $conf, $snapname);
- }
-
- &$snapshot_copy_config($conf, $snap);
-
- $snap->{snapstate} = "prepare";
- $snap->{snaptime} = time();
- $snap->{description} = $comment if $comment;
-
- # always overwrite machine if we save vmstate. This makes sure we
- # can restore it later using correct machine type
- $snap->{machine} = get_current_qemu_machine($vmid) if $snap->{vmstate};
-
- update_config_nolock($vmid, $conf, 1);
- };
-
- lock_config($vmid, $updatefn);
-
- return $snap;
-};
-
-my $snapshot_commit = sub {
- my ($vmid, $snapname) = @_;
-
- my $updatefn = sub {
-
- my $conf = load_config($vmid);
-
- die "missing snapshot lock\n"
- if !($conf->{lock} && $conf->{lock} eq 'snapshot');
-
- my $has_machine_config = defined($conf->{machine});
-
- my $snap = $conf->{snapshots}->{$snapname};
-
- die "snapshot '$snapname' does not exist\n" if !defined($snap);
-
- die "wrong snapshot state\n"
- if !($snap->{snapstate} && $snap->{snapstate} eq "prepare");
-
- delete $snap->{snapstate};
- delete $conf->{lock};
-
- my $newconf = &$snapshot_apply_config($conf, $snap);
-
- delete $newconf->{machine} if !$has_machine_config;
-
- $newconf->{parent} = $snapname;
-
- update_config_nolock($vmid, $newconf, 1);
- };
-
- lock_config($vmid, $updatefn);
-};
-
-sub snapshot_rollback {
- my ($vmid, $snapname) = @_;
-
- my $prepare = 1;
-
- my $storecfg = PVE::Storage::config();
-
- my $conf = load_config($vmid);
-
- my $get_snapshot_config = sub {
-
- die "you can't rollback if vm is a template\n" if is_template($conf);
-
- my $res = $conf->{snapshots}->{$snapname};
-
- die "snapshot '$snapname' does not exist\n" if !defined($res);
-
- return $res;
- };
-
- my $snap = &$get_snapshot_config();
-
- foreach_drive($snap, sub {
- my ($ds, $drive) = @_;
-
- return if drive_is_cdrom($drive);
-
- my $volid = $drive->{file};
-
- PVE::Storage::volume_rollback_is_possible($storecfg, $volid, $snapname);
- });
-
- my $updatefn = sub {
-
- $conf = load_config($vmid);
-
- $snap = &$get_snapshot_config();
-
- die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
- if $snap->{snapstate};
-
- if ($prepare) {
- check_lock($conf);
- vm_stop($storecfg, $vmid, undef, undef, 5, undef, undef);
- }
-
- die "unable to rollback vm $vmid: vm is running\n"
- if check_running($vmid);
-
- if ($prepare) {
- $conf->{lock} = 'rollback';
- } else {
- die "got wrong lock\n" if !($conf->{lock} && $conf->{lock} eq 'rollback');
- delete $conf->{lock};
- }
-
- my $forcemachine;
-
- if (!$prepare) {
- my $has_machine_config = defined($conf->{machine});
-
- # copy snapshot config to current config
- $conf = &$snapshot_apply_config($conf, $snap);
- $conf->{parent} = $snapname;
-
- # Note: old code did not store 'machine', so we try to be smart
- # and guess the snapshot was generated with kvm 1.4 (pc-i440fx-1.4).
- $forcemachine = $conf->{machine} || 'pc-i440fx-1.4';
- # we remove the 'machine' configuration if not explicitly specified
- # in the original config.
- delete $conf->{machine} if $snap->{vmstate} && !$has_machine_config;
- }
-
- update_config_nolock($vmid, $conf, 1);
-
- if (!$prepare && $snap->{vmstate}) {
- my $statefile = PVE::Storage::path($storecfg, $snap->{vmstate});
- vm_start($storecfg, $vmid, $statefile, undef, undef, undef, $forcemachine);
- }
- };
-
- lock_config($vmid, $updatefn);
-
- foreach_drive($snap, sub {
- my ($ds, $drive) = @_;
-
- return if drive_is_cdrom($drive);
-
- my $volid = $drive->{file};
- my $device = "drive-$ds";
-
- PVE::Storage::volume_snapshot_rollback($storecfg, $volid, $snapname);
- });
-
- $prepare = 0;
- lock_config($vmid, $updatefn);
-}
-
-my $savevm_wait = sub {
- my ($vmid) = @_;
-
- for(;;) {
- my $stat = vm_mon_cmd_nocheck($vmid, "query-savevm");
- if (!$stat->{status}) {
- die "savevm not active\n";
- } elsif ($stat->{status} eq 'active') {
- sleep(1);
- next;
- } elsif ($stat->{status} eq 'completed') {
- last;
- } else {
- die "query-savevm returned status '$stat->{status}'\n";
- }
- }
-};
-
sub do_snapshots_with_qemu {
my ($storecfg, $volid) = @_;
return undef;
}
-sub snapshot_create {
- my ($vmid, $snapname, $save_vmstate, $comment) = @_;
-
- my $snap = &$snapshot_prepare($vmid, $snapname, $save_vmstate, $comment);
-
- $save_vmstate = 0 if !$snap->{vmstate}; # vm is not running
-
- my $config = load_config($vmid);
-
- my $running = check_running($vmid);
-
- my $freezefs = $running && $config->{agent};
- $freezefs = 0 if $snap->{vmstate}; # not needed if we save RAM
-
- my $drivehash = {};
-
- if ($freezefs) {
- eval { vm_mon_cmd($vmid, "guest-fsfreeze-freeze"); };
- warn "guest-fsfreeze-freeze problems - $@" if $@;
- }
-
- eval {
- # create internal snapshots of all drives
-
- my $storecfg = PVE::Storage::config();
-
- if ($running) {
- if ($snap->{vmstate}) {
- my $path = PVE::Storage::path($storecfg, $snap->{vmstate});
- vm_mon_cmd($vmid, "savevm-start", statefile => $path);
- &$savevm_wait($vmid);
- } else {
- vm_mon_cmd($vmid, "savevm-start");
- }
- };
-
- foreach_drive($snap, sub {
- my ($ds, $drive) = @_;
-
- return if drive_is_cdrom($drive);
-
- my $volid = $drive->{file};
- my $device = "drive-$ds";
-
- qemu_volume_snapshot($vmid, $device, $storecfg, $volid, $snapname);
- $drivehash->{$ds} = 1;
- });
- };
- my $err = $@;
-
- if ($running) {
- eval { vm_mon_cmd($vmid, "savevm-end") };
- warn $@ if $@;
-
- if ($freezefs) {
- eval { vm_mon_cmd($vmid, "guest-fsfreeze-thaw"); };
- warn "guest-fsfreeze-thaw problems - $@" if $@;
- }
-
- # savevm-end is async, we need to wait
- for (;;) {
- my $stat = vm_mon_cmd_nocheck($vmid, "query-savevm");
- if (!$stat->{bytes}) {
- last;
- } else {
- print "savevm not yet finished\n";
- sleep(1);
- next;
- }
- }
- }
+sub qga_check_running {
+ my ($vmid) = @_;
- if ($err) {
- warn "snapshot create failed: starting cleanup\n";
- eval { snapshot_delete($vmid, $snapname, 0, $drivehash); };
- warn $@ if $@;
- die $err;
+ eval { vm_mon_cmd($vmid, "guest-ping", timeout => 3); };
+ if ($@) {
+ warn "Qemu Guest Agent are not running - $@";
+ return 0;
}
-
- &$snapshot_commit($vmid, $snapname);
-}
-
-# Note: $drivehash is only set when called from snapshot_create.
-sub snapshot_delete {
- my ($vmid, $snapname, $force, $drivehash) = @_;
-
- my $prepare = 1;
-
- my $snap;
- my $unused = [];
-
- my $unlink_parent = sub {
- my ($confref, $new_parent) = @_;
-
- if ($confref->{parent} && $confref->{parent} eq $snapname) {
- if ($new_parent) {
- $confref->{parent} = $new_parent;
- } else {
- delete $confref->{parent};
- }
- }
- };
-
- my $updatefn = sub {
- my ($remove_drive) = @_;
-
- my $conf = load_config($vmid);
-
- if (!$drivehash) {
- check_lock($conf);
- die "you can't delete a snapshot if vm is a template\n"
- if is_template($conf);
- }
-
- $snap = $conf->{snapshots}->{$snapname};
-
- die "snapshot '$snapname' does not exist\n" if !defined($snap);
-
- # remove parent refs
- 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) {
- if ($remove_drive eq 'vmstate') {
- delete $snap->{$remove_drive};
- } else {
- my $drive = parse_drive($remove_drive, $snap->{$remove_drive});
- my $volid = $drive->{file};
- delete $snap->{$remove_drive};
- add_unused_volume($conf, $volid);
- }
- }
-
- if ($prepare) {
- $snap->{snapstate} = 'delete';
- } else {
- delete $conf->{snapshots}->{$snapname};
- delete $conf->{lock} if $drivehash;
- foreach my $volid (@$unused) {
- add_unused_volume($conf, $volid);
- }
- }
-
- update_config_nolock($vmid, $conf, 1);
- };
-
- lock_config($vmid, $updatefn);
-
- # now remove vmstate file
-
- my $storecfg = PVE::Storage::config();
-
- if ($snap->{vmstate}) {
- eval { PVE::Storage::vdisk_free($storecfg, $snap->{vmstate}); };
- if (my $err = $@) {
- die $err if !$force;
- warn $err;
- }
- # save changes (remove vmstate from snapshot)
- lock_config($vmid, $updatefn, 'vmstate') if !$force;
- };
-
- # now remove all internal snapshots
- foreach_drive($snap, sub {
- my ($ds, $drive) = @_;
-
- return if drive_is_cdrom($drive);
-
- my $volid = $drive->{file};
- my $device = "drive-$ds";
-
- if (!$drivehash || $drivehash->{$ds}) {
- eval { qemu_volume_snapshot_delete($vmid, $device, $storecfg, $volid, $snapname); };
- if (my $err = $@) {
- die $err if !$force;
- warn $err;
- }
- }
-
- # save changes (remove drive fron snapshot)
- lock_config($vmid, $updatefn, $ds) if !$force;
- push @$unused, $volid;
- });
-
- # now cleanup config
- $prepare = 0;
- lock_config($vmid, $updatefn);
-}
-
-sub has_feature {
- my ($feature, $conf, $storecfg, $snapname, $running) = @_;
-
- my $err;
- foreach_drive($conf, sub {
- my ($ds, $drive) = @_;
-
- return if drive_is_cdrom($drive);
- my $volid = $drive->{file};
- $err = 1 if !PVE::Storage::volume_has_feature($storecfg, $feature, $volid, $snapname, $running);
- });
-
- return $err ? 0 : 1;
+ return 1;
}
sub template_create {
my $voliddst = PVE::Storage::vdisk_create_base($storecfg, $volid);
$drive->{file} = $voliddst;
$conf->{$ds} = print_drive($vmid, $drive);
- update_config_nolock($vmid, $conf, 1);
+ PVE::QemuConfig->write_config($vmid, $conf);
});
}
-sub is_template {
- my ($conf) = @_;
-
- return 1 if defined $conf->{template} && $conf->{template} == 1;
-}
-
sub qemu_img_convert {
my ($src_volid, $dst_volid, $size, $snapname) = @_;
my ($dst_storeid, $dst_volname) = PVE::Storage::parse_volume_id($dst_volid, 1);
if ($src_storeid && $dst_storeid) {
+
+ PVE::Storage::activate_volumes($storecfg, [$src_volid], $snapname);
+
my $src_scfg = PVE::Storage::storage_config($storecfg, $src_storeid);
my $dst_scfg = PVE::Storage::storage_config($storecfg, $dst_storeid);
}
+sub qemu_machine_pxe {
+ my ($vmid, $conf, $machine) = @_;
+
+ $machine = PVE::QemuServer::get_current_qemu_machine($vmid) if !$machine;
+
+ foreach my $opt (keys %$conf) {
+ next if $opt !~ m/^net(\d+)$/;
+ my $net = PVE::QemuServer::parse_net($conf->{$opt});
+ next if !$net;
+ my $romfile = PVE::QemuServer::vm_mon_cmd_nocheck($vmid, 'qom-get', path => $opt, property => 'romfile');
+ return $machine.".pxe" if $romfile =~ m/pxe/;
+ last;
+ }
+
+ return $machine;
+}
+
+sub qemu_use_old_bios_files {
+ my ($machine_type) = @_;
+
+ return if !$machine_type;
+
+ my $use_old_bios_files = undef;
+
+ if ($machine_type =~ m/^(\S+)\.pxe$/) {
+ $machine_type = $1;
+ $use_old_bios_files = 1;
+ } else {
+ my $kvmver = kvm_user_version();
+ # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
+ # load new efi bios files on migration. So this hack is required to allow
+ # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
+ # updrading from proxmox-ve-3.X to proxmox-ve 4.0
+ $use_old_bios_files = !qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 4);
+ }
+
+ return ($use_old_bios_files, $machine_type);
+}
+
sub lspci {
my $devices = {};
my $res = [];
foreach my $sid (keys %$ids) {
next if !PVE::Storage::storage_check_enabled($cfg, $sid, undef, 1);
+ next if !$ids->{$sid}->{content}->{images};
push @$res, $sid;
}