maxLength => 256,
optional => 1,
},
+ protection => {
+ optional => 1,
+ type => 'boolean',
+ description => "Sets the protection flag of the VM. This will prevent the remove operation.",
+ default => 0,
+ },
};
# what about other qemu settings ?
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]',
+ 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] [,serial=serial][,model=model]',
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 $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>]',
+ 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>] [,serial=serial]',
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 $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]',
+ 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] [,serial=serial]',
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 $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]',
+ 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] [,serial=serial]',
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 $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:
my $tmp = `kvm -help 2>/dev/null`;
- if ($tmp =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)[,\s]/) {
+ if ($tmp =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
$kvm_user_version = $2;
}
# [,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]
+# [,serial=serial][,model=model]
sub parse_drive {
my ($key, $data) = @_;
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)=(.+)$/) {
+ 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|serial|model)=(.+)$/) {
my ($k, $v) = ($1, $2);
$k = 'file' if $k eq 'volume';
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}));
}
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);
+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 serial);
sub print_drive {
my ($vmid, $drive) = @_;
$opts .= ",size=" . &$format_size($drive->{size});
}
+ if (my $model = $drive->{model}) {
+ $opts .= ",model=$model";
+ }
+
return "$drive->{file}$opts";
}
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})) {
+ $device .= ",model=$model";
+ }
} elsif ($drive->{interface} eq 'sata'){
my $controller = int($drive->{index} / $MAX_SATA_DISKS);
my $unit = $drive->{index} % $MAX_SATA_DISKS;
}
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;
}
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)$/) {
return undef;
}
+sub split_flagged_list {
+ my $text = shift || '';
+ $text =~ s/[,;]/ /g;
+ $text =~ s/^\s+//;
+ return { map { /^(!?)(.*)$/ && ($2, $1) } ($text =~ /\S+/g) };
+}
+
+sub join_flagged_list {
+ my ($how, $lst) = @_;
+ join $how, map { $lst->{$_} . $_ } keys %$lst;
+}
+
sub vmconfig_delete_pending_option {
- my ($conf, $key) = @_;
+ my ($conf, $key, $force) = @_;
delete $conf->{pending}->{$key};
- my $pending_delete_hash = { $key => 1 };
- foreach my $opt (PVE::Tools::split_list($conf->{pending}->{delete})) {
- $pending_delete_hash->{$opt} = 1;
- }
- $conf->{pending}->{delete} = join(',', keys %$pending_delete_hash);
+ my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
+ $pending_delete_hash->{$key} = $force ? '!' : '';
+ $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash);
}
sub vmconfig_undelete_pending_option {
my ($conf, $key) = @_;
- my $pending_delete_hash = {};
- foreach my $opt (PVE::Tools::split_list($conf->{pending}->{delete})) {
- $pending_delete_hash->{$opt} = 1;
- }
+ my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
delete $pending_delete_hash->{$key};
- my @keylist = keys %$pending_delete_hash;
- if (scalar(@keylist)) {
- $conf->{pending}->{delete} = join(',', @keylist);
+ if (%$pending_delete_hash) {
+ $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash);
} else {
delete $conf->{pending}->{delete};
}
}
}
- # remove delete if option is not set
+ my $current_delete_hash = split_flagged_list($conf->{pending}->{delete});
my $pending_delete_hash = {};
- foreach my $opt (PVE::Tools::split_list($conf->{pending}->{delete})) {
+ while (my ($opt, $force) = each %$current_delete_hash) {
if (defined($conf->{$opt})) {
- $pending_delete_hash->{$opt} = 1;
+ $pending_delete_hash->{$opt} = $force;
} else {
$changes = 1;
}
}
- my @keylist = keys %$pending_delete_hash;
- if (scalar(@keylist)) {
- $conf->{pending}->{delete} = join(',', @keylist);
+ if (%$pending_delete_hash) {
+ $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash);
} else {
delete $conf->{pending}->{delete};
}
my $vmid = $1;
my $conf = $res;
- my $descr = '';
+ my $descr;
my $section = '';
my @lines = split(/\n/, $raw);
if ($line =~ m/^\[PENDING\]\s*$/i) {
$section = 'pending';
- $conf->{description} = $descr if $descr;
- $descr = '';
+ if (defined($descr)) {
+ $descr =~ s/\s+$//;
+ $conf->{description} = $descr;
+ }
+ $descr = undef;
$conf = $res->{$section} = {};
next;
} elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
$section = $1;
- $conf->{description} = $descr if $descr;
- $descr = '';
+ if (defined($descr)) {
+ $descr =~ s/\s+$//;
+ $conf->{description} = $descr;
+ }
+ $descr = undef;
$conf = $res->{snapshots}->{$section} = {};
next;
}
if ($line =~ m/^\#(.*)\s*$/) {
+ $descr = '' if !defined($descr);
$descr .= PVE::Tools::decode_text($1) . "\n";
next;
}
if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
+ $descr = '' if !defined($descr);
$descr .= PVE::Tools::decode_text($2);
} elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
$conf->{snapstate} = $1;
}
}
- $conf->{description} = $descr if $descr;
-
+ if (defined($descr)) {
+ $descr =~ s/\s+$//;
+ $conf->{description} = $descr;
+ }
delete $res->{snapstate}; # just to be sure
return $res;
}
my $generate_raw_config = sub {
- my ($conf) = @_;
+ my ($conf, $pending) = @_;
my $raw = '';
# add description as comment to top of file
- my $descr = $conf->{description} || '';
- foreach my $cl (split(/\n/, $descr)) {
- $raw .= '#' . PVE::Tools::encode_text($cl) . "\n";
+ if (defined(my $descr = $conf->{description})) {
+ if ($descr) {
+ foreach my $cl (split(/\n/, $descr)) {
+ $raw .= '#' . PVE::Tools::encode_text($cl) . "\n";
+ }
+ } else {
+ $raw .= "#\n" if $pending;
+ }
}
foreach my $key (sort keys %$conf) {
if (scalar(keys %{$conf->{pending}})){
$raw .= "\n[PENDING]\n";
- $raw .= &$generate_raw_config($conf->{pending});
+ $raw .= &$generate_raw_config($conf->{pending}, 1);
}
foreach my $snapname (sort keys %{$conf->{snapshots}}) {
}
}
+sub foreach_reverse_dimm {
+ my ($conf, $vmid, $memory, $sockets, $func) = @_;
+
+ my $dimm_id = 253;
+ my $current_size = 4177920;
+ my $dimm_size = 65536;
+ return if $current_size == $memory;
+
+ for (my $j = 0; $j < 8; $j++) {
+ for (my $i = 0; $i < 32; $i++) {
+ my $name = "dimm${dimm_id}";
+ $dimm_id--;
+ my $numanode = $i % $sockets;
+ $current_size -= $dimm_size;
+ &$func($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory);
+ return $current_size if $current_size <= $memory;
+ }
+ $dimm_size /= 2;
+ }
+}
+
sub foreach_drive {
my ($conf, $func) = @_;
push @$cmd, '-p', "CPUQuota=$cpulimit\%";
}
+ # 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
+ my $use_old_bios_files = !qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 4);
+
push @$cmd, '/usr/bin/kvm';
push @$cmd, '-id', $vmid;
}
my $rombar = $d->{rombar} && $d->{rombar} eq 'off' ? ",rombar=0" : "";
- my $driver = $d->{driver} && $d->{driver} eq 'vfio' ? "vfio-pci" : "pci-assign";
my $xvga = $d->{'x-vga'} && $d->{'x-vga'} eq 'on' ? ",x-vga=on" : "";
if ($xvga && $xvga ne '') {
push @$cpuFlags, 'kvm=off';
$vga = 'none';
}
- $driver = "vfio-pci" if $xvga ne '';
my $pcidevices = $d->{pciid};
my $multifunction = 1 if @$pcidevices > 1;
$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";
my $allowed_vcpus = $cpuinfo->{cpus};
- die "MAX $maxcpus vcpus allowed per VM on this node\n"
+ die "MAX $allowed_vcpus vcpus allowed per VM on this node\n"
if ($allowed_vcpus < $maxcpus);
push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64';
- push @$cpuFlags , '+x2apic' if !$nokvm && $conf->{ostype} ne 'solaris';
-
- push @$cpuFlags , '-x2apic' if $conf->{ostype} eq 'solaris';
+ push @$cpuFlags , '-x2apic'
+ if $conf->{ostype} && $conf->{ostype} eq 'solaris';
push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
+ push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
+
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_steal_time' if !$nokvm;
}
+ push @$cpuFlags, 'enforce' if $cpu ne 'host' && !$nokvm;
+
$cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
- push @$cmd, '-cpu', "$cpu,enforce";
+ push @$cmd, '-cpu', $cpu;
my $memory = $conf->{memory} || $defaults->{memory};
my $static_memory = 0;
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;
}
my $dimm_memory = $memory - $static_memory;
die "memory can't be lower than $static_memory MB" if $value < $static_memory;
- die "memory unplug is not yet available" if $value < $memory;
die "you cannot add more memory than $MAX_MEM MB!\n" if $memory > $MAX_MEM;
my $sockets = 1;
$sockets = $conf->{sockets} if $conf->{sockets};
- foreach_dimm($conf, $vmid, $value, $sockets, sub {
- my ($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory) = @_;
+ if($value > $memory) {
- return if $current_size <= $conf->{memory};
+ foreach_dimm($conf, $vmid, $value, $sockets, sub {
+ my ($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory) = @_;
- eval { vm_mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-ram", id => "mem-$name", props => { size => int($dimm_size*1024*1024) } ) };
- if (my $err = $@) {
- eval { qemu_objectdel($vmid, "mem-$name"); };
- die $err;
- }
+ return if $current_size <= $conf->{memory};
- eval { vm_mon_cmd($vmid, "device_add", driver => "pc-dimm", id => "$name", memdev => "mem-$name", node => $numanode) };
- if (my $err = $@) {
- eval { qemu_objectdel($vmid, "mem-$name"); };
- die $err;
- }
- #update conf after each succesful module hotplug
- $conf->{memory} = $current_size;
- update_config_nolock($vmid, $conf, 1);
- });
+ eval { vm_mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-ram", id => "mem-$name", props => { size => int($dimm_size*1024*1024) } ) };
+ if (my $err = $@) {
+ eval { qemu_objectdel($vmid, "mem-$name"); };
+ die $err;
+ }
+
+ eval { vm_mon_cmd($vmid, "device_add", driver => "pc-dimm", id => "$name", memdev => "mem-$name", node => $numanode) };
+ if (my $err = $@) {
+ eval { qemu_objectdel($vmid, "mem-$name"); };
+ die $err;
+ }
+ #update conf after each succesful module hotplug
+ $conf->{memory} = $current_size;
+ update_config_nolock($vmid, $conf, 1);
+ });
+
+ } else {
+
+ foreach_reverse_dimm($conf, $vmid, $value, $sockets, sub {
+ my ($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory) = @_;
+
+ return if $current_size >= $conf->{memory};
+ print "try to unplug memory dimm $name\n";
+
+ my $retry = 0;
+ while (1) {
+ eval { qemu_devicedel($vmid, $name) };
+ sleep 3;
+ my $dimm_list = qemu_dimm_list($vmid);
+ last if !$dimm_list->{$name};
+ raise_param_exc({ $name => "error unplug memory module" }) if $retry > 5;
+ $retry++;
+ }
+
+ #update conf after each succesful module unplug
+ $conf->{memory} = $current_size;
+
+ eval { qemu_objectdel($vmid, "mem-$name"); };
+ update_config_nolock($vmid, $conf, 1);
+ });
+ }
+}
+
+sub qemu_dimm_list {
+ my ($vmid) = @_;
+
+ my $dimmarray = vm_mon_cmd_nocheck($vmid, "query-memory-devices");
+ my $dimms = {};
+
+ foreach my $dimm (@$dimmarray) {
+
+ $dimms->{$dimm->{data}->{id}}->{id} = $dimm->{data}->{id};
+ $dimms->{$dimm->{data}->{id}}->{node} = $dimm->{data}->{node};
+ $dimms->{$dimm->{data}->{id}}->{addr} = $dimm->{data}->{addr};
+ $dimms->{$dimm->{data}->{id}}->{size} = $dimm->{data}->{size};
+ $dimms->{$dimm->{data}->{id}}->{slot} = $dimm->{data}->{slot};
+ }
+ return $dimms;
}
sub qemu_block_set_io_throttle {
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");
'onboot' => 1,
'shares' => 1,
'startup' => 1,
+ 'description' => 1,
};
# hotplug changes in [PENDING]
my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
- my @delete = PVE::Tools::split_list($conf->{pending}->{delete});
- foreach my $opt (@delete) {
+ my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
+ while (my ($opt, $force) = each %$pending_delete_hash) {
next if $selection && !$selection->{$opt};
eval {
if ($opt eq 'hotplug') {
} elsif (valid_drivename($opt)) {
die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
vm_deviceunplug($vmid, $conf, $opt);
- vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}));
+ vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
} elsif ($opt =~ m/^memory$/) {
die "skip\n" if !$hotplug_features->{memory};
qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
}
}
+sub try_deallocate_drive {
+ my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
+
+ if (($force || $key =~ /^unused/) && !drive_is_cdrom($drive, 1)) {
+ my $volid = $drive->{file};
+ if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
+ my $sid = PVE::Storage::parse_volume_id($volid);
+ $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};
+ PVE::Storage::vdisk_free($storecfg, $volid);
+ return 1;
+ } else {
+ # If vm is not owner of this disk remove from config
+ return 1;
+ }
+ }
+
+ return undef;
+}
+
+sub vmconfig_delete_or_detach_drive {
+ my ($vmid, $storecfg, $conf, $opt, $force) = @_;
+
+ my $drive = parse_drive($opt, $conf->{$opt});
+
+ my $rpcenv = PVE::RPCEnvironment::get();
+ my $authuser = $rpcenv->get_user();
+
+ if ($force) {
+ $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
+ try_deallocate_drive($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
+ } else {
+ vmconfig_register_unused_drive($storecfg, $vmid, $conf, $drive);
+ }
+}
+
sub vmconfig_apply_pending {
my ($vmid, $conf, $storecfg) = @_;
# cold plug
- my @delete = PVE::Tools::split_list($conf->{pending}->{delete});
- foreach my $opt (@delete) { # delete
+ 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
if (!defined($conf->{$opt})) {
vmconfig_undelete_pending_option($conf, $opt);
update_config_nolock($vmid, $conf, 1);
} elsif (valid_drivename($opt)) {
- vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$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);
}
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);
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);
}
}
$conf = load_config($vmid);
check_lock($conf) if !$skiplock;
if (!defined($timeout) && $shutdown && $conf->{startup}) {
- my $opts = parse_startup($conf->{startup});
+ my $opts = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
$timeout = $opts->{down} if $opts->{down};
}
}
}
sub vm_resume {
- my ($vmid, $skiplock) = @_;
+ my ($vmid, $skiplock, $nocheck) = @_;
lock_config($vmid, sub {
- my $conf = load_config($vmid);
+ if (!$nocheck) {
- check_lock($conf) if !($skiplock || ($conf->{lock} && $conf->{lock} eq 'backup'));
+ my $conf = load_config($vmid);
+
+ check_lock($conf) if !($skiplock || ($conf->{lock} && $conf->{lock} eq 'backup'));
- vm_mon_cmd($vmid, "cont");
+ vm_mon_cmd($vmid, "cont");
+
+ } else {
+ vm_mon_cmd_nocheck($vmid, "cont");
+ }
});
}
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) = @_;
$d->{volid} = $volid;
my $path = PVE::Storage::path($cfg, $volid);
+ 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' ||
alarm($oldtimeout) if $oldtimeout;
+ my $vollist = [];
+ foreach my $devname (keys %$devinfo) {
+ my $volid = $devinfo->{$devname}->{volid};
+ push @$vollist, $volid if $volid;
+ }
+
+ my $cfg = cfs_read_file('storage.cfg');
+ PVE::Storage::deactivate_volumes($cfg, $vollist);
+
unlink $mapfifo;
if ($err) {
rmtree $tmpdir;
unlink $tmpfn;
- my $cfg = cfs_read_file('storage.cfg');
foreach my $devname (keys %$devinfo) {
my $volid = $devinfo->{$devname}->{volid};
next if !$volid;
my $storage_name = PVE::Storage::parse_volume_id($volid);
- if ($qemu_snap_storage->{$storecfg->{ids}->{$storage_name}->{type}} ){
+ if ($qemu_snap_storage->{$storecfg->{ids}->{$storage_name}->{type}}
+ && !$storecfg->{ids}->{$storage_name}->{krbd}){
return 1;
}
$newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $format, undef, ($size/1024));
push @$newvollist, $newvolid;
+ PVE::Storage::activate_volumes($storecfg, $newvollist);
+
if (!$running || $snapname) {
qemu_img_convert($drive->{file}, $newvolid, $size, $snapname);
} else {
return ($maxdev, $controller, $controller_prefix);
}
+# bash completion helper
+
+sub complete_backup_archives {
+ my ($cmdname, $pname, $cvalue) = @_;
+
+ my $cfg = PVE::Storage::config();
+
+ my $storeid;
+
+ if ($cvalue =~ m/^([^:]+):/) {
+ $storeid = $1;
+ }
+
+ my $data = PVE::Storage::template_list($cfg, $storeid, 'backup');
+
+ my $res = [];
+ foreach my $id (keys %$data) {
+ foreach my $item (@{$data->{$id}}) {
+ next if $item->{format} !~ m/^vma\.(gz|lzo)$/;
+ push @$res, $item->{volid} if defined($item->{volid});
+ }
+ }
+
+ return $res;
+}
+
+my $complete_vmid_full = sub {
+ my ($running) = @_;
+
+ my $idlist = vmstatus();
+
+ my $res = [];
+
+ foreach my $id (keys %$idlist) {
+ my $d = $idlist->{$id};
+ if (defined($running)) {
+ next if $d->{template};
+ next if $running && $d->{status} ne 'running';
+ next if !$running && $d->{status} eq 'running';
+ }
+ push @$res, $id;
+
+ }
+ return $res;
+};
+
+sub complete_vmid {
+ return &$complete_vmid_full();
+}
+
+sub complete_vmid_stopped {
+ return &$complete_vmid_full(0);
+}
+
+sub complete_vmid_running {
+ return &$complete_vmid_full(1);
+}
+
+sub complete_storage {
+
+ my $cfg = PVE::Storage::config();
+ my $ids = $cfg->{ids};
+
+ my $res = [];
+ foreach my $sid (keys %$ids) {
+ next if !PVE::Storage::storage_check_enabled($cfg, $sid, undef, 1);
+ push @$res, $sid;
+ }
+
+ return $res;
+}
+
1;