my $OVMF_CODE = '/usr/share/kvm/OVMF_CODE-pure-efi.fd';
my $OVMF_VARS = '/usr/share/kvm/OVMF_VARS-pure-efi.fd';
-my $OVMF_IMG = '/usr/share/kvm/OVMF-pure-efi.fd';
my $qemu_snap_storage = {rbd => 1, sheepdog => 1};
\&parse_vm_config,
\&write_vm_config);
-PVE::JSONSchema::register_standard_option('skiplock', {
- description => "Ignore locks - only root is allowed to use this option.",
- type => 'boolean',
- optional => 1,
-});
-
PVE::JSONSchema::register_standard_option('pve-qm-stateuri', {
description => "Some command save/restore state from this location.",
type => 'string',
coreduo => 'GenuineIntel',
core2duo => 'GenuineIntel',
Conroe => 'GenuineIntel',
- Penryn => 'GenuineIntel',
+ Penryn => 'GenuineIntel',
Nehalem => 'GenuineIntel',
+ 'Nehalem-IBRS' => 'GenuineIntel',
Westmere => 'GenuineIntel',
+ 'Westmere-IBRS' => 'GenuineIntel',
SandyBridge => 'GenuineIntel',
+ 'SandyBridge-IBRS' => 'GenuineIntel',
IvyBridge => 'GenuineIntel',
+ 'IvyBridge-IBRS' => 'GenuineIntel',
Haswell => 'GenuineIntel',
+ 'Haswell-IBRS' => 'GenuineIntel',
'Haswell-noTSX' => 'GenuineIntel',
+ 'Haswell-noTSX-IBRS' => 'GenuineIntel',
Broadwell => 'GenuineIntel',
+ 'Broadwell-IBRS' => 'GenuineIntel',
'Broadwell-noTSX' => 'GenuineIntel',
-
+ 'Broadwell-noTSX-IBRS' => 'GenuineIntel',
+ 'Skylake-Client' => 'GenuineIntel',
+ 'Skylake-Client-IBRS' => 'GenuineIntel',
+ 'Skylake-Server' => 'GenuineIntel',
+ 'Skylake-Server-IBRS' => 'GenuineIntel',
+
# AMD CPUs
athlon => 'AuthenticAMD',
phenom => 'AuthenticAMD',
kvm64 => 'default',
qemu32 => 'default',
qemu64 => 'default',
+ max => 'default',
};
+my $cpu_flag = qr/[+-](pcid|spec-ctrl)/;
+
my $cpu_fmt = {
cputype => {
description => "Emulated CPU type.",
optional => 1,
default => 0
},
+ flags => {
+ description => "List of additional CPU flags separated by ';'."
+ . " Use '+FLAG' to enable, '-FLAG' to disable a flag."
+ . " Currently supported flags: 'pcid', 'spec-ctrl'.",
+ format_description => '+FLAG[;-FLAG...]',
+ type => 'string',
+ pattern => qr/$cpu_flag(;$cpu_flag)*/,
+ optional => 1,
+ },
};
my $watchdog_fmt = {
optional => 1,
type => 'integer',
description => "CPU weight for a VM.",
- verbose_description => "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs.\n\nNOTE: You can disable fair-scheduler configuration by setting this to 0.",
- minimum => 0,
- maximum => 500000,
+ verbose_description => "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs.",
+ minimum => 2,
+ maximum => 262144,
default => 1024,
},
memory => {
keyboard => {
optional => 1,
type => 'string',
- description => "Keybord layout for vnc server. Default is read from the '/etc/pve/datacenter.conf' configuration file.",
+ description => "Keybord layout for vnc server. Default is read from the '/etc/pve/datacenter.conf' configuration file.".
+ "It should not be necessary to set it.",
enum => PVE::Tools::kvmkeymaplist(),
- default => 'en-us',
+ default => undef,
},
name => {
optional => 1,
w2k8;; Microsoft Windows 2008
wvista;; Microsoft Windows Vista
win7;; Microsoft Windows 7
-win8;; Microsoft Windows 8/2012
+win8;; Microsoft Windows 8/2012/2012r2
+win10;; Microsoft Windows 10/2016
l24;; Linux 2.4 Kernel
l26;; Linux 2.6/3.X Kernel
solaris;; Solaris/OpenSolaris/OpenIndiania kernel
type => 'string', format => 'pve-volume-id',
description => "Reference to a volume which stores the VM state. This is used internally for snapshots.",
},
+ vmstatestorage => get_standard_option('pve-storage-id', {
+ description => "Default storage for VM state volumes/files.",
+ optional => 1,
+ }),
machine => {
description => "Specific the Qemu machine type.",
type => 'string',
description => "Whether the drive should be included when making backups.",
optional => 1,
},
+ replicate => {
+ type => 'boolean',
+ description => 'Whether the drive should considered for replication jobs.',
+ optional => 1,
+ default => 1,
+ },
+ rerror => {
+ type => 'string',
+ enum => [qw(ignore report stop)],
+ description => 'Read error action.',
+ optional => 1,
+ },
werror => {
type => 'string',
enum => [qw(enospc ignore report stop)],
}
);
-my %rerror_fmt = (
- rerror => {
- type => 'string',
- enum => [qw(ignore report stop)],
- description => 'Read error action.',
- optional => 1,
- },
-);
-
my %iothread_fmt = ( iothread => {
type => 'boolean',
description => "Whether to use iothreads for this drive",
}
);
+my %scsiblock_fmt = (
+ scsiblock => {
+ type => 'boolean',
+ description => "whether to use scsi-block for full passthrough of host block device\n\nWARNING: can lead to I/O errors in combination with low memory or high memory fragmentation on host",
+ optional => 1,
+ default => 0,
+ },
+);
+
my $add_throttle_desc = sub {
my ($key, $type, $what, $unit, $longunit, $minimum) = @_;
my $d = {
$add_throttle_desc->('iops_wr_max', 'integer', 'unthrottled write I/O pool', 'iops', 'operations per second');
# burst lengths
-$add_throttle_desc->('bps_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
-$add_throttle_desc->('bps_rd_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
-$add_throttle_desc->('bps_wr_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
-$add_throttle_desc->('iops_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
-$add_throttle_desc->('iops_rd_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
-$add_throttle_desc->('iops_wr_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
+$add_throttle_desc->('bps_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
+$add_throttle_desc->('bps_rd_max_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
+$add_throttle_desc->('bps_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
+$add_throttle_desc->('iops_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
+$add_throttle_desc->('iops_rd_max_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
+$add_throttle_desc->('iops_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
+
+# legacy support
+$drivedesc_base{'bps_rd_length'} = { alias => 'bps_rd_max_length' };
+$drivedesc_base{'bps_wr_length'} = { alias => 'bps_wr_max_length' };
+$drivedesc_base{'iops_rd_length'} = { alias => 'iops_rd_max_length' };
+$drivedesc_base{'iops_wr_length'} = { alias => 'iops_wr_max_length' };
my $ide_fmt = {
%drivedesc_base,
- %rerror_fmt,
%model_fmt,
};
PVE::JSONSchema::register_format("pve-qm-ide", $ide_fmt);
%drivedesc_base,
%iothread_fmt,
%queues_fmt,
+ %scsiblock_fmt,
};
my $scsidesc = {
optional => 1,
my $sata_fmt = {
%drivedesc_base,
- %rerror_fmt,
};
my $satadesc = {
optional => 1,
my $virtio_fmt = {
%drivedesc_base,
%iothread_fmt,
- %rerror_fmt,
};
my $virtiodesc = {
optional => 1,
my $alldrive_fmt = {
%drivedesc_base,
- %rerror_fmt,
%iothread_fmt,
%model_fmt,
%queues_fmt,
+ %scsiblock_fmt,
};
my $efidisk_fmt = {
optional => 1,
default => 1,
},
+ romfile => {
+ type => 'string',
+ pattern => '[^,;]+',
+ format_description => 'string',
+ description => "Custom pci device rom filename (must be located in /usr/share/kvm/).",
+ optional => 1,
+ },
pcie => {
type => 'boolean',
description => "Choose the PCI-express bus (needs the 'q35' machine model).",
# can't use the schema's 'requires' because of the mbps* => bps* "transforming aliases"
for my $requirement (
+ [mbps_max => 'mbps'],
+ [mbps_rd_max => 'mbps_rd'],
+ [mbps_wr_max => 'mbps_wr'],
+ [miops_max => 'miops'],
+ [miops_rd_max => 'miops_rd'],
+ [miops_wr_max => 'miops_wr'],
[bps_max_length => 'mbps_max'],
[bps_rd_max_length => 'mbps_rd_max'],
[bps_wr_max_length => 'mbps_wr_max'],
if ($drive->{file} =~ m|^/|) {
$path = $drive->{file};
if (my $info = path_is_scsi($path)) {
- if ($info->{type} == 0) {
+ if ($info->{type} == 0 && $drive->{scsiblock}) {
$devicetype = 'block';
} elsif ($info->{type} == 1) { # tape
$devicetype = 'generic';
}
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);
+ my @qemu_drive_options = qw(heads secs cyls trans media format cache rerror werror aio discard);
foreach my $o (@qemu_drive_options) {
$opts .= ",$o=$drive->{$o}" if $drive->{$o};
}
+
+ # snapshot only accepts on|off
+ if (defined($drive->{snapshot})) {
+ my $v = $drive->{snapshot} ? 'on' : 'off';
+ $opts .= ",snapshot=$v";
+ }
+
+ foreach my $type (['', '-total'], [_rd => '-read'], [_wr => '-write']) {
+ my ($dir, $qmpname) = @$type;
+ if (my $v = $drive->{"mbps$dir"}) {
+ $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
+ }
+ if (my $v = $drive->{"mbps${dir}_max"}) {
+ $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
+ }
+ if (my $v = $drive->{"bps${dir}_max_length"}) {
+ $opts .= ",throttling.bps$qmpname-max-length=$v";
+ }
+ if (my $v = $drive->{"iops${dir}"}) {
+ $opts .= ",throttling.iops$qmpname=$v";
+ }
+ if (my $v = $drive->{"iops${dir}_max"}) {
+ $opts .= ",throttling.iops$qmpname-max=$v";
+ }
+ if (my $v = $drive->{"iops${dir}_max_length"}) {
+ $opts .= ",throttling.iops$qmpname-max-length=$v";
+ }
+ }
+
if (my $serial = $drive->{serial}) {
$serial = URI::Escape::uri_unescape($serial);
$opts .= ",serial=$serial";
$opts .= ",format=$format" if $format && !$drive->{format};
- foreach my $o (qw(bps bps_rd bps_wr)) {
- my $v = $drive->{"m$o"};
- $opts .= ",$o=" . int($v*1024*1024) if $v;
- }
-
my $cache_direct = 0;
if (my $cache = $drive->{cache}) {
sub print_cpu_device {
my ($conf, $id) = @_;
- my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0;
- my $cpu = $nokvm ? "qemu64" : "kvm64";
+ my $kvm = $conf->{kvm} // 1;
+ my $cpu = $kvm ? "kvm64" : "qemu64";
if (my $cputype = $conf->{cpu}) {
my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
or die "Cannot parse cpu description: $cputype\n";
$cpu = $cpuconf->{cputype};
}
- my $sockets = 1;
- $sockets = $conf->{sockets} if $conf->{sockets};
my $cores = $conf->{cores} || 1;
my $current_core = ($id - 1) % $cores;
- my $current_socket = int(($id - $current_core)/$cores);
+ my $current_socket = int(($id - 1 - $current_core)/$cores);
return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0";
}
PVE::QemuConfig->check_lock($conf) if !$skiplock;
+ if ($conf->{template}) {
+ # check if any base image is still used by a linked clone
+ foreach_drive($conf, sub {
+ my ($ds, $drive) = @_;
+
+ return if drive_is_cdrom($drive);
+
+ my $volid = $drive->{file};
+
+ return if !$volid || $volid =~ m|^/|;
+
+ die "base volume '$volid' is still in use by linked cloned\n"
+ if PVE::Storage::volume_is_base_and_used($storecfg, $volid);
+
+ });
+ }
+
# only remove disks owned by this VM
foreach_drive($conf, sub {
my ($ds, $drive) = @_;
my $storecfg = PVE::Storage::config();
my $list = vzlist();
+ my $defaults = load_defaults();
+
my ($uptime) = PVE::ProcFSTools::read_proc_uptime(1);
my $cpucount = $cpuinfo->{cpus} || 1;
$d->{maxdisk} = 0;
}
- $d->{cpus} = ($conf->{sockets} || 1) * ($conf->{cores} || 1);
+ $d->{cpus} = ($conf->{sockets} || $defaults->{sockets})
+ * ($conf->{cores} || $defaults->{cores});
$d->{cpus} = $cpucount if $d->{cpus} > $cpucount;
$d->{cpus} = $conf->{vcpus} if $conf->{vcpus};
$d->{name} = $conf->{name} || "VM $vmid";
- $d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024) : 0;
+ $d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024)
+ : $defaults->{memory}*(1024*1024);
if ($conf->{balloon}) {
$d->{balloon_min} = $conf->{balloon}*(1024*1024);
- $d->{shares} = defined($conf->{shares}) ? $conf->{shares} : 1000;
+ $d->{shares} = defined($conf->{shares}) ? $conf->{shares}
+ : $defaults->{shares};
}
$d->{uptime} = 0;
$d->{template} = PVE::QemuConfig->is_template($conf);
+ $d->{serial} = 1 if conf_has_serial($conf);
+
$res->{$vmid} = $d;
}
my $volhash = {};
my $test_volid = sub {
- my ($volid, $is_cdrom) = @_;
+ my ($volid, $is_cdrom, $replicate, $snapname) = @_;
return if !$volid;
- $volhash->{$volid} = $is_cdrom || 0;
+ $volhash->{$volid}->{cdrom} //= 1;
+ $volhash->{$volid}->{cdrom} = 0 if !$is_cdrom;
+
+ $volhash->{$volid}->{replicate} //= 0;
+ $volhash->{$volid}->{replicate} = 1 if $replicate;
+
+ $volhash->{$volid}->{referenced_in_config} //= 0;
+ $volhash->{$volid}->{referenced_in_config} = 1 if !defined($snapname);
+
+ $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
+ if defined($snapname);
};
foreach_drive($conf, sub {
my ($ds, $drive) = @_;
- &$test_volid($drive->{file}, drive_is_cdrom($drive));
+ $test_volid->($drive->{file}, drive_is_cdrom($drive), $drive->{replicate} // 1, undef);
});
foreach my $snapname (keys %{$conf->{snapshots}}) {
my $snap = $conf->{snapshots}->{$snapname};
- &$test_volid($snap->{vmstate}, 0);
+ $test_volid->($snap->{vmstate}, 0, 1, $snapname);
foreach_drive($snap, sub {
my ($ds, $drive) = @_;
- &$test_volid($drive->{file}, drive_is_cdrom($drive));
+ $test_volid->($drive->{file}, drive_is_cdrom($drive), $drive->{replicate} // 1, $snapname);
});
}
}
}
+sub conf_has_serial {
+ my ($conf) = @_;
+
+ for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
+ if ($conf->{"serial$i"}) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
sub vga_conf_has_spice {
my ($vga) = @_;
my $vernum = 0; # unknown
my $ostype = $conf->{ostype};
my $winversion = windows_version($ostype);
+ my $kvm = $conf->{kvm} // 1;
+
+ die "KVM virtualisation configured, but not available. Either disable in VM configuration or enable in BIOS.\n" if (!$cpuinfo->{hvm} && $kvm);
if ($kvmver =~ m/^(\d+)\.(\d+)$/) {
$vernum = $1*1000000+$2*1000;
}
if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
- my $ovmfbase;
-
- # prefer the OVMF_CODE variant
- if (-f $OVMF_CODE) {
- $ovmfbase = $OVMF_CODE;
- } elsif (-f $OVMF_IMG) {
- $ovmfbase = $OVMF_IMG;
- }
-
- die "no uefi base img found\n" if !$ovmfbase;
- push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$ovmfbase";
+ die "uefi base image not found\n" if ! -f $OVMF_CODE;
- if (defined($conf->{efidisk0}) && ($ovmfbase eq $OVMF_CODE)) {
- my $d = PVE::JSONSchema::parse_property_string($efidisk_fmt, $conf->{efidisk0});
- my $format = $d->{format} // 'raw';
- my $path;
+ my $path;
+ my $format;
+ if (my $efidisk = $conf->{efidisk0}) {
+ my $d = PVE::JSONSchema::parse_property_string($efidisk_fmt, $efidisk);
my ($storeid, $volname) = PVE::Storage::parse_volume_id($d->{file}, 1);
+ $format = $d->{format};
if ($storeid) {
$path = PVE::Storage::path($storecfg, $d->{file});
- my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
- $format = qemu_img_format($scfg, $volname);
+ if (!defined($format)) {
+ my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+ $format = qemu_img_format($scfg, $volname);
+ }
} else {
$path = $d->{file};
- $format = "raw";
+ die "efidisk format must be specified\n"
+ if !defined($format);
}
- push @$cmd, '-drive', "if=pflash,unit=1,id=drive-efidisk0,format=$format,file=$path";
- } elsif ($ovmfbase eq $OVMF_CODE) {
- warn "using uefi without permanent efivars disk\n";
- my $ovmfvar_dst = "/tmp/$vmid-ovmf.fd";
- PVE::Tools::file_copy($OVMF_VARS, $ovmfvar_dst, 256*1024);
- push @$cmd, '-drive', "if=pflash,unit=1,format=raw,file=$ovmfvar_dst";
} else {
- # if the base img is not OVMF_CODE, we do not have to bother
- # to create/use a vars image, since it will not be used anyway
- # this can only happen if someone manually deletes the OVMF_CODE image
- # or has an old pve-qemu-kvm version installed.
- # both should not happen, but we ignore it here
+ warn "no efidisk configured! Using temporary efivars disk.\n";
+ $path = "/tmp/$vmid-ovmf.fd";
+ PVE::Tools::file_copy($OVMF_VARS, $path, -s $OVMF_VARS);
+ $format = 'raw';
}
+
+ push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$OVMF_CODE";
+ push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0,file=$path";
}
$vga = 'qxl' if $qxlnum;
if (!$vga) {
- $vga = $winversion >= 6 ? 'std' : 'cirrus';
+ if (qemu_machine_feature_enabled($machine_type, $kvmver, 2, 9)) {
+ $vga = (!$winversion || $winversion >= 6) ? 'std' : 'cirrus';
+ } else {
+ $vga = ($winversion >= 6) ? 'std' : 'cirrus';
+ }
}
# enable absolute mouse coordinates (needed by vnc)
}
my $rombar = defined($d->{rombar}) && !$d->{rombar} ? ',rombar=0' : '';
+ my $romfile = $d->{romfile};
+
my $xvga = '';
if ($d->{'x-vga'}) {
$xvga = ',x-vga=on';
if($j == 0){
$devicestr .= "$rombar$xvga";
$devicestr .= ",multifunction=on" if $multifunction;
+ $devicestr .= ",romfile=/usr/share/kvm/$romfile" if $romfile;
}
push @$devices, '-device', $devicestr;
# 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 ($winversion >= 5) { # windows
push @$rtcFlags, 'driftfix=slew' if $tdf;
- if ($nokvm) {
+ if (!$kvm) {
push @$machineFlags, 'accel=tcg';
- } else {
- die "No accelerator found!\n" if !$cpuinfo->{hvm};
}
if ($machine_type) {
push @$rtcFlags, 'base=localtime';
}
- my $cpu = $nokvm ? "qemu64" : "kvm64";
+ my $cpu = $kvm ? "kvm64" : "qemu64";
if (my $cputype = $conf->{cpu}) {
my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype)
or die "Cannot parse cpu description: $cputype\n";
$cpu = $cpuconf->{cputype};
$kvm_off = 1 if $cpuconf->{hidden};
+
+ if (defined(my $flags = $cpuconf->{flags})) {
+ push @$cpuFlags, split(";", $flags);
+ }
}
push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64';
if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) {
- push @$cpuFlags , '+kvm_pv_unhalt' if !$nokvm;
- push @$cpuFlags , '+kvm_pv_eoi' if !$nokvm;
+ push @$cpuFlags , '+kvm_pv_unhalt' if $kvm;
+ push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
}
- add_hyperv_enlighments($cpuFlags, $winversion, $machine_type, $kvmver, $nokvm, $conf->{bios}, $gpu_passthrough);
+ add_hyperv_enlightenments($cpuFlags, $winversion, $machine_type, $kvmver, $conf->{bios}, $gpu_passthrough) if $kvm;
- push @$cpuFlags, 'enforce' if $cpu ne 'host' && !$nokvm;
+ push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm;
push @$cpuFlags, 'kvm=off' if $kvm_off;
my $nodename = PVE::INotify::nodename();
my $pfamily = PVE::Tools::get_host_address_family($nodename);
- $spice_port = PVE::Tools::next_spice_port($pfamily);
+ my @nodeaddrs = PVE::Tools::getaddrinfo_all('localhost', family => $pfamily);
+ die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
+ my $localhost = PVE::Network::addr_to_ip($nodeaddrs[0]->{addr});
+ $spice_port = PVE::Tools::next_spice_port($pfamily, $localhost);
- push @$devices, '-spice', "tls-port=${spice_port},addr=localhost,tls-ciphers=DES-CBC3-SHA,seamless-migration=on";
+ push @$devices, '-spice', "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
push @$vollist, $drive->{file};
}
+ # ignore efidisk here, already added in bios/fw handling code above
+ return if $drive->{interface} eq 'efidisk';
+
$use_virtio = 1 if $ds =~ m/^virtio/;
if (drive_is_cdrom ($drive)) {
$ahcicontroller->{$controller}=1;
}
- if ($drive->{interface} eq 'efidisk') {
- # this will be added somewhere else
- return;
- }
-
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);
my $running = check_running($vmid);
- return if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
+ $size = 0 if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
return if !$running;
'startup' => 1,
'description' => 1,
'protection' => 1,
+ 'vmstatestorage' => 1,
};
# hotplug changes in [PENDING]
my $local_volumes = {};
- if( $targetstorage ){
-
+ if ($targetstorage) {
foreach_drive($conf, sub {
my ($ds, $drive) = @_;
my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
: $defaults->{cpuunits};
- my %run_params = (timeout => $statefile ? undef : 30, umask => 0077);
+ my $start_timeout = $conf->{hugepages} ? 300 : 30;
+ my %run_params = (timeout => $statefile ? undef : $start_timeout, umask => 0077);
my %properties = (
Slice => 'qemu.slice',
print "migration listens on $migrate_uri\n" if $migrate_uri;
- if ($statefile && $statefile ne 'tcp') {
+ if ($statefile && $statefile ne 'tcp' && $statefile ne 'unix') {
eval { vm_mon_cmd_nocheck($vmid, "cont"); };
warn $@ if $@;
}
#start nbd server for storage migration
if ($targetstorage) {
-
my $nodename = PVE::INotify::nodename();
my $migrate_network_addr = PVE::Cluster::get_local_migration_ip($migration_network);
my $localip = $migrate_network_addr ? $migrate_network_addr : PVE::Cluster::remote_node_ip($nodename, 1);
}
if ($migratedfrom) {
-
eval {
set_migration_caps($vmid);
};
}
} else {
-
if (!$statefile && (!defined($conf->{balloon}) || $conf->{balloon})) {
vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024)
if $conf->{balloon};
my $vollist = [];
foreach_volid($conf, sub {
- my ($volid, $is_cdrom) = @_;
+ my ($volid, $attr) = @_;
return if $volid =~ m|^/|;
} else {
print $outfd $line;
}
+ } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
+ my ($uuid, $uuid_str);
+ UUID::generate($uuid);
+ UUID::unparse($uuid, $uuid_str);
+ my $smbios1 = parse_smbios1($2);
+ $smbios1->{uuid} = $uuid_str;
+ print $outfd $1.print_smbios1($smbios1)."\n";
} else {
print $outfd $line;
}
my $changes;
- my $used = {};
+ # used and unused disks
+ my $referenced = {};
# Note: it is allowed to define multiple storages with same path (alias), so
# we need to check both 'volid' and real 'path' (two different volid can point
# to the same path).
- my $usedpath = {};
+ my $referencedpath = {};
# update size info
foreach my $opt (keys %$conf) {
my $volid = $drive->{file};
next if !$volid;
- $used->{$volid} = 1;
+ $referenced->{$volid} = 1;
if ($volid_hash->{$volid} &&
(my $path = $volid_hash->{$volid}->{path})) {
- $usedpath->{$path} = 1;
+ $referencedpath->{$path} = 1;
}
next if drive_is_cdrom($drive);
next if $opt !~ m/^unused\d+$/;
my $volid = $conf->{$opt};
my $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid};
- if ($used->{$volid} || ($path && $usedpath->{$path})) {
+ if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
$changes = 1;
delete $conf->{$opt};
}
+
+ $referenced->{$volid} = 1;
+ $referencedpath->{$path} = 1 if $path;
}
foreach my $volid (sort keys %$volid_hash) {
next if $volid =~ m/vm-$vmid-state-/;
- next if $used->{$volid};
+ next if $referenced->{$volid};
my $path = $volid_hash->{$volid}->{path};
next if !$path; # just to be sure
- next if $usedpath->{$path};
+ next if $referencedpath->{$path};
$changes = 1;
PVE::QemuConfig->add_unused_volume($conf, $volid);
- $usedpath->{$path} = 1; # avoid to add more than once (aliases)
+ $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
}
return $changes;
rmtree $tmpdir;
# disable interrupts (always do cleanups)
- local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
- warn "got interrupt - ignored\n";
- };
+ local $SIG{INT} =
+ local $SIG{TERM} =
+ local $SIG{QUIT} =
+ local $SIG{HUP} = sub { warn "got interrupt - ignored\n"; };
my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
POSIX::mkfifo($mapfifo, 0600);
eval {
# enable interrupts
- local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
- die "interrupted by signal\n";
- };
+ local $SIG{INT} =
+ local $SIG{TERM} =
+ local $SIG{QUIT} =
+ local $SIG{HUP} =
+ local $SIG{PIPE} = sub { die "interrupted by signal\n"; };
local $SIG{ALRM} = sub { die "got timeout\n"; };
$oldtimeout = alarm($timeout);
my $tmpfn = "$conffile.$$.tmp";
# disable interrupts (always do cleanups)
- local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub {
- print STDERR "got interrupt - ignored\n";
- };
+ local $SIG{INT} =
+ local $SIG{TERM} =
+ local $SIG{QUIT} =
+ local $SIG{HUP} = sub { print STDERR "got interrupt - ignored\n"; };
eval {
# enable interrupts
- local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub {
- die "interrupted by signal\n";
- };
+ local $SIG{INT} =
+ local $SIG{TERM} =
+ local $SIG{QUIT} =
+ local $SIG{HUP} =
+ local $SIG{PIPE} = sub { die "interrupted by signal\n"; };
if ($archive eq '-') {
print "extracting archive from STDIN\n";
warn $@ if $@;
};
-sub foreach_writable_storage {
+sub foreach_storage_used_by_vm {
my ($conf, $func) = @_;
my $sidhash = {};
- foreach my $ds (keys %$conf) {
- next if !is_valid_drivename($ds);
-
- my $drive = parse_drive($ds, $conf->{$ds});
- next if !$drive;
- next if drive_is_cdrom($drive);
+ foreach_drive($conf, sub {
+ my ($ds, $drive) = @_;
+ return if drive_is_cdrom($drive);
my $volid = $drive->{file};
my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
$sidhash->{$sid} = $sid if $sid;
- }
+ });
foreach my $sid (sort keys %$sidhash) {
&$func($sid);
eval { vm_mon_cmd($vmid, "guest-ping", timeout => 3); };
if ($@) {
- warn "Qemu Guest Agent are not running - $@";
+ warn "Qemu Guest Agent is not running - $@";
return 0;
}
return 1;
my $format;
$jobs->{"drive-$drive"} = {};
- if($dst_volid =~ /^nbd:(localhost|[\d\.]+|\[[\d\.:a-fA-F]+\]):(\d+):exportname=(\S+)/) {
+ if ($dst_volid =~ /^nbd:(localhost|[\d\.]+|\[[\d\.:a-fA-F]+\]):(\d+):exportname=(\S+)/) {
my $server = $1;
my $port = $2;
my $exportname = $3;
$format = "nbd";
my $unixsocket = "/run/qemu-server/$vmid.mirror-drive-$drive";
$qemu_target = "nbd+unix:///$exportname?socket=$unixsocket";
- my $cmd = ['socat', "UNIX-LISTEN:$unixsocket,fork", "TCP:$server:$2,connect-timeout=1"];
+ my $cmd = ['socat', '-T30', "UNIX-LISTEN:$unixsocket,fork", "TCP:$server:$2,connect-timeout=5"];
my $pid = fork();
if (!defined($pid)) {
- die "forking socat tunnel failed";
+ die "forking socat tunnel failed\n";
} elsif ($pid == 0) {
exec(@$cmd);
- exit(-1);
- } else {
-
- $jobs->{"drive-$drive"}->{pid} = $pid;
+ warn "exec failed: $!\n";
+ POSIX::_exit(-1);
+ }
+ $jobs->{"drive-$drive"}->{pid} = $pid;
- my $timeout = 0;
- while (1) {
- last if -S $unixsocket;
- die if $timeout > 5;
- $timeout++;
- sleep 1;
- }
+ my $timeout = 0;
+ while (!-S $unixsocket) {
+ die "nbd connection helper timed out\n"
+ if $timeout++ > 5;
+ sleep 1;
}
} else {
-
my $storecfg = PVE::Storage::config();
my ($dst_storeid, $dst_volname) = PVE::Storage::parse_volume_id($dst_volid);
die "mirroring error: $err";
}
-
qemu_drive_mirror_monitor ($vmid, $vmiddst, $jobs, $skipcomplete, $qga);
}
my ($vmid, $vmiddst, $jobs, $skipcomplete, $qga) = @_;
eval {
-
my $err_complete = 0;
while (1) {
next;
}
- die "$job: mirroring has been cancelled. Maybe do you have bad sectors?" if !defined($running_mirror_jobs->{$job});
+ die "$job: mirroring has been cancelled\n" if !defined($running_mirror_jobs->{$job});
my $busy = $running_mirror_jobs->{$job}->{busy};
my $ready = $running_mirror_jobs->{$job}->{ready};
print "$job: transferred: $transferred bytes remaining: $remaining bytes total: $total bytes progression: $percent % busy: $busy ready: $ready \n";
}
- $readycounter++ if $running_mirror_jobs->{$job}->{ready} eq 'true';
+ $readycounter++ if $running_mirror_jobs->{$job}->{ready};
}
last if scalar(keys %$jobs) == 0;
last if $skipcomplete; #do the complete later
if ($vmiddst && $vmiddst != $vmid) {
- if ($qga) {
+ my $agent_running = $qga && qga_check_running($vmid);
+ if ($agent_running) {
print "freeze filesystem\n";
eval { PVE::QemuServer::vm_mon_cmd($vmid, "guest-fsfreeze-freeze"); };
} else {
# if we clone a disk for a new target vm, we don't switch the disk
PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs);
- if ($qga) {
+ if ($agent_running) {
print "unfreeze filesystem\n";
eval { PVE::QemuServer::vm_mon_cmd($vmid, "guest-fsfreeze-thaw"); };
} else {
foreach my $job (keys %$jobs) {
# try to switch the disk if source and destination are on the same guest
- print "$job : Try to complete block job\n";
+ print "$job: Completing block job...\n";
eval { vm_mon_cmd($vmid, "block-job-complete", device => $job) };
if ($@ =~ m/cannot be completed/) {
- print "$job : block job cannot be complete. Try again \n";
+ print "$job: Block job cannot be completed, try again.\n";
$err_complete++;
}else {
- print "$job : complete ok : flushing pending writes\n";
+ print "$job: Completed successfully.\n";
$jobs->{$job}->{complete} = 1;
eval { qemu_blockjobs_finish_tunnel($vmid, $job, $jobs->{$job}->{pid}) } ;
}
my ($vmid, $jobs) = @_;
foreach my $job (keys %$jobs) {
- print "$job: try to cancel block job\n";
+ print "$job: Cancelling block job\n";
eval { vm_mon_cmd($vmid, "block-job-cancel", device => $job); };
$jobs->{$job}->{cancel} = 1;
}
foreach my $job (keys %$jobs) {
- if(defined($jobs->{$job}->{cancel}) && !defined($running_jobs->{$job})) {
- print "$job : finished\n";
+ if (defined($jobs->{$job}->{cancel}) && !defined($running_jobs->{$job})) {
+ print "$job: Done.\n";
eval { qemu_blockjobs_finish_tunnel($vmid, $job, $jobs->{$job}->{pid}) } ;
delete $jobs->{$job};
}
my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
$storeid = $storage if $storage;
- my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
- if (!$format) {
- my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
- $format = qemu_img_format($scfg, $volname);
- }
-
- # test if requested format is supported - else use default
- my $supported = grep { $_ eq $format } @$validFormats;
- $format = $defFormat if !$supported;
-
+ my $dst_format = resolve_dst_disk_format($storecfg, $storeid, $volname, $format);
my ($size) = PVE::Storage::volume_size_info($storecfg, $drive->{file}, 3);
print "create full clone of drive $drivename ($drive->{file})\n";
- $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $format, undef, ($size/1024));
+ $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $dst_format, undef, ($size/1024));
push @$newvollist, $newvolid;
PVE::Storage::activate_volumes($storecfg, [$newvolid]);
return ($use_old_bios_files, $machine_type);
}
+sub create_efidisk {
+ my ($storecfg, $storeid, $vmid, $fmt) = @_;
+
+ die "EFI vars default image not found\n" if ! -f $OVMF_VARS;
+
+ my $vars_size = PVE::Tools::convert_size(-s $OVMF_VARS, 'b' => 'kb');
+ my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
+ PVE::Storage::activate_volumes($storecfg, [$volid]);
+
+ my $path = PVE::Storage::path($storecfg, $volid);
+ eval {
+ run_command(['/usr/bin/qemu-img', 'convert', '-n', '-f', 'raw', '-O', $fmt, $OVMF_VARS, $path]);
+ };
+ die "Copying EFI vars image failed: $@" if $@;
+
+ return ($volid, $vars_size);
+}
+
sub lspci {
my $devices = {};
push @{$devices->{$id}}, $res;
});
+ # Entries should be sorted by functions.
+ foreach my $id (keys %$devices) {
+ my $dev = $devices->{$id};
+ $devices->{$id} = [ sort { $a->{function} <=> $b->{function} } @$dev ];
+ }
+
return $devices;
}
my $maxdev = 0;
- if ($conf->{scsihw} && ($conf->{scsihw} =~ m/^lsi/)) {
+ if (!$conf->{scsihw} || ($conf->{scsihw} =~ m/^lsi/)) {
$maxdev = 7;
} elsif ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
$maxdev = 1;
return ($maxdev, $controller, $controller_prefix);
}
-sub add_hyperv_enlighments {
- my ($cpuFlags, $winversion, $machine_type, $kvmver, $nokvm, $bios, $gpu_passthrough) = @_;
+sub add_hyperv_enlightenments {
+ my ($cpuFlags, $winversion, $machine_type, $kvmver, $bios, $gpu_passthrough) = @_;
- return if $nokvm;
return if $winversion < 6;
return if $bios && $bios eq 'ovmf' && $winversion < 8;
return $winversion;
}
+sub resolve_dst_disk_format {
+ my ($storecfg, $storeid, $src_volname, $format) = @_;
+ my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
+
+ if (!$format) {
+ # if no target format is specified, use the source disk format as hint
+ if ($src_volname) {
+ my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
+ $format = qemu_img_format($scfg, $src_volname);
+ } else {
+ return $defFormat;
+ }
+ }
+
+ # test if requested format is supported - else use default
+ my $supported = grep { $_ eq $format } @$validFormats;
+ $format = $defFormat if !$supported;
+ return $format;
+}
+
+sub resolve_first_disk {
+ my $conf = shift;
+ my @disks = PVE::QemuServer::valid_drive_names();
+ my $firstdisk;
+ foreach my $ds (reverse @disks) {
+ next if !$conf->{$ds};
+ my $disk = PVE::QemuServer::parse_drive($ds, $conf->{$ds});
+ next if PVE::QemuServer::drive_is_cdrom($disk);
+ $firstdisk = $ds;
+ }
+ return $firstdisk;
+}
+
+sub generate_smbios1_uuid {
+ my ($uuid, $uuid_str);
+ UUID::generate($uuid);
+ UUID::unparse($uuid, $uuid_str);
+ return "uuid=$uuid_str";
+}
+
# bash completion helper
sub complete_backup_archives {