use strict;
use warnings;
+
use POSIX;
use IO::Handle;
use IO::Select;
],
};
-my $qemu_snap_storage = {rbd => 1, sheepdog => 1};
+my $qemu_snap_storage = { rbd => 1 };
my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
optional => 1,
});
-PVE::JSONSchema::register_standard_option('pve-snapshot-name', {
- description => "The name of the snapshot.",
- type => 'string', format => 'pve-configid',
- maxLength => 40,
-});
-
PVE::JSONSchema::register_standard_option('pve-qm-image-format', {
type => 'string',
enum => [qw(raw cow qcow qed qcow2 vmdk cloop)],
pattern => qr/$PCIRE(;$PCIRE)*/,
format_description => 'HOSTPCIID[;HOSTPCIID2...]',
description => <<EODESCR,
-Host PCI device pass through. The PCI ID of a host's PCI device or a list
+Host PCI device pass through. The PCI ID of a host's PCI device or a list
of PCI virtual functions of the host. HOSTPCIID syntax is:
'bus:dev.func' (hexadecimal numbers)
verbose_description => <<EODESCR,
Map host PCI devices into guest.
-NOTE: This option allows direct access to host hardware. So it is no longer
+NOTE: This option allows direct access to host hardware. So it is no longer
possible to migrate such machines - use with special care.
CAUTION: Experimental! User reported problems with this option.
my $path;
my $volid = $drive->{file};
my $format;
-
+
if (drive_is_cdrom($drive)) {
$path = get_iso_path($storecfg, $vmid, $volid);
} else {
sub check_local_resources {
my ($conf, $noerr) = @_;
- my $loc_res = 0;
+ my @loc_res = ();
- $loc_res = 1 if $conf->{hostusb}; # old syntax
- $loc_res = 1 if $conf->{hostpci}; # old syntax
+ push @loc_res, "hostusb" if $conf->{hostusb}; # old syntax
+ push @loc_res, "hostpci" if $conf->{hostpci}; # old syntax
- $loc_res = 1 if $conf->{ivshmem};
+ push @loc_res, "ivshmem" if $conf->{ivshmem};
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+$/;
+ push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
}
- die "VM uses local resources\n" if $loc_res && !$noerr;
+ die "VM uses local resources\n" if scalar @loc_res && !$noerr;
- return $loc_res;
+ return \@loc_res;
}
# check if used storages are available on all nodes (use by migrate)
type => 'number',
optional => 1,
},
+ lock => {
+ description => "The current config lock, if any.",
+ type => 'string',
+ optional => 1,
+ }
};
my $last_proc_pid_stat;
$d->{template} = PVE::QemuConfig->is_template($conf);
$d->{serial} = 1 if conf_has_serial($conf);
+ $d->{lock} = $conf->{lock} if $conf->{lock};
$res->{$vmid} = $d;
}
push @$cmd, get_cpu_options($conf, $arch, $kvm, $machine_type, $kvm_off, $kvmver, $winversion, $gpu_passthrough);
PVE::QemuServer::Memory::config($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
-
+
push @$cmd, '-S' if $conf->{freeze};
push @$cmd, '-k', $conf->{keyboard} if defined($conf->{keyboard});
my $queues = '';
if($conf->{scsihw} && $conf->{scsihw} eq "virtio-scsi-single" && $drive->{queues}){
$queues = ",num_queues=$drive->{queues}";
- }
+ }
push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues" if !$scsicontroller->{$controller};
$scsicontroller->{$controller}=1;
if ($is_suspended) {
# enforce machine type on suspended vm to ensure HW compatibility
$forcemachine = $conf->{runningmachine};
+ print "Resuming suspended VM\n";
}
my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
: $defaults->{cpuunits};
- my $start_timeout = $conf->{hugepages} ? 300 : 30;
+ my $start_timeout = ($conf->{hugepages} || $is_suspended) ? 300 : 30;
my %run_params = (timeout => $statefile ? undef : $start_timeout, umask => 0077);
my %properties = (
value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
if ($is_suspended && (my $vmstate = $conf->{vmstate})) {
+ print "Resumed VM, removing state\n";
delete $conf->@{qw(lock vmstate runningmachine)};
PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
PVE::Storage::vdisk_free($storecfg, $vmstate);
sleep(1);
next;
} elsif ($state->{status} eq 'completed') {
+ print "State saved, quitting\n";
last;
} elsif ($state->{status} eq 'failed' && $state->{error}) {
die "query-savevm failed with error '$state->{error}'\n"
my ($vmid, $skiplock, $nocheck) = @_;
PVE::QemuConfig->lock_config($vmid, sub {
-
- my $res = vm_mon_cmd($vmid, 'query-status');
+ my $vm_mon_cmd = $nocheck ? \&vm_mon_cmd_nocheck : \&vm_mon_cmd;
+ my $res = $vm_mon_cmd->($vmid, 'query-status');
my $resume_cmd = 'cont';
if ($res->{status} && $res->{status} eq 'suspended') {
PVE::QemuConfig->check_lock($conf)
if !($skiplock || PVE::QemuConfig->has_lock($conf, 'backup'));
-
- vm_mon_cmd($vmid, $resume_cmd);
-
- } else {
- vm_mon_cmd_nocheck($vmid, $resume_cmd);
}
+
+ $vm_mon_cmd->($vmid, $resume_cmd);
});
}
return if $line =~ m/^lock:/;
return if $line =~ m/^unused\d+:/;
return if $line =~ m/^parent:/;
- return if $line =~ m/^template:/; # restored VM is never a template
my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
$storage_limits{$storeid} = $bwlimit;
$virtdev_hash->{$virtdev} = $devinfo->{$devname};
+ } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
+ my $virtdev = $1;
+ my $drive = parse_drive($virtdev, $2);
+ if (drive_is_cloudinit($drive)) {
+ my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
+ my $scfg = PVE::Storage::storage_config($cfg, $storeid);
+ my $format = qemu_img_format($scfg, $volname); # has 'raw' fallback
+
+ my $d = {
+ format => $format,
+ storeid => $opts->{storage} // $storeid,
+ size => PVE::QemuServer::Cloudinit::CLOUDINIT_DISK_SIZE,
+ file => $drive->{file}, # to make drive_is_cloudinit check possible
+ name => "vm-$vmid-cloudinit",
+ is_cloudinit => 1,
+ };
+ $virtdev_hash->{$virtdev} = $d;
+ }
}
}
foreach_drive($oldconf, sub {
my ($ds, $drive) = @_;
- return if drive_is_cdrom($drive);
+ return if !$drive->{is_cloudinit} && drive_is_cdrom($drive);
my $volid = $drive->{file};
-
return if !$volid || $volid =~ m|^/|;
my ($path, $owner) = PVE::Storage::path($cfg, $volid);
}
});
- # delete vmstate files
- # since after the restore we have no snapshots anymore
+ # delete vmstate files, after the restore we have no snapshots anymore
foreach my $snapname (keys %{$oldconf->{snapshots}}) {
my $snap = $oldconf->{snapshots}->{$snapname};
if ($snap->{vmstate}) {
my $supported = grep { $_ eq $d->{format} } @$validFormats;
$d->{format} = $defFormat if !$supported;
- my $volid = PVE::Storage::vdisk_alloc($cfg, $storeid, $vmid,
- $d->{format}, undef, $alloc_size);
+ my $name;
+ if ($d->{is_cloudinit}) {
+ $name = $d->{name};
+ $name .= ".$d->{format}" if $d->{format} ne 'raw';
+ }
+
+ my $volid = PVE::Storage::vdisk_alloc($cfg, $storeid, $vmid, $d->{format}, $name, $alloc_size);
print STDERR "new volume ID is '$volid'\n";
$d->{volid} = $volid;
- my $path = PVE::Storage::path($cfg, $volid);
- PVE::Storage::activate_volumes($cfg,[$volid]);
+ PVE::Storage::activate_volumes($cfg, [$volid]);
my $write_zeros = 1;
if (PVE::Storage::volume_has_feature($cfg, 'sparseinit', $volid)) {
$write_zeros = 0;
}
- print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
+ if (!$d->{is_cloudinit}) {
+ my $path = PVE::Storage::path($cfg, $volid);
- print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
+ print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
+
+ print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
+ }
$map->{$virtdev} = $volid;
}
my ($storecfg, $volid) = @_;
my $storage_name = PVE::Storage::parse_volume_id($volid);
+ my $scfg = $storecfg->{ids}->{$storage_name};
- if ($qemu_snap_storage->{$storecfg->{ids}->{$storage_name}->{type}}
- && !$storecfg->{ids}->{$storage_name}->{krbd}){
+ if ($qemu_snap_storage->{$scfg->{type}} && !$scfg->{krbd}){
return 1;
}
}
sub qemu_drive_mirror {
- my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $skipcomplete, $qga) = @_;
+ my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $skipcomplete, $qga, $bwlimit) = @_;
$jobs = {} if !$jobs;
my $opts = { timeout => 10, device => "drive-$drive", mode => "existing", sync => "full", target => $qemu_target };
$opts->{format} = $format if $format;
- print "drive mirror is starting for drive-$drive\n";
-
- eval { vm_mon_cmd($vmid, "drive-mirror", %$opts); }; #if a job already run for this device,it's throw an error
+ if (defined($bwlimit)) {
+ $opts->{speed} = $bwlimit * 1024;
+ print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
+ } else {
+ print "drive mirror is starting for drive-$drive\n";
+ }
+ # if a job already runs for this device we get an error, catch it for cleanup
+ eval { vm_mon_cmd($vmid, "drive-mirror", %$opts); };
if (my $err = $@) {
eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
- die "mirroring error: $err";
+ warn "$@\n" if $@;
+ die "mirroring error: $err\n";
}
qemu_drive_mirror_monitor ($vmid, $vmiddst, $jobs, $skipcomplete, $qga);
sub clone_disk {
my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
- $newvmid, $storage, $format, $full, $newvollist, $jobs, $skipcomplete, $qga) = @_;
+ $newvmid, $storage, $format, $full, $newvollist, $jobs, $skipcomplete, $qga, $bwlimit) = @_;
my $newvolid;
if (drive_is_cloudinit($drive)) {
$name = "vm-$newvmid-cloudinit";
$snapname = undef;
- # cloudinit only supports raw and qcow2 atm:
- if ($dst_format eq 'qcow2') {
- $name .= '.qcow2';
- } elsif ($dst_format ne 'raw') {
- die "clone: unhandled format for cloudinit image\n";
+ # we only get here if it's supported by QEMU_FORMAT_RE, so just accept
+ if ($dst_format ne 'raw') {
+ $name .= ".$dst_format";
}
}
$newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024));
my $sparseinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $newvolid);
if (!$running || $snapname) {
+ # TODO: handle bwlimits
qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit);
} else {
if $drive->{iothread};
}
- qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs, $skipcomplete, $qga);
+ qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs, $skipcomplete, $qga, $bwlimit);
}
}