},
};
+my $watchdog_fmt = {
+ model => {
+ default_key => 1,
+ type => 'string',
+ enum => [qw(i6300esb ib700)],
+ description => "Watchdog type to emulate.",
+ default => 'i6300esb',
+ optional => 1,
+ },
+ action => {
+ type => 'string',
+ enum => [qw(reset shutdown poweroff pause debug none)],
+ description => "The action to perform if after activation the guest fails to poll the watchdog in time.",
+ optional => 1,
+ },
+};
+PVE::JSONSchema::register_format('pve-qm-watchdog', $watchdog_fmt);
+
my $confdesc = {
onboot => {
optional => 1,
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 watchdog will reset " .
optional => 1,
type => 'string',
description => <<EODESCR,
-NOTE: this option is for experts only. It allows you to pass arbitrary arguments to kvm, for example:
+Arbitrary arguments passed to kvm, for example:
args: -no-reboot -no-hpet
+
+NOTE: this option is for experts only.
EODESCR
},
tablet => {
$confdesc->{"net$i"} = $netdesc;
}
-PVE::JSONSchema::register_format('pve-volume-id-or-none', \&verify_volume_id_or_none);
-sub verify_volume_id_or_none {
+PVE::JSONSchema::register_format('pve-volume-id-or-qm-path', \&verify_volume_id_or_qm_path);
+sub verify_volume_id_or_qm_path {
my ($volid, $noerr) = @_;
- return $volid if $volid eq 'none';
+ if ($volid eq 'none' || $volid eq 'cdrom' || $volid =~ m|^/|) {
+ return $volid;
+ }
+
+ # if its neither 'none' nor 'cdrom' nor a path, check if its a volume-id
$volid = eval { PVE::JSONSchema::check_format('pve-volume-id', $volid, '') };
if ($@) {
return undef if $noerr;
volume => { alias => 'file' },
file => {
type => 'string',
- format => 'pve-volume-id-or-none',
+ format => 'pve-volume-id-or-qm-path',
default_key => 1,
format_description => 'volume',
description => "The drive's backing volume.",
};
PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
+# NOTE: the match-groups of this regex are used in parse_hostpci
+my $PCIRE = qr/([a-f0-9]{2}:[a-f0-9]{2})(?:\.([a-f0-9]))?/;
+my $hostpci_fmt = {
+ host => {
+ default_key => 1,
+ type => 'string',
+ pattern => qr/$PCIRE(;$PCIRE)*/,
+ format_description => 'HOSTPCIID[;HOSTPCIID2...]',
+ description => "The PCI ID of a host's PCI device or a list of PCI virtual functions of the host.",
+ },
+ rombar => {
+ type => 'boolean',
+ optional => 1,
+ default => 1,
+ },
+ pcie => {
+ type => 'boolean',
+ optional => 1,
+ default => 0,
+ },
+ 'x-vga' => {
+ type => 'boolean',
+ optional => 1,
+ default => 0,
+ },
+};
+PVE::JSONSchema::register_format('pve-qm-hostpci', $hostpci_fmt);
+
my $hostpcidesc = {
optional => 1,
type => 'string', format => 'pve-qm-hostpci',
- typetext => "[host=]HOSTPCIDEVICE [,rombar=on|off] [,pcie=0|1] [,x-vga=on|off]",
description => <<EODESCR,
Map host pci devices. HOSTPCIDEVICE syntax is:
return undef if !$value;
+ my $res = PVE::JSONSchema::parse_property_string($hostpci_fmt, $value);
- my @list = split(/,/, $value);
- my $found;
-
- my $res = {};
- foreach my $kv (@list) {
-
- if ($kv =~ m/^(host=)?([a-f0-9]{2}:[a-f0-9]{2})(\.([a-f0-9]))?$/) {
- $found = 1;
- if(defined($4)){
- push @{$res->{pciid}}, { id => $2 , function => $4};
-
- }else{
- my $pcidevices = lspci($2);
- $res->{pciid} = $pcidevices->{$2};
- }
- } elsif ($kv =~ m/^rombar=(on|off)$/) {
- $res->{rombar} = $1;
- } elsif ($kv =~ m/^x-vga=(on|off)$/) {
- $res->{'x-vga'} = $1;
- } elsif ($kv =~ m/^pcie=(\d+)$/) {
- $res->{pcie} = 1 if $1 == 1;
+ my @idlist = split(/;/, $res->{host});
+ delete $res->{host};
+ foreach my $id (@idlist) {
+ if ($id =~ /^$PCIRE$/) {
+ push @{$res->{pciid}}, { id => $1, function => ($2//'0') };
} else {
- warn "unknown hostpci setting '$kv'\n";
+ # should have been caught by parse_property_string already
+ die "failed to parse PCI id: $id\n";
}
}
-
- return undef if !$found;
-
return $res;
}
die "unable to parse network options\n";
}
-PVE::JSONSchema::register_format('pve-qm-hostpci', \&verify_hostpci);
-sub verify_hostpci {
- my ($value, $noerr) = @_;
-
- return $value if parse_hostpci($value);
-
- return undef if $noerr;
-
- die "unable to parse pci id\n";
-}
-
-PVE::JSONSchema::register_format('pve-qm-watchdog', \&verify_watchdog);
-sub verify_watchdog {
- my ($value, $noerr) = @_;
-
- return $value if parse_watchdog($value);
-
- return undef if $noerr;
-
- die "unable to parse watchdog options\n";
-}
-
sub parse_watchdog {
my ($value) = @_;
return undef if !$value;
- my $res = {};
-
- foreach my $p (split(/,/, $value)) {
- next if $p =~ m/^\s*$/;
-
- if ($p =~ m/^(model=)?(i6300esb|ib700)$/) {
- $res->{model} = $2;
- } elsif ($p =~ m/^(action=)?(reset|shutdown|poweroff|pause|debug|none)$/) {
- $res->{action} = $2;
- } else {
- return undef;
- }
- }
-
+ my $res = eval { PVE::JSONSchema::parse_property_string($watchdog_fmt, $value) };
+ warn $@ if $@;
return $res;
}
$pciaddr = print_pci_addr("hostpci$i", $bridges);
}
- my $rombar = $d->{rombar} && $d->{rombar} eq 'off' ? ",rombar=0" : "";
- my $xvga = $d->{'x-vga'} && $d->{'x-vga'} eq 'on' ? ",x-vga=on" : "";
- if ($xvga && $xvga ne '') {
+ my $rombar = defined($d->{rombar}) && !$d->{rombar} ? ',rombar=0' : '';
+ my $xvga = '';
+ if ($d->{'x-vga'}) {
+ $xvga = ',x-vga=on';
$kvm_off = 1;
$vga = 'none';
if ($ostype eq 'win7' || $ostype eq 'win8' || $ostype eq 'w2k8') {
print "drive mirror is starting (scanning bitmap) : this step can take some minutes/hours, depend of disk size and storage speed\n";
+ my $finish_job = sub {
+ while (1) {
+ my $stats = vm_mon_cmd($vmid, "query-block-jobs");
+ my $stat = @$stats[0];
+ last if !$stat;
+ sleep 1;
+ }
+ };
+
eval {
vm_mon_cmd($vmid, "drive-mirror", %$opts);
while (1) {
# try to switch the disk if source and destination are on the same guest
eval { vm_mon_cmd($vmid, "block-job-complete", device => "drive-$drive") };
- last if !$@;
+ if (!$@) {
+ &$finish_job();
+ last;
+ }
die $@ if $@ !~ m/cannot be completed/;
}
sleep 1;
my $cancel_job = sub {
vm_mon_cmd($vmid, "block-job-cancel", device => "drive-$drive");
- while (1) {
- my $stats = vm_mon_cmd($vmid, "query-block-jobs");
- my $stat = @$stats[0];
- last if !$stat;
- sleep 1;
- }
+ &$finish_job();
};
if ($err) {