use PVE::QemuServer::Helpers qw(min_version config_aware_timeout);
use PVE::QemuServer::Cloudinit;
use PVE::QemuServer::CPUConfig qw(print_cpu_device get_cpu_options);
-use PVE::QemuServer::Drive qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom parse_drive print_drive foreach_drive foreach_volid);
+use PVE::QemuServer::Drive qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom parse_drive print_drive);
use PVE::QemuServer::Machine;
use PVE::QemuServer::Memory;
use PVE::QemuServer::Monitor qw(mon_cmd);
if ($conf->{template}) {
# check if any base image is still used by a linked clone
- foreach_drive($conf, sub {
+ PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
return if drive_is_cdrom($drive);
}
# only remove disks owned by this VM
- foreach_drive($conf, sub {
+ PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
return if drive_is_cdrom($drive, 1);
sub check_storage_availability {
my ($storecfg, $conf, $node) = @_;
- foreach_drive($conf, sub {
+ PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
my $volid = $drive->{file};
my $nodehash = { map { $_ => 1 } @$nodelist };
my $nodename = nodename();
- foreach_drive($conf, sub {
+ PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
my $volid = $drive->{file};
my $nodelist = PVE::Cluster::get_nodelist();
my $nodehash = { map { $_ => {} } @$nodelist };
- foreach_drive($conf, sub {
+ PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
my $volid = $drive->{file};
};
}
+sub audio_devs {
+ my ($audio, $audiopciaddr) = @_;
+
+ my $devs = [];
+
+ my $id = $audio->{dev_id};
+ my $audiodev = "audiodev=$audio->{backend_id}";
+
+ if ($audio->{dev} eq 'AC97') {
+ push @$devs, '-device', "AC97,id=${id}${audiopciaddr},$audiodev";
+ } elsif ($audio->{dev} =~ /intel\-hda$/) {
+ push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
+ push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0,$audiodev";
+ push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1,$audiodev";
+ } else {
+ die "unkown audio device '$audio->{dev}', implement me!";
+ }
+
+ push @$devs, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
+
+ return $devs;
+}
+
sub vga_conf_has_spice {
my ($vga) = @_;
$machine_version =~ m/(\d+)\.(\d+)/;
my ($machine_major, $machine_minor) = ($1, $2);
- die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type', please upgrade node '$nodename'\n"
- if !PVE::QemuServer::min_version($kvmver, $machine_major, $machine_minor);
- if (!PVE::QemuServer::Machine::can_run_pve_machine_version($machine_version, $kvmver)) {
+ if ($kvmver =~ m/^\d+\.\d+\.(\d+)/ && $1 >= 90) {
+ warn "warning: Installed QEMU version ($kvmver) is a release candidate, ignoring version checks\n";
+ } elsif (!min_version($kvmver, $machine_major, $machine_minor)) {
+ die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type', please upgrade node '$nodename'\n"
+ } elsif (!PVE::QemuServer::Machine::can_run_pve_machine_version($machine_version, $kvmver)) {
my $max_pve_version = PVE::QemuServer::Machine::get_pve_version($machine_version);
die "Installed qemu-server (max feature level for $machine_major.$machine_minor is pve$max_pve_version)"
- . " is too old to run machine type '$machine_type', please upgrade node '$nodename'\n";
+ ." is too old to run machine type '$machine_type', please upgrade node '$nodename'\n";
}
# if a specific +pve version is required for a feature, use $version_guard
}
}
- my ($ovmf_code, $ovmf_vars) = get_ovmf_files($arch);
if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
- die "uefi base image not found\n" if ! -f $ovmf_code;
+ my ($ovmf_code, $ovmf_vars) = get_ovmf_files($arch);
+ die "uefi base image '$ovmf_code' not found\n" if ! -f $ovmf_code;
- my $path;
- my $format;
- if (my $efidisk = $conf->{efidisk0}) {
- my $d = parse_drive('efidisk0', $efidisk);
+ my ($path, $format);
+ if (my $d = parse_drive('efidisk0', $conf->{efidisk0})) {
my ($storeid, $volname) = PVE::Storage::parse_volume_id($d->{file}, 1);
$format = $d->{format};
if ($storeid) {
}
}
- if (my $audio = conf_has_audio($conf)) {
-
+ if (min_version($machine_version, 4, 0) && (my $audio = conf_has_audio($conf))) {
my $audiopciaddr = print_pci_addr("audio0", $bridges, $arch, $machine_type);
-
- my $id = $audio->{dev_id};
- if ($audio->{dev} eq 'AC97') {
- push @$devices, '-device', "AC97,id=${id}${audiopciaddr}";
- } elsif ($audio->{dev} =~ /intel\-hda$/) {
- push @$devices, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
- push @$devices, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0";
- push @$devices, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1";
- } else {
- die "unkown audio device '$audio->{dev}', implement me!";
- }
-
- push @$devices, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
+ my $audio_devs = audio_devs($audio, $audiopciaddr);
+ push @$devices, @$audio_devs;
}
my $sockets = 1;
# time drift fix
my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
-
my $useLocaltime = $conf->{localtime};
if ($winversion >= 5) { # windows
push @$rtcFlags, 'driftfix=slew' if $tdf;
- if (($conf->{startdate}) && ($conf->{startdate} ne 'now')) {
+ if ($conf->{startdate} && $conf->{startdate} ne 'now') {
push @$rtcFlags, "base=$conf->{startdate}";
} elsif ($useLocaltime) {
push @$rtcFlags, 'base=localtime';
if ($forcecpu) {
push @$cmd, '-cpu', $forcecpu;
} else {
- push @$cmd, get_cpu_options($conf, $arch, $kvm, $kvm_off,
- $machine_version, $winversion, $gpu_passthrough);
+ push @$cmd, get_cpu_options($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
}
PVE::QemuServer::Memory::config($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
if ($qxlnum) {
if ($qxlnum > 1) {
if ($winversion){
- for(my $i = 1; $i < $qxlnum; $i++){
+ for (my $i = 1; $i < $qxlnum; $i++){
push @$devices, '-device', print_vga_device($conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
}
} else {
push @$devices, '-iscsi', "initiator-name=$initiator";
}
- foreach_drive($conf, sub {
+ PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
if (PVE::Storage::parse_volume_id($drive->{file}, 1)) {
}
}
- if($drive->{interface} eq 'virtio'){
+ if ($drive->{interface} eq 'virtio'){
push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread};
}
- if ($drive->{interface} eq 'scsi') {
+ if ($drive->{interface} eq 'scsi') {
my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive);
push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues" if !$scsicontroller->{$controller};
$scsicontroller->{$controller}=1;
- }
+ }
if ($drive->{interface} eq 'sata') {
- my $controller = int($drive->{index} / $PVE::QemuServer::Drive::MAX_SATA_DISKS);
- $pciaddr = print_pci_addr("ahci$controller", $bridges, $arch, $machine_type);
- push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
- $ahcicontroller->{$controller}=1;
+ my $controller = int($drive->{index} / $PVE::QemuServer::Drive::MAX_SATA_DISKS);
+ $pciaddr = print_pci_addr("ahci$controller", $bridges, $arch, $machine_type);
+ push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
+ $ahcicontroller->{$controller}=1;
}
my $drive_cmd = print_drive_commandline_full($storecfg, $vmid, $drive);
});
for (my $i = 0; $i < $MAX_NETS; $i++) {
- next if !$conf->{"net$i"};
- my $d = parse_net($conf->{"net$i"});
- next if !$d;
+ next if !$conf->{"net$i"};
+ my $d = parse_net($conf->{"net$i"});
+ next if !$d;
- $use_virtio = 1 if $d->{model} eq 'virtio';
+ $use_virtio = 1 if $d->{model} eq 'virtio';
- if ($bootindex_hash->{n}) {
- $d->{bootindex} = $bootindex_hash->{n};
- $bootindex_hash->{n} += 1;
- }
+ if ($bootindex_hash->{n}) {
+ $d->{bootindex} = $bootindex_hash->{n};
+ $bootindex_hash->{n} += 1;
+ }
- my $netdevfull = print_netdev_full($vmid, $conf, $arch, $d, "net$i");
- push @$devices, '-netdev', $netdevfull;
+ my $netdevfull = print_netdev_full($vmid, $conf, $arch, $d, "net$i");
+ push @$devices, '-netdev', $netdevfull;
- my $netdevicefull = print_netdevice_full($vmid, $conf, $d, "net$i", $bridges, $use_old_bios_files, $arch, $machine_type);
- push @$devices, '-device', $netdevicefull;
+ my $netdevicefull = print_netdevice_full($vmid, $conf, $d, "net$i", $bridges, $use_old_bios_files, $arch, $machine_type);
+ push @$devices, '-device', $netdevicefull;
}
if ($conf->{ivshmem}) {
push @$machineFlags, "type=${machine_type_min}";
push @$cmd, @$devices;
- push @$cmd, '-rtc', join(',', @$rtcFlags)
- if scalar(@$rtcFlags);
- push @$cmd, '-machine', join(',', @$machineFlags)
- if scalar(@$machineFlags);
- push @$cmd, '-global', join(',', @$globalFlags)
- if scalar(@$globalFlags);
+ push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
+ push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
+ push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags);
if (my $vmstate = $conf->{vmstate}) {
my $statepath = PVE::Storage::path($storecfg, $vmstate);
$running = undef;
my $conf = PVE::QemuConfig->load_config($vmid);
- foreach_drive($conf, sub {
+ PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
$running = 1 if $drive->{file} eq $volid;
});
mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
}
+sub foreach_volid {
+ my ($conf, $func, @param) = @_;
+
+ my $volhash = {};
+
+ my $test_volid = sub {
+ my ($volid, $is_cdrom, $replicate, $shared, $snapname, $size) = @_;
+
+ return if !$volid;
+
+ $volhash->{$volid}->{cdrom} //= 1;
+ $volhash->{$volid}->{cdrom} = 0 if !$is_cdrom;
+
+ $volhash->{$volid}->{replicate} //= 0;
+ $volhash->{$volid}->{replicate} = 1 if $replicate;
+
+ $volhash->{$volid}->{shared} //= 0;
+ $volhash->{$volid}->{shared} = 1 if $shared;
+
+ $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);
+ $volhash->{$volid}->{size} = $size if $size;
+ };
+
+ PVE::QemuConfig->foreach_volume($conf, sub {
+ my ($ds, $drive) = @_;
+ $test_volid->($drive->{file}, drive_is_cdrom($drive), $drive->{replicate} // 1, $drive->{shared}, undef, $drive->{size});
+ });
+
+ foreach my $snapname (keys %{$conf->{snapshots}}) {
+ my $snap = $conf->{snapshots}->{$snapname};
+ $test_volid->($snap->{vmstate}, 0, 1, $snapname);
+ $volhash->{$snap->{vmstate}}->{is_vmstate} = 1 if $snap->{vmstate};
+ PVE::QemuConfig->foreach_volume($snap, sub {
+ my ($ds, $drive) = @_;
+ $test_volid->($drive->{file}, drive_is_cdrom($drive), $drive->{replicate} // 1, $drive->{shared}, $snapname);
+ });
+ }
+
+ foreach my $volid (keys %$volhash) {
+ &$func($volid, $volhash->{$volid}, @param);
+ }
+}
+
my $fast_plug_option = {
'lock' => 1,
'name' => 1,
my ($storecfg, $conf, $replicated_volumes) = @_;
my $local_volumes = {};
- foreach_drive($conf, sub {
+ PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
return if drive_is_cdrom($drive);
print "Resuming suspended VM\n";
}
- my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf,
- $defaults, $forcemachine, $forcecpu);
+ my ($cmd, $vollist, $spice_port) =
+ config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu);
my $migration_ip;
my $get_migration_ip = sub {
my $restore_cleanup_oldconf = sub {
my ($storecfg, $vmid, $oldconf, $virtdev_hash) = @_;
- foreach_drive($oldconf, sub {
+ PVE::QemuConfig->foreach_volume($oldconf, sub {
my ($ds, $drive) = @_;
return if drive_is_cdrom($drive, 1);
my $sidhash = {};
- foreach_drive($conf, sub {
+ PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
return if drive_is_cdrom($drive);
my $storecfg = PVE::Storage::config();
- foreach_drive($conf, sub {
+ PVE::QemuConfig->foreach_volume($conf, sub {
my ($ds, $drive) = @_;
return if drive_is_cdrom($drive);