1 package PVE
::QemuServer
;
11 use File
::Copy
qw(copy);
22 use List
::Util
qw(first);
25 use Storable
qw(dclone);
26 use Time
::HiRes
qw(gettimeofday usleep);
30 use PVE
::Cluster
qw(cfs_register_file cfs_read_file cfs_write_file);
33 use PVE
::DataCenterConfig
;
34 use PVE
::Exception
qw(raise raise_param_exc);
35 use PVE
::Format
qw(render_duration render_bytes);
36 use PVE
::GuestHelpers
qw(safe_string_ne safe_num_ne safe_boolean_ne);
38 use PVE
::Mapping
::PCI
;
39 use PVE
::Mapping
::USB
;
41 use PVE
::JSONSchema
qw(get_standard_option parse_property_string);
44 use PVE
::RESTEnvironment
qw(log_warn);
45 use PVE
::RPCEnvironment
;
49 use PVE
::Tools
qw(run_command file_read_firstline file_get_contents dir_glob_foreach get_host_arch $IPV6RE);
53 use PVE
::QemuServer
::Helpers
qw(config_aware_timeout min_version windows_version);
54 use PVE
::QemuServer
::Cloudinit
;
55 use PVE
::QemuServer
::CGroup
;
56 use PVE
::QemuServer
::CPUConfig
qw(print_cpu_device get_cpu_options get_cpu_bitness is_native_arch);
57 use PVE
::QemuServer
::Drive
qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom drive_is_read_only parse_drive print_drive);
58 use PVE
::QemuServer
::Machine
;
59 use PVE
::QemuServer
::Memory
qw(get_current_memory);
60 use PVE
::QemuServer
::Monitor
qw(mon_cmd);
61 use PVE
::QemuServer
::PCI
qw(print_pci_addr print_pcie_addr print_pcie_root_port parse_hostpci);
62 use PVE
::QemuServer
::QMPHelpers
qw(qemu_deviceadd qemu_devicedel qemu_objectadd qemu_objectdel);
63 use PVE
::QemuServer
::USB
;
67 require PVE
::Network
::SDN
::Zones
;
68 require PVE
::Network
::SDN
::Vnets
;
72 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
76 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
77 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
80 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
81 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
84 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
85 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
88 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
89 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
91 # FIXME: These are legacy 2MB-sized images that modern OVMF doesn't supports to build
92 # anymore. how can we deperacate this sanely without breaking existing instances, or using
93 # older backups and snapshot?
95 "$EDK2_FW_BASE/OVMF_CODE.fd",
96 "$EDK2_FW_BASE/OVMF_VARS.fd",
101 "$EDK2_FW_BASE/AAVMF_CODE.fd",
102 "$EDK2_FW_BASE/AAVMF_VARS.fd",
107 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
109 # Note about locking: we use flock on the config file protect against concurent actions.
110 # Aditionaly, we have a 'lock' setting in the config file. This can be set to 'migrate',
111 # 'backup', 'snapshot' or 'rollback'. Most actions are not allowed when such lock is set.
112 # But you can ignore this kind of lock with the --skiplock flag.
120 PVE
::JSONSchema
::register_standard_option
('pve-qm-stateuri', {
121 description
=> "Some command save/restore state from this location.",
127 # FIXME: remove in favor of just using the INotify one, it's cached there exactly the same way
130 $nodename_cache //= PVE
::INotify
::nodename
();
131 return $nodename_cache;
138 enum
=> [qw(i6300esb ib700)],
139 description
=> "Watchdog type to emulate.",
140 default => 'i6300esb',
145 enum
=> [qw(reset shutdown poweroff pause debug none)],
146 description
=> "The action to perform if after activation the guest fails to poll the watchdog in time.",
150 PVE
::JSONSchema
::register_format
('pve-qm-watchdog', $watchdog_fmt);
154 description
=> "Enable/disable communication with a QEMU Guest Agent (QGA) running in the VM.",
159 fstrim_cloned_disks
=> {
160 description
=> "Run fstrim after moving a disk or migrating the VM.",
165 'freeze-fs-on-backup' => {
166 description
=> "Freeze/thaw guest filesystems on backup for consistency.",
172 description
=> "Select the agent type",
176 enum
=> [qw(virtio isa)],
182 description
=> "Select the VGA type.",
187 enum
=> [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio virtio-gl vmware)],
190 description
=> "Sets the VGA memory (in MiB). Has no effect with serial display.",
197 description
=> 'Enable a specific clipboard. If not set, depending on the display type the'
198 .' SPICE one will be added. Migration with VNC clipboard is not yet supported!',
209 description
=> "The size of the file in MB.",
213 pattern
=> '[a-zA-Z0-9\-]+',
215 format_description
=> 'string',
216 description
=> "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
223 enum
=> [qw(ich9-intel-hda intel-hda AC97)],
224 description
=> "Configure an audio device."
228 enum
=> ['spice', 'none'],
231 description
=> "Driver backend for the audio device."
235 my $spice_enhancements_fmt = {
240 description
=> "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
244 enum
=> ['off', 'all', 'filter'],
247 description
=> "Enable video streaming. Uses compression for detected video streams."
254 enum
=> ['/dev/urandom', '/dev/random', '/dev/hwrng'],
256 description
=> "The file on the host to gather entropy from. In most cases '/dev/urandom'"
257 ." should be preferred over '/dev/random' to avoid entropy-starvation issues on the"
258 ." host. Using urandom does *not* decrease security in any meaningful way, as it's"
259 ." still seeded from real entropy, and the bytes provided will most likely be mixed"
260 ." with real entropy on the guest as well. '/dev/hwrng' can be used to pass through"
261 ." a hardware RNG from the host.",
265 description
=> "Maximum bytes of entropy allowed to get injected into the guest every"
266 ." 'period' milliseconds. Prefer a lower value when using '/dev/random' as source. Use"
267 ." `0` to disable limiting (potentially dangerous!).",
270 # default is 1 KiB/s, provides enough entropy to the guest to avoid boot-starvation issues
271 # (e.g. systemd etc...) while allowing no chance of overwhelming the host, provided we're
272 # reading from /dev/urandom
277 description
=> "Every 'period' milliseconds the entropy-injection quota is reset, allowing"
278 ." the guest to retrieve another 'max_bytes' of entropy.",
284 my $meta_info_fmt = {
287 description
=> "The guest creation timestamp as UNIX epoch time",
293 description
=> "The QEMU (machine) version from the time this VM was created.",
294 pattern
=> '\d+(\.\d+)+',
303 description
=> "Specifies whether a VM will be started during system bootup.",
309 description
=> "Automatic restart after crash (currently ignored).",
314 type
=> 'string', format
=> 'pve-hotplug-features',
315 description
=> "Selectively enable hotplug features. This is a comma separated list of"
316 ." hotplug features: 'network', 'disk', 'cpu', 'memory', 'usb' and 'cloudinit'. Use '0' to disable"
317 ." hotplug completely. Using '1' as value is an alias for the default `network,disk,usb`."
318 ." USB hotplugging is possible for guests with machine version >= 7.1 and ostype l26 or"
320 default => 'network,disk,usb',
325 description
=> "Allow reboot. If set to '0' the VM exit on reboot.",
331 description
=> "Lock/unlock the VM.",
332 enum
=> [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
337 description
=> "Limit of CPU usage.",
338 verbose_description
=> "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has"
339 ." total of '2' CPU time. Value '0' indicates no CPU limit.",
347 description
=> "CPU weight for a VM, will be clamped to [1, 10000] in cgroup v2.",
348 verbose_description
=> "CPU weight for a VM. Argument is used in the kernel fair scheduler."
349 ." The larger the number is, the more CPU time this VM gets. Number is relative to"
350 ." weights of all the other running VMs.",
353 default => 'cgroup v1: 1024, cgroup v2: 100',
358 description
=> "Memory properties.",
359 format
=> $PVE::QemuServer
::Memory
::memory_fmt
364 description
=> "Amount of target RAM for the VM in MiB. Using zero disables the ballon driver.",
370 description
=> "Amount of memory shares for auto-ballooning. The larger the number is, the"
371 ." more memory this VM gets. Number is relative to weights of all other running VMs."
372 ." Using zero disables auto-ballooning. Auto-ballooning is done by pvestatd.",
380 description
=> "Keyboard layout for VNC server. This option is generally not required and"
381 ." is often better handled from within the guest OS.",
382 enum
=> PVE
::Tools
::kvmkeymaplist
(),
387 type
=> 'string', format
=> 'dns-name',
388 description
=> "Set a name for the VM. Only used on the configuration web interface.",
393 description
=> "SCSI controller model",
394 enum
=> [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
400 description
=> "Description for the VM. Shown in the web-interface VM's summary."
401 ." This is saved as comment inside the configuration file.",
402 maxLength
=> 1024 * 8,
407 # NOTE: When extending, also consider extending `%guest_types` in `Import/ESXi.pm`.
408 enum
=> [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 win11 l24 l26 solaris)],
409 description
=> "Specify guest operating system.",
410 verbose_description
=> <<EODESC,
411 Specify guest operating system. This is used to enable special
412 optimization/features for specific operating systems:
415 other;; unspecified OS
416 wxp;; Microsoft Windows XP
417 w2k;; Microsoft Windows 2000
418 w2k3;; Microsoft Windows 2003
419 w2k8;; Microsoft Windows 2008
420 wvista;; Microsoft Windows Vista
421 win7;; Microsoft Windows 7
422 win8;; Microsoft Windows 8/2012/2012r2
423 win10;; Microsoft Windows 10/2016/2019
424 win11;; Microsoft Windows 11/2022
425 l24;; Linux 2.4 Kernel
426 l26;; Linux 2.6 - 6.X Kernel
427 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
432 type
=> 'string', format
=> 'pve-qm-boot',
433 description
=> "Specify guest boot order. Use the 'order=' sub-property as usage with no"
434 ." key or 'legacy=' is deprecated.",
438 type
=> 'string', format
=> 'pve-qm-bootdisk',
439 description
=> "Enable booting from specified disk. Deprecated: Use 'boot: order=foo;bar' instead.",
440 pattern
=> '(ide|sata|scsi|virtio)\d+',
445 description
=> "The number of CPUs. Please use option -sockets instead.",
452 description
=> "The number of CPU sockets.",
459 description
=> "The number of cores per socket.",
466 description
=> "Enable/disable NUMA.",
472 description
=> "Enable/disable hugepages memory.",
473 enum
=> [qw(any 2 1024)],
479 description
=> "Use together with hugepages. If enabled, hugepages will not not be deleted"
480 ." after VM shutdown and can be used for subsequent starts.",
485 description
=> "Number of hotplugged vcpus.",
492 description
=> "Enable/disable ACPI.",
497 description
=> "Enable/disable communication with the QEMU Guest Agent and its properties.",
499 format
=> $agent_fmt,
504 description
=> "Enable/disable KVM hardware virtualization.",
510 description
=> "Enable/disable time drift fix.",
516 description
=> "Set the real time clock (RTC) to local time. This is enabled by default if"
517 ." the `ostype` indicates a Microsoft Windows OS.",
522 description
=> "Freeze CPU at startup (use 'c' monitor command to start execution).",
526 type
=> 'string', format
=> $vga_fmt,
527 description
=> "Configure the VGA hardware.",
528 verbose_description
=> "Configure the VGA Hardware. If you want to use high resolution"
529 ." modes (>= 1280x1024x16) you may need to increase the vga memory option. Since QEMU"
530 ." 2.9 the default VGA display type is 'std' for all OS types besides some Windows"
531 ." versions (XP and older) which use 'cirrus'. The 'qxl' option enables the SPICE"
532 ." display server. For win* OS you can select how many independent displays you want,"
533 ." Linux guests can add displays them self.\nYou can also run without any graphic card,"
534 ." using a serial device as terminal.",
538 type
=> 'string', format
=> 'pve-qm-watchdog',
539 description
=> "Create a virtual hardware watchdog device.",
540 verbose_description
=> "Create a virtual hardware watchdog device. Once enabled (by a guest"
541 ." action), the watchdog must be periodically polled by an agent inside the guest or"
542 ." else the watchdog will reset the guest (or execute the respective action specified)",
547 typetext
=> "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
548 description
=> "Set the initial date of the real time clock. Valid format for date are:"
549 ."'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
550 pattern
=> '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
553 startup
=> get_standard_option
('pve-startup-order'),
557 description
=> "Enable/disable Template.",
563 description
=> "Arbitrary arguments passed to kvm.",
564 verbose_description
=> <<EODESCR,
565 Arbitrary arguments passed to kvm, for example:
567 args: -no-reboot -smbios 'type=0,vendor=FOO'
569 NOTE: this option is for experts only.
576 description
=> "Enable/disable the USB tablet device.",
577 verbose_description
=> "Enable/disable the USB tablet device. This device is usually needed"
578 ." to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with"
579 ." normal VNC clients. If you're running lots of console-only guests on one host, you"
580 ." may consider disabling this to save some context switches. This is turned off by"
581 ." default if you use spice (`qm set <vmid> --vga qxl`).",
586 description
=> "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
590 migrate_downtime
=> {
593 description
=> "Set maximum tolerated downtime (in seconds) for migrations.",
599 type
=> 'string', format
=> 'pve-qm-ide',
600 typetext
=> '<volume>',
601 description
=> "This is an alias for option -ide2",
605 description
=> "Emulated CPU type.",
607 format
=> 'pve-vm-cpu-conf',
609 parent
=> get_standard_option
('pve-snapshot-name', {
611 description
=> "Parent snapshot name. This is used internally, and should not be modified.",
615 description
=> "Timestamp for snapshots.",
621 type
=> 'string', format
=> 'pve-volume-id',
622 description
=> "Reference to a volume which stores the VM state. This is used internally"
625 vmstatestorage
=> get_standard_option
('pve-storage-id', {
626 description
=> "Default storage for VM state volumes/files.",
629 runningmachine
=> get_standard_option
('pve-qemu-machine', {
630 description
=> "Specifies the QEMU machine type of the running vm. This is used internally"
634 description
=> "Specifies the QEMU '-cpu' parameter of the running vm. This is used"
635 ." internally for snapshots.",
638 pattern
=> $PVE::QemuServer
::CPUConfig
::qemu_cmdline_cpu_re
,
639 format_description
=> 'QEMU -cpu parameter'
641 machine
=> get_standard_option
('pve-qemu-machine'),
643 description
=> "Virtual processor architecture. Defaults to the host.",
646 enum
=> [qw(x86_64 aarch64)],
649 description
=> "Specify SMBIOS type 1 fields.",
650 type
=> 'string', format
=> 'pve-qm-smbios1',
657 description
=> "Sets the protection flag of the VM. This will disable the remove VM and"
658 ." remove disk operations.",
664 enum
=> [ qw(seabios ovmf) ],
665 description
=> "Select BIOS implementation.",
666 default => 'seabios',
670 pattern
=> '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
671 format_description
=> 'UUID',
672 description
=> "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0'"
673 ." to disable explicitly.",
674 verbose_description
=> "The VM generation ID (vmgenid) device exposes a 128-bit integer"
675 ." value identifier to the guest OS. This allows to notify the guest operating system"
676 ." when the virtual machine is executed with a different configuration (e.g. snapshot"
677 ." execution or creation from a template). The guest operating system notices the"
678 ." change, and is then able to react as appropriate by marking its copies of"
679 ." distributed databases as dirty, re-initializing its random number generator, etc.\n"
680 ."Note that auto-creation only works when done through API/CLI create or update methods"
681 .", but not when manually editing the config file.",
682 default => "1 (autogenerated)",
687 format
=> 'pve-volume-id',
689 description
=> "Script that will be executed during various steps in the vms lifetime.",
693 format
=> $ivshmem_fmt,
694 description
=> "Inter-VM shared memory. Useful for direct communication between VMs, or to"
700 format
=> $audio_fmt,
701 description
=> "Configure a audio device, useful in combination with QXL/Spice.",
704 spice_enhancements
=> {
706 format
=> $spice_enhancements_fmt,
707 description
=> "Configure additional enhancements for SPICE.",
711 type
=> 'string', format
=> 'pve-tag-list',
712 description
=> 'Tags of the VM. This is only meta information.',
718 description
=> "Configure a VirtIO-based Random Number Generator.",
723 format
=> $meta_info_fmt,
724 description
=> "Some (read-only) meta-information about this guest.",
728 type
=> 'string', format
=> 'pve-cpuset',
729 description
=> "List of host cores used to execute guest processes, for example: 0,5,8-11",
738 description
=> 'Specify a custom file containing all meta data passed to the VM via"
739 ." cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
740 format
=> 'pve-volume-id',
741 format_description
=> 'volume',
746 description
=> 'To pass a custom file containing all network data to the VM via cloud-init.',
747 format
=> 'pve-volume-id',
748 format_description
=> 'volume',
753 description
=> 'To pass a custom file containing all user data to the VM via cloud-init.',
754 format
=> 'pve-volume-id',
755 format_description
=> 'volume',
760 description
=> 'To pass a custom file containing all vendor data to the VM via cloud-init.',
761 format
=> 'pve-volume-id',
762 format_description
=> 'volume',
765 PVE
::JSONSchema
::register_format
('pve-qm-cicustom', $cicustom_fmt);
767 # any new option might need to be added to $cloudinitoptions in PVE::API2::Qemu
768 my $confdesc_cloudinit = {
772 description
=> 'Specifies the cloud-init configuration format. The default depends on the'
773 .' configured operating system type (`ostype`. We use the `nocloud` format for Linux,'
774 .' and `configdrive2` for windows.',
775 enum
=> ['configdrive2', 'nocloud', 'opennebula'],
780 description
=> "cloud-init: User name to change ssh keys and password for instead of the"
781 ." image's configured default user.",
786 description
=> 'cloud-init: Password to assign the user. Using this is generally not'
787 .' recommended. Use ssh keys instead. Also note that older cloud-init versions do not'
788 .' support hashed passwords.',
793 description
=> 'cloud-init: do an automatic package upgrade after the first boot.',
799 description
=> 'cloud-init: Specify custom files to replace the automatically generated'
801 format
=> 'pve-qm-cicustom',
806 description
=> 'cloud-init: Sets DNS search domains for a container. Create will'
807 .' automatically use the setting from the host if neither searchdomain nor nameserver'
812 type
=> 'string', format
=> 'address-list',
813 description
=> 'cloud-init: Sets DNS server IP address for a container. Create will'
814 .' automatically use the setting from the host if neither searchdomain nor nameserver'
820 format
=> 'urlencoded',
821 description
=> "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
825 # what about other qemu settings ?
827 #machine => 'string',
840 ##soundhw => 'string',
842 while (my ($k, $v) = each %$confdesc) {
843 PVE
::JSONSchema
::register_standard_option
("pve-qm-$k", $v);
847 my $MAX_SERIAL_PORTS = 4;
848 my $MAX_PARALLEL_PORTS = 3;
850 for (my $i = 0; $i < $PVE::QemuServer
::Memory
::MAX_NUMA
; $i++) {
851 $confdesc->{"numa$i"} = $PVE::QemuServer
::Memory
::numadesc
;
854 my $nic_model_list = [
870 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
872 my $net_fmt_bridge_descr = <<__EOD__;
873 Bridge to attach the network device to. The Proxmox VE standard bridge
876 If you do not specify a bridge, we create a kvm user (NATed) network
877 device, which provides DHCP and DNS services. The following addresses
884 The DHCP server assign addresses to the guest starting from 10.0.2.15.
888 macaddr
=> get_standard_option
('mac-addr', {
889 description
=> "MAC address. That address must be unique withing your network. This is"
890 ." automatically generated if not specified.",
894 description
=> "Network Card Model. The 'virtio' model provides the best performance with"
895 ." very low CPU overhead. If your guest does not support this driver, it is usually"
896 ." best to use 'e1000'.",
897 enum
=> $nic_model_list,
900 (map { $_ => { keyAlias
=> 'model', alias
=> 'macaddr' }} @$nic_model_list),
901 bridge
=> get_standard_option
('pve-bridge-id', {
902 description
=> $net_fmt_bridge_descr,
907 minimum
=> 0, maximum
=> 64,
908 description
=> 'Number of packet queues to be used on the device.',
914 description
=> "Rate limit in mbps (megabytes per second) as floating point number.",
919 minimum
=> 1, maximum
=> 4094,
920 description
=> 'VLAN tag to apply to packets on this interface.',
925 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
926 description
=> 'VLAN trunks to pass through this interface.',
927 format_description
=> 'vlanid[;vlanid...]',
932 description
=> 'Whether this interface should be protected by the firewall.',
937 description
=> 'Whether this interface should be disconnected (like pulling the plug).',
942 minimum
=> 1, maximum
=> 65520,
943 description
=> "Force MTU, for VirtIO only. Set to '1' to use the bridge MTU",
950 type
=> 'string', format
=> $net_fmt,
951 description
=> "Specify network devices.",
954 PVE
::JSONSchema
::register_standard_option
("pve-qm-net", $netdesc);
959 format
=> 'pve-ipv4-config',
960 format_description
=> 'IPv4Format/CIDR',
961 description
=> 'IPv4 address in CIDR format.',
968 format_description
=> 'GatewayIPv4',
969 description
=> 'Default gateway for IPv4 traffic.',
975 format
=> 'pve-ipv6-config',
976 format_description
=> 'IPv6Format/CIDR',
977 description
=> 'IPv6 address in CIDR format.',
984 format_description
=> 'GatewayIPv6',
985 description
=> 'Default gateway for IPv6 traffic.',
990 PVE
::JSONSchema
::register_format
('pve-qm-ipconfig', $ipconfig_fmt);
993 type
=> 'string', format
=> 'pve-qm-ipconfig',
994 description
=> <<'EODESCR',
995 cloud-init: Specify IP addresses and gateways for the corresponding interface.
997 IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
999 The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit
1000 gateway should be provided.
1001 For IPv6 the special string 'auto' can be used to use stateless autoconfiguration. This requires
1002 cloud-init 19.4 or newer.
1004 If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using
1008 PVE
::JSONSchema
::register_standard_option
("pve-qm-ipconfig", $netdesc);
1010 for (my $i = 0; $i < $MAX_NETS; $i++) {
1011 $confdesc->{"net$i"} = $netdesc;
1012 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
1015 foreach my $key (keys %$confdesc_cloudinit) {
1016 $confdesc->{$key} = $confdesc_cloudinit->{$key};
1019 PVE
::JSONSchema
::register_format
('pve-cpuset', \
&pve_verify_cpuset
);
1020 sub pve_verify_cpuset
{
1021 my ($set_text, $noerr) = @_;
1023 my ($count, $members) = eval { PVE
::CpuSet
::parse_cpuset
($set_text) };
1027 die "unable to parse cpuset option\n";
1030 return PVE
::CpuSet-
>new($members)->short_string();
1033 PVE
::JSONSchema
::register_format
('pve-volume-id-or-qm-path', \
&verify_volume_id_or_qm_path
);
1034 sub verify_volume_id_or_qm_path
{
1035 my ($volid, $noerr) = @_;
1037 return $volid if $volid eq 'none' || $volid eq 'cdrom';
1039 return verify_volume_id_or_absolute_path
($volid, $noerr);
1042 PVE
::JSONSchema
::register_format
('pve-volume-id-or-absolute-path', \
&verify_volume_id_or_absolute_path
);
1043 sub verify_volume_id_or_absolute_path
{
1044 my ($volid, $noerr) = @_;
1046 return $volid if $volid =~ m
|^/|;
1048 $volid = eval { PVE
::JSONSchema
::check_format
('pve-volume-id', $volid, '') };
1059 pattern
=> '(/dev/.+|socket)',
1060 description
=> "Create a serial device inside the VM (n is 0 to 3)",
1061 verbose_description
=> <<EODESCR,
1062 Create a serial device inside the VM (n is 0 to 3), and pass through a
1063 host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1064 host side (use 'qm terminal' to open a terminal connection).
1066 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines -
1067 use with special care.
1069 CAUTION: Experimental! User reported problems with this option.
1076 pattern
=> '/dev/parport\d+|/dev/usb/lp\d+',
1077 description
=> "Map host parallel devices (n is 0 to 2).",
1078 verbose_description
=> <<EODESCR,
1079 Map host parallel devices (n is 0 to 2).
1081 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1082 machines - use with special care.
1084 CAUTION: Experimental! User reported problems with this option.
1088 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1089 $confdesc->{"parallel$i"} = $paralleldesc;
1092 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1093 $confdesc->{"serial$i"} = $serialdesc;
1096 for (my $i = 0; $i < $PVE::QemuServer
::PCI
::MAX_HOSTPCI_DEVICES
; $i++) {
1097 $confdesc->{"hostpci$i"} = $PVE::QemuServer
::PCI
::hostpcidesc
;
1100 for my $key (keys %{$PVE::QemuServer
::Drive
::drivedesc_hash
}) {
1101 $confdesc->{$key} = $PVE::QemuServer
::Drive
::drivedesc_hash-
>{$key};
1104 for (my $i = 0; $i < $PVE::QemuServer
::USB
::MAX_USB_DEVICES
; $i++) {
1105 $confdesc->{"usb$i"} = $PVE::QemuServer
::USB
::usbdesc
;
1113 description
=> "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)."
1114 . " Deprecated, use 'order=' instead.",
1115 pattern
=> '[acdn]{1,4}',
1116 format_description
=> "[acdn]{1,4}",
1118 # note: this is also the fallback if boot: is not given at all
1124 format
=> 'pve-qm-bootdev-list',
1125 format_description
=> "device[;device...]",
1126 description
=> <<EODESC,
1127 The guest will attempt to boot from devices in the order they appear here.
1129 Disks, optical drives and passed-through storage USB devices will be directly
1130 booted from, NICs will load PXE, and PCIe devices will either behave like disks
1131 (e.g. NVMe) or load an option ROM (e.g. RAID controller, hardware NIC).
1133 Note that only devices in this list will be marked as bootable and thus loaded
1134 by the guest firmware (BIOS/UEFI). If you require multiple disks for booting
1135 (e.g. software-raid), you need to specify all of them here.
1137 Overrides the deprecated 'legacy=[acdn]*' value when given.
1141 PVE
::JSONSchema
::register_format
('pve-qm-boot', $boot_fmt);
1143 PVE
::JSONSchema
::register_format
('pve-qm-bootdev', \
&verify_bootdev
);
1144 sub verify_bootdev
{
1145 my ($dev, $noerr) = @_;
1147 my $special = $dev =~ m/^efidisk/ || $dev =~ m/^tpmstate/;
1148 return $dev if PVE
::QemuServer
::Drive
::is_valid_drivename
($dev) && !$special;
1152 return 0 if $dev !~ m/^$base\d+$/;
1153 return 0 if !$confdesc->{$dev};
1157 return $dev if $check->("net");
1158 return $dev if $check->("usb");
1159 return $dev if $check->("hostpci");
1162 die "invalid boot device '$dev'\n";
1165 sub print_bootorder
{
1167 return "" if !@$devs;
1168 my $data = { order
=> join(';', @$devs) };
1169 return PVE
::JSONSchema
::print_property_string
($data, $boot_fmt);
1172 my $kvm_api_version = 0;
1175 return $kvm_api_version if $kvm_api_version;
1177 open my $fh, '<', '/dev/kvm' or return;
1179 # 0xae00 => KVM_GET_API_VERSION
1180 $kvm_api_version = ioctl($fh, 0xae00, 0);
1183 return $kvm_api_version;
1186 my $kvm_user_version = {};
1189 sub kvm_user_version
{
1192 $binary //= get_command_for_arch
(get_host_arch
()); # get the native arch by default
1193 my $st = stat($binary);
1195 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1196 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1197 $cachedmtime == $st->mtime;
1199 $kvm_user_version->{$binary} = 'unknown';
1200 $kvm_mtime->{$binary} = $st->mtime;
1204 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1205 $kvm_user_version->{$binary} = $2;
1209 eval { run_command
([$binary, '--version'], outfunc
=> $code); };
1212 return $kvm_user_version->{$binary};
1215 my sub extract_version
{
1216 my ($machine_type, $version) = @_;
1217 $version = kvm_user_version
() if !defined($version);
1218 return PVE
::QemuServer
::Machine
::extract_version
($machine_type, $version)
1221 sub kernel_has_vhost_net
{
1222 return -c
'/dev/vhost-net';
1227 return defined($confdesc->{$key});
1231 sub get_cdrom_path
{
1233 return $cdrom_path if defined($cdrom_path);
1235 $cdrom_path = first
{ -l
$_ } map { "/dev/cdrom$_" } ('', '1', '2');
1237 if (!defined($cdrom_path)) {
1238 log_warn
("no physical CD-ROM available, ignoring");
1246 my ($storecfg, $vmid, $cdrom) = @_;
1248 if ($cdrom eq 'cdrom') {
1249 return get_cdrom_path
();
1250 } elsif ($cdrom eq 'none') {
1252 } elsif ($cdrom =~ m
|^/|) {
1255 return PVE
::Storage
::path
($storecfg, $cdrom);
1259 # try to convert old style file names to volume IDs
1260 sub filename_to_volume_id
{
1261 my ($vmid, $file, $media) = @_;
1263 if (!($file eq 'none' || $file eq 'cdrom' ||
1264 $file =~ m
|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1266 return if $file =~ m
|/|;
1268 if ($media && $media eq 'cdrom') {
1269 $file = "local:iso/$file";
1271 $file = "local:$vmid/$file";
1278 sub verify_media_type
{
1279 my ($opt, $vtype, $media) = @_;
1284 if ($media eq 'disk') {
1286 } elsif ($media eq 'cdrom') {
1289 die "internal error";
1292 return if ($vtype eq $etype);
1294 raise_param_exc
({ $opt => "unexpected media type ($vtype != $etype)" });
1297 sub cleanup_drive_path
{
1298 my ($opt, $storecfg, $drive) = @_;
1300 # try to convert filesystem paths to volume IDs
1302 if (($drive->{file
} !~ m/^(cdrom|none)$/) &&
1303 ($drive->{file
} !~ m
|^/dev/.+|) &&
1304 ($drive->{file
} !~ m/^([^:]+):(.+)$/) &&
1305 ($drive->{file
} !~ m/^\d+$/)) {
1306 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($storecfg, $drive->{file
});
1307 raise_param_exc
({ $opt => "unable to associate path '$drive->{file}' to any storage"})
1309 $drive->{media
} = 'cdrom' if !$drive->{media
} && $vtype eq 'iso';
1310 verify_media_type
($opt, $vtype, $drive->{media
});
1311 $drive->{file
} = $volid;
1314 $drive->{media
} = 'cdrom' if !$drive->{media
} && $drive->{file
} =~ m/^(cdrom|none)$/;
1317 sub parse_hotplug_features
{
1322 return $res if $data eq '0';
1324 $data = $confdesc->{hotplug
}->{default} if $data eq '1';
1326 foreach my $feature (PVE
::Tools
::split_list
($data)) {
1327 if ($feature =~ m/^(network|disk|cpu|memory|usb|cloudinit)$/) {
1330 die "invalid hotplug feature '$feature'\n";
1336 PVE
::JSONSchema
::register_format
('pve-hotplug-features', \
&pve_verify_hotplug_features
);
1337 sub pve_verify_hotplug_features
{
1338 my ($value, $noerr) = @_;
1340 return $value if parse_hotplug_features
($value);
1344 die "unable to parse hotplug option\n";
1347 sub assert_clipboard_config
{
1350 my $clipboard_regex = qr/^(std|cirrus|vmware|virtio|qxl)/;
1354 && $vga->{'clipboard'} eq 'vnc'
1356 && $vga->{type
} !~ $clipboard_regex
1358 die "vga type $vga->{type} is not compatible with VNC clipboard\n";
1362 sub print_tabletdevice_full
{
1363 my ($conf, $arch) = @_;
1365 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1367 # we use uhci for old VMs because tablet driver was buggy in older qemu
1369 if ($q35 || $arch eq 'aarch64') {
1375 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1378 sub print_keyboarddevice_full
{
1379 my ($conf, $arch) = @_;
1381 return if $arch ne 'aarch64';
1383 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1386 my sub get_drive_id
{
1388 return "$drive->{interface}$drive->{index}";
1391 sub print_drivedevice_full
{
1392 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1397 my $drive_id = get_drive_id
($drive);
1398 if ($drive->{interface
} eq 'virtio') {
1399 my $pciaddr = print_pci_addr
("$drive_id", $bridges, $arch, $machine_type);
1400 $device = "virtio-blk-pci,drive=drive-$drive_id,id=${drive_id}${pciaddr}";
1401 $device .= ",iothread=iothread-$drive_id" if $drive->{iothread
};
1402 } elsif ($drive->{interface
} eq 'scsi') {
1404 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
1405 my $unit = $drive->{index} % $maxdev;
1407 my $machine_version = extract_version
($machine_type, kvm_user_version
());
1408 my $device_type = PVE
::QemuServer
::Drive
::get_scsi_device_type
(
1409 $drive, $storecfg, $machine_version);
1411 if (!$conf->{scsihw
} || $conf->{scsihw
} =~ m/^lsi/ || $conf->{scsihw
} eq 'pvscsi') {
1412 $device = "scsi-$device_type,bus=$controller_prefix$controller.0,scsi-id=$unit";
1414 $device = "scsi-$device_type,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
1415 .",lun=$drive->{index}";
1417 $device .= ",drive=drive-$drive_id,id=$drive_id";
1419 if ($drive->{ssd
} && ($device_type eq 'block' || $device_type eq 'hd')) {
1420 $device .= ",rotation_rate=1";
1422 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1424 # only scsi-hd and scsi-cd support passing vendor and product information
1425 if ($device_type eq 'hd' || $device_type eq 'cd') {
1426 if (my $vendor = $drive->{vendor
}) {
1427 $device .= ",vendor=$vendor";
1429 if (my $product = $drive->{product
}) {
1430 $device .= ",product=$product";
1434 } elsif ($drive->{interface
} eq 'ide' || $drive->{interface
} eq 'sata') {
1435 my $maxdev = ($drive->{interface
} eq 'sata') ?
$PVE::QemuServer
::Drive
::MAX_SATA_DISKS
: 2;
1436 my $controller = int($drive->{index} / $maxdev);
1437 my $unit = $drive->{index} % $maxdev;
1439 # machine type q35 only supports unit=0 for IDE rather than 2 units. This wasn't handled
1440 # correctly before, so e.g. index=2 was mapped to controller=1,unit=0 rather than
1441 # controller=2,unit=0. Note that odd indices never worked, as they would be mapped to
1442 # unit=1, so to keep backwards compat for migration, it suffices to keep even ones as they
1443 # were before. Move odd ones up by 2 where they don't clash.
1444 if (PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf) && $drive->{interface
} eq 'ide') {
1445 $controller += 2 * ($unit % 2);
1449 my $device_type = ($drive->{media
} && $drive->{media
} eq 'cdrom') ?
"cd" : "hd";
1451 $device = "ide-$device_type";
1452 if ($drive->{interface
} eq 'ide') {
1453 $device .= ",bus=ide.$controller,unit=$unit";
1455 $device .= ",bus=ahci$controller.$unit";
1457 $device .= ",drive=drive-$drive_id,id=$drive_id";
1459 if ($device_type eq 'hd') {
1460 if (my $model = $drive->{model
}) {
1461 $model = URI
::Escape
::uri_unescape
($model);
1462 $device .= ",model=$model";
1464 if ($drive->{ssd
}) {
1465 $device .= ",rotation_rate=1";
1468 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1469 } elsif ($drive->{interface
} eq 'usb') {
1471 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1473 die "unsupported interface type";
1476 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex
};
1478 if (my $serial = $drive->{serial
}) {
1479 $serial = URI
::Escape
::uri_unescape
($serial);
1480 $device .= ",serial=$serial";
1487 sub get_initiator_name
{
1490 my $fh = IO
::File-
>new('/etc/iscsi/initiatorname.iscsi') || return;
1491 while (defined(my $line = <$fh>)) {
1492 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1501 my sub storage_allows_io_uring_default
{
1502 my ($scfg, $cache_direct) = @_;
1504 # io_uring with cache mode writeback or writethrough on krbd will hang...
1505 return if $scfg && $scfg->{type
} eq 'rbd' && $scfg->{krbd
} && !$cache_direct;
1507 # io_uring with cache mode writeback or writethrough on LVM will hang, without cache only
1508 # sometimes, just plain disable...
1509 return if $scfg && $scfg->{type
} eq 'lvm';
1511 # io_uring causes problems when used with CIFS since kernel 5.15
1512 # Some discussion: https://www.spinics.net/lists/linux-cifs/msg26734.html
1513 return if $scfg && $scfg->{type
} eq 'cifs';
1518 my sub drive_uses_cache_direct
{
1519 my ($drive, $scfg) = @_;
1521 my $cache_direct = 0;
1523 if (my $cache = $drive->{cache
}) {
1524 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1525 } elsif (!drive_is_cdrom
($drive) && !($scfg && $scfg->{type
} eq 'btrfs' && !$scfg->{nocow
})) {
1529 return $cache_direct;
1532 sub print_drive_commandline_full
{
1533 my ($storecfg, $vmid, $drive, $live_restore_name, $io_uring) = @_;
1536 my $volid = $drive->{file
};
1537 my $format = $drive->{format
};
1538 my $drive_id = get_drive_id
($drive);
1540 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1541 my $scfg = $storeid ? PVE
::Storage
::storage_config
($storecfg, $storeid) : undef;
1543 if (drive_is_cdrom
($drive)) {
1544 $path = get_iso_path
($storecfg, $vmid, $volid);
1545 die "$drive_id: cannot back cdrom drive with a live restore image\n" if $live_restore_name;
1548 $path = PVE
::Storage
::path
($storecfg, $volid);
1549 $format //= qemu_img_format
($scfg, $volname);
1556 my $is_rbd = $path =~ m/^rbd:/;
1559 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1560 foreach my $o (@qemu_drive_options) {
1561 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1564 # snapshot only accepts on|off
1565 if (defined($drive->{snapshot
})) {
1566 my $v = $drive->{snapshot
} ?
'on' : 'off';
1567 $opts .= ",snapshot=$v";
1570 if (defined($drive->{ro
})) { # ro maps to QEMUs `readonly`, which accepts `on` or `off` only
1571 $opts .= ",readonly=" . ($drive->{ro
} ?
'on' : 'off');
1574 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1575 my ($dir, $qmpname) = @$type;
1576 if (my $v = $drive->{"mbps$dir"}) {
1577 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1579 if (my $v = $drive->{"mbps${dir}_max"}) {
1580 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1582 if (my $v = $drive->{"bps${dir}_max_length"}) {
1583 $opts .= ",throttling.bps$qmpname-max-length=$v";
1585 if (my $v = $drive->{"iops${dir}"}) {
1586 $opts .= ",throttling.iops$qmpname=$v";
1588 if (my $v = $drive->{"iops${dir}_max"}) {
1589 $opts .= ",throttling.iops$qmpname-max=$v";
1591 if (my $v = $drive->{"iops${dir}_max_length"}) {
1592 $opts .= ",throttling.iops$qmpname-max-length=$v";
1596 if ($live_restore_name) {
1597 $format = "rbd" if $is_rbd;
1598 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1600 $opts .= ",format=alloc-track,file.driver=$format";
1602 $opts .= ",format=$format";
1605 my $cache_direct = drive_uses_cache_direct
($drive, $scfg);
1607 $opts .= ",cache=none" if !$drive->{cache
} && $cache_direct;
1609 if (!$drive->{aio
}) {
1610 if ($io_uring && storage_allows_io_uring_default
($scfg, $cache_direct)) {
1611 # io_uring supports all cache modes
1612 $opts .= ",aio=io_uring";
1614 # aio native works only with O_DIRECT
1616 $opts .= ",aio=native";
1618 $opts .= ",aio=threads";
1623 if (!drive_is_cdrom
($drive)) {
1625 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
1626 $detectzeroes = 'off';
1627 } elsif ($drive->{discard
}) {
1628 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
1630 # This used to be our default with discard not being specified:
1631 $detectzeroes = 'on';
1634 # note: 'detect-zeroes' works per blockdev and we want it to persist
1635 # after the alloc-track is removed, so put it on 'file' directly
1636 my $dz_param = $live_restore_name ?
"file.detect-zeroes" : "detect-zeroes";
1637 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
1640 if ($live_restore_name) {
1641 $opts .= ",backing=$live_restore_name";
1642 $opts .= ",auto-remove=on";
1645 # my $file_param = $live_restore_name ? "file.file.filename" : "file";
1646 my $file_param = "file";
1647 if ($live_restore_name) {
1648 # non-rbd drivers require the underlying file to be a seperate block
1649 # node, so add a second .file indirection
1650 $file_param .= ".file" if !$is_rbd;
1651 $file_param .= ".filename";
1653 my $pathinfo = $path ?
"$file_param=$path," : '';
1655 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1658 sub print_pbs_blockdev
{
1659 my ($pbs_conf, $pbs_name) = @_;
1660 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1661 $blockdev .= ",repository=$pbs_conf->{repository}";
1662 $blockdev .= ",namespace=$pbs_conf->{namespace}" if $pbs_conf->{namespace
};
1663 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1664 $blockdev .= ",archive=$pbs_conf->{archive}";
1665 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile
};
1669 sub print_netdevice_full
{
1670 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version) = @_;
1672 my $device = $net->{model
};
1673 if ($net->{model
} eq 'virtio') {
1674 $device = 'virtio-net-pci';
1677 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
1678 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
1679 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
1680 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1681 # and out of each queue plus one config interrupt and control vector queue
1682 my $vectors = $net->{queues
} * 2 + 2;
1683 $tmpstr .= ",vectors=$vectors,mq=on";
1684 if (min_version
($machine_version, 7, 1)) {
1685 $tmpstr .= ",packed=on";
1689 if (min_version
($machine_version, 7, 1) && $net->{model
} eq 'virtio'){
1690 $tmpstr .= ",rx_queue_size=1024,tx_queue_size=256";
1693 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
1695 if (my $mtu = $net->{mtu
}) {
1696 if ($net->{model
} eq 'virtio' && $net->{bridge
}) {
1697 my $bridge_mtu = PVE
::Network
::read_bridge_mtu
($net->{bridge
});
1700 } elsif ($mtu < 576) {
1701 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1702 } elsif ($mtu > $bridge_mtu) {
1703 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1705 $tmpstr .= ",host_mtu=$mtu";
1707 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
1711 if ($use_old_bios_files) {
1713 if ($device eq 'virtio-net-pci') {
1714 $romfile = 'pxe-virtio.rom';
1715 } elsif ($device eq 'e1000') {
1716 $romfile = 'pxe-e1000.rom';
1717 } elsif ($device eq 'e1000e') {
1718 $romfile = 'pxe-e1000e.rom';
1719 } elsif ($device eq 'ne2k') {
1720 $romfile = 'pxe-ne2k_pci.rom';
1721 } elsif ($device eq 'pcnet') {
1722 $romfile = 'pxe-pcnet.rom';
1723 } elsif ($device eq 'rtl8139') {
1724 $romfile = 'pxe-rtl8139.rom';
1726 $tmpstr .= ",romfile=$romfile" if $romfile;
1732 sub print_netdev_full
{
1733 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
1736 if ($netid =~ m/^net(\d+)$/) {
1740 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
1742 my $ifname = "tap${vmid}i$i";
1744 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1745 die "interface name '$ifname' is too long (max 15 character)\n"
1746 if length($ifname) >= 16;
1748 my $vhostparam = '';
1749 if (is_native_arch
($arch)) {
1750 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
1753 my $vmname = $conf->{name
} || "vm$vmid";
1756 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
1758 if ($net->{bridge
}) {
1759 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1760 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
1762 $netdev = "type=user,id=$netid,hostname=$vmname";
1765 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
1771 'cirrus' => 'cirrus-vga',
1773 'vmware' => 'vmware-svga',
1774 'virtio' => 'virtio-vga',
1775 'virtio-gl' => 'virtio-vga-gl',
1778 sub print_vga_device
{
1779 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
1781 my $type = $vga_map->{$vga->{type
}};
1782 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
1783 $type = 'virtio-gpu';
1785 my $vgamem_mb = $vga->{memory
};
1787 my $max_outputs = '';
1789 $type = $id ?
'qxl' : 'qxl-vga';
1791 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
1792 # set max outputs so linux can have up to 4 qxl displays with one device
1793 if (min_version
($machine_version, 4, 1)) {
1794 $max_outputs = ",max_outputs=4";
1799 die "no device-type for $vga->{type}\n" if !$type;
1803 if ($vga->{type
} =~ /^virtio/) {
1804 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
1805 $memory = ",max_hostmem=$bytes";
1807 # from https://www.spice-space.org/multiple-monitors.html
1808 $memory = ",vgamem_mb=$vga->{memory}";
1809 my $ram = $vgamem_mb * 4;
1810 my $vram = $vgamem_mb * 2;
1811 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1813 $memory = ",vgamem_mb=$vga->{memory}";
1815 } elsif ($qxlnum && $id) {
1816 $memory = ",ram_size=67108864,vram_size=33554432";
1820 if ($type eq 'VGA' && windows_version
($conf->{ostype
})) {
1821 $edidoff=",edid=off" if (!defined($conf->{bios
}) || $conf->{bios
} ne 'ovmf');
1824 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1825 my $vgaid = "vga" . ($id // '');
1827 if ($q35 && $vgaid eq 'vga') {
1828 # the first display uses pcie.0 bus on q35 machines
1829 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
1831 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
1834 if ($vga->{type
} eq 'virtio-gl') {
1835 my $base = '/usr/lib/x86_64-linux-gnu/lib';
1836 die "missing libraries for '$vga->{type}' detected! Please install 'libgl1' and 'libegl1'\n"
1837 if !-e
"${base}EGL.so.1" || !-e
"${base}GL.so.1";
1839 die "no DRM render node detected (/dev/dri/renderD*), no GPU? - needed for '$vga->{type}' display\n"
1840 if !PVE
::Tools
::dir_glob_regex
('/dev/dri/', "renderD.*");
1843 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
1846 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1848 my ($data, $disable_mac_autogen) = @_;
1850 my $res = eval { parse_property_string
($net_fmt, $data) };
1855 if (!defined($res->{macaddr
}) && !$disable_mac_autogen) {
1856 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1857 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1862 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1863 sub parse_ipconfig
{
1866 my $res = eval { parse_property_string
($ipconfig_fmt, $data) };
1872 if ($res->{gw
} && !$res->{ip
}) {
1873 warn 'gateway specified without specifying an IP address';
1876 if ($res->{gw6
} && !$res->{ip6
}) {
1877 warn 'IPv6 gateway specified without specifying an IPv6 address';
1880 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
1881 warn 'gateway specified together with DHCP';
1884 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
1886 warn "IPv6 gateway specified together with $res->{ip6} address";
1890 if (!$res->{ip
} && !$res->{ip6
}) {
1891 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
1900 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
1903 sub add_random_macs
{
1904 my ($settings) = @_;
1906 foreach my $opt (keys %$settings) {
1907 next if $opt !~ m/^net(\d+)$/;
1908 my $net = parse_net
($settings->{$opt});
1910 $settings->{$opt} = print_net
($net);
1914 sub vm_is_volid_owner
{
1915 my ($storecfg, $vmid, $volid) = @_;
1917 if ($volid !~ m
|^/|) {
1919 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
1920 if ($owner && ($owner == $vmid)) {
1928 sub vmconfig_register_unused_drive
{
1929 my ($storecfg, $vmid, $conf, $drive) = @_;
1931 if (drive_is_cloudinit
($drive)) {
1932 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
1934 delete $conf->{cloudinit
};
1935 } elsif (!drive_is_cdrom
($drive)) {
1936 my $volid = $drive->{file
};
1937 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
1938 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
1943 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
1947 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
1948 format_description
=> 'UUID',
1949 description
=> "Set SMBIOS1 UUID.",
1954 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1955 format_description
=> 'Base64 encoded string',
1956 description
=> "Set SMBIOS1 version.",
1961 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1962 format_description
=> 'Base64 encoded string',
1963 description
=> "Set SMBIOS1 serial number.",
1968 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1969 format_description
=> 'Base64 encoded string',
1970 description
=> "Set SMBIOS1 manufacturer.",
1975 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1976 format_description
=> 'Base64 encoded string',
1977 description
=> "Set SMBIOS1 product ID.",
1982 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1983 format_description
=> 'Base64 encoded string',
1984 description
=> "Set SMBIOS1 SKU string.",
1989 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1990 format_description
=> 'Base64 encoded string',
1991 description
=> "Set SMBIOS1 family string.",
1996 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
2004 my $res = eval { parse_property_string
($smbios1_fmt, $data) };
2011 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2014 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2016 sub parse_watchdog
{
2021 my $res = eval { parse_property_string
($watchdog_fmt, $value) };
2026 sub parse_guest_agent
{
2029 return {} if !defined($conf->{agent
});
2031 my $res = eval { parse_property_string
($agent_fmt, $conf->{agent
}) };
2034 # if the agent is disabled ignore the other potentially set properties
2035 return {} if !$res->{enabled
};
2040 my ($conf, $key) = @_;
2041 return undef if !defined($conf->{agent
});
2043 my $agent = parse_guest_agent
($conf);
2044 return $agent->{$key};
2050 return {} if !$value;
2051 my $res = eval { parse_property_string
($vga_fmt, $value) };
2061 my $res = eval { parse_property_string
($rng_fmt, $value) };
2066 sub parse_meta_info
{
2071 my $res = eval { parse_property_string
($meta_info_fmt, $value) };
2076 sub new_meta_info_string
{
2077 my () = @_; # for now do not allow to override any value
2079 return PVE
::JSONSchema
::print_property_string
(
2081 'creation-qemu' => kvm_user_version
(),
2082 ctime
=> "". int(time()),
2088 sub qemu_created_version_fixups
{
2089 my ($conf, $forcemachine, $kvmver) = @_;
2091 my $meta = parse_meta_info
($conf->{meta
}) // {};
2092 my $forced_vers = PVE
::QemuServer
::Machine
::extract_version
($forcemachine);
2094 # check if we need to apply some handling for VMs that always use the latest machine version but
2095 # had a machine version transition happen that affected HW such that, e.g., an OS config change
2096 # would be required (we do not want to pin machine version for non-windows OS type)
2097 my $machine_conf = PVE
::QemuServer
::Machine
::parse_machine
($conf->{machine
});
2099 (!defined($machine_conf->{type
}) || $machine_conf->{type
} =~ m/^(?:pc|q35|virt)$/) # non-versioned machine
2100 && (!defined($meta->{'creation-qemu'}) || !min_version
($meta->{'creation-qemu'}, 6, 1)) # created before 6.1
2101 && (!$forced_vers || min_version
($forced_vers, 6, 1)) # handle snapshot-rollback/migrations
2102 && min_version
($kvmver, 6, 1) # only need to apply the change since 6.1
2104 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
2105 if ($q35 && $conf->{ostype
} && $conf->{ostype
} eq 'l26') {
2106 # this changed to default-on in Q 6.1 for q35 machines, it will mess with PCI slot view
2107 # and thus with the predictable interface naming of systemd
2108 return ['-global', 'ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off'];
2114 # add JSON properties for create and set function
2115 sub json_config_properties
{
2116 my ($prop, $with_disk_alloc) = @_;
2118 my $skip_json_config_opts = {
2122 runningmachine
=> 1,
2127 foreach my $opt (keys %$confdesc) {
2128 next if $skip_json_config_opts->{$opt};
2130 if ($with_disk_alloc && is_valid_drivename
($opt)) {
2131 $prop->{$opt} = $PVE::QemuServer
::Drive
::drivedesc_hash_with_alloc-
>{$opt};
2133 $prop->{$opt} = $confdesc->{$opt};
2140 # Properties that we can read from an OVF file
2141 sub json_ovf_properties
{
2144 for my $device (PVE
::QemuServer
::Drive
::valid_drive_names
()) {
2145 $prop->{$device} = {
2147 format
=> 'pve-volume-id-or-absolute-path',
2148 description
=> "Disk image that gets imported to $device",
2155 description
=> "The number of CPU cores.",
2160 description
=> "Amount of RAM for the VM in MB.",
2165 description
=> "Name of the VM.",
2172 # return copy of $confdesc_cloudinit to generate documentation
2173 sub cloudinit_config_properties
{
2175 return dclone
($confdesc_cloudinit);
2178 sub cloudinit_pending_properties
{
2180 map { $_ => 1 } keys $confdesc_cloudinit->%*,
2183 $p->{"net$_"} = 1 for 0..($MAX_NETS-1);
2188 my ($key, $value) = @_;
2190 die "unknown setting '$key'\n" if !$confdesc->{$key};
2192 my $type = $confdesc->{$key}->{type
};
2194 if (!defined($value)) {
2195 die "got undefined value\n";
2198 if ($value =~ m/[\n\r]/) {
2199 die "property contains a line feed\n";
2202 if ($type eq 'boolean') {
2203 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2204 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2205 die "type check ('boolean') failed - got '$value'\n";
2206 } elsif ($type eq 'integer') {
2207 return int($1) if $value =~ m/^(\d+)$/;
2208 die "type check ('integer') failed - got '$value'\n";
2209 } elsif ($type eq 'number') {
2210 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2211 die "type check ('number') failed - got '$value'\n";
2212 } elsif ($type eq 'string') {
2213 if (my $fmt = $confdesc->{$key}->{format
}) {
2214 PVE
::JSONSchema
::check_format
($fmt, $value);
2217 $value =~ s/^\"(.*)\"$/$1/;
2220 die "internal error"
2225 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
2227 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2229 if (!$skiplock && !PVE
::QemuConfig-
>has_lock($conf, 'suspended')) {
2230 PVE
::QemuConfig-
>check_lock($conf);
2233 if ($conf->{template
}) {
2234 # check if any base image is still used by a linked clone
2235 PVE
::QemuConfig-
>foreach_volume_full($conf, { include_unused
=> 1 }, sub {
2236 my ($ds, $drive) = @_;
2237 return if drive_is_cdrom
($drive);
2239 my $volid = $drive->{file
};
2240 return if !$volid || $volid =~ m
|^/|;
2242 die "base volume '$volid' is still in use by linked cloned\n"
2243 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2249 my $remove_owned_drive = sub {
2250 my ($ds, $drive) = @_;
2251 return if drive_is_cdrom
($drive, 1);
2253 my $volid = $drive->{file
};
2254 return if !$volid || $volid =~ m
|^/|;
2255 return if $volids->{$volid};
2257 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2258 return if !$path || !$owner || ($owner != $vmid);
2260 $volids->{$volid} = 1;
2261 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2262 warn "Could not remove disk '$volid', check manually: $@" if $@;
2265 # only remove disks owned by this VM (referenced in the config)
2266 my $include_opts = {
2267 include_unused
=> 1,
2268 extra_keys
=> ['vmstate'],
2270 PVE
::QemuConfig-
>foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2272 for my $snap (values %{$conf->{snapshots
}}) {
2273 next if !defined($snap->{vmstate
});
2274 my $drive = PVE
::QemuConfig-
>parse_volume('vmstate', $snap->{vmstate
}, 1);
2275 next if !defined($drive);
2276 $remove_owned_drive->('vmstate', $drive);
2279 PVE
::QemuConfig-
>foreach_volume_full($conf->{pending
}, $include_opts, $remove_owned_drive);
2281 if ($purge_unreferenced) { # also remove unreferenced disk
2282 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid, undef, 'images');
2283 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2284 my ($volid, $sid, $volname, $d) = @_;
2285 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2290 eval { delete_ifaces_ipams_ips
($conf, $vmid)};
2293 if (defined $replacement_conf) {
2294 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2296 PVE
::QemuConfig-
>destroy_config($vmid);
2300 sub parse_vm_config
{
2301 my ($filename, $raw, $strict) = @_;
2303 return if !defined($raw);
2306 digest
=> Digest
::SHA
::sha1_hex
($raw),
2312 my $handle_error = sub {
2322 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2323 || die "got strange filename '$filename'";
2329 my $finish_description = sub {
2330 if (defined($descr)) {
2332 $conf->{description
} = $descr;
2338 my @lines = split(/\n/, $raw);
2339 foreach my $line (@lines) {
2340 next if $line =~ m/^\s*$/;
2342 if ($line =~ m/^\[PENDING\]\s*$/i) {
2343 $section = 'pending';
2344 $finish_description->();
2345 $conf = $res->{$section} = {};
2347 } elsif ($line =~ m/^\[special:cloudinit\]\s*$/i) {
2348 $section = 'cloudinit';
2349 $finish_description->();
2350 $conf = $res->{$section} = {};
2353 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2355 $finish_description->();
2356 $conf = $res->{snapshots
}->{$section} = {};
2360 if ($line =~ m/^\#(.*)$/) {
2361 $descr = '' if !defined($descr);
2362 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2366 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2367 $descr = '' if !defined($descr);
2368 $descr .= PVE
::Tools
::decode_text
($2);
2369 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2370 $conf->{snapstate
} = $1;
2371 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2374 $conf->{$key} = $value;
2375 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2377 if ($section eq 'pending') {
2378 $conf->{delete} = $value; # we parse this later
2380 $handle_error->("vm $vmid - property 'delete' is only allowed in [PENDING]\n");
2382 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2385 if ($section eq 'cloudinit') {
2386 # ignore validation only used for informative purpose
2387 $conf->{$key} = $value;
2390 eval { $value = check_type
($key, $value); };
2392 $handle_error->("vm $vmid - unable to parse value of '$key' - $@");
2394 $key = 'ide2' if $key eq 'cdrom';
2395 my $fmt = $confdesc->{$key}->{format
};
2396 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2397 my $v = parse_drive
($key, $value);
2398 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2399 $v->{file
} = $volid;
2400 $value = print_drive
($v);
2402 $handle_error->("vm $vmid - unable to parse value of '$key'\n");
2407 $conf->{$key} = $value;
2410 $handle_error->("vm $vmid - unable to parse config: $line\n");
2414 $finish_description->();
2415 delete $res->{snapstate
}; # just to be sure
2420 sub write_vm_config
{
2421 my ($filename, $conf) = @_;
2423 delete $conf->{snapstate
}; # just to be sure
2425 if ($conf->{cdrom
}) {
2426 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2427 $conf->{ide2
} = $conf->{cdrom
};
2428 delete $conf->{cdrom
};
2431 # we do not use 'smp' any longer
2432 if ($conf->{sockets
}) {
2433 delete $conf->{smp
};
2434 } elsif ($conf->{smp
}) {
2435 $conf->{sockets
} = $conf->{smp
};
2436 delete $conf->{cores
};
2437 delete $conf->{smp
};
2440 my $used_volids = {};
2442 my $cleanup_config = sub {
2443 my ($cref, $pending, $snapname) = @_;
2445 foreach my $key (keys %$cref) {
2446 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2447 $key eq 'snapstate' || $key eq 'pending' || $key eq 'cloudinit';
2448 my $value = $cref->{$key};
2449 if ($key eq 'delete') {
2450 die "propertry 'delete' is only allowed in [PENDING]\n"
2452 # fixme: check syntax?
2455 eval { $value = check_type
($key, $value); };
2456 die "unable to parse value of '$key' - $@" if $@;
2458 $cref->{$key} = $value;
2460 if (!$snapname && is_valid_drivename
($key)) {
2461 my $drive = parse_drive
($key, $value);
2462 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2467 &$cleanup_config($conf);
2469 &$cleanup_config($conf->{pending
}, 1);
2471 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2472 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2473 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2476 # remove 'unusedX' settings if we re-add a volume
2477 foreach my $key (keys %$conf) {
2478 my $value = $conf->{$key};
2479 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2480 delete $conf->{$key};
2484 my $generate_raw_config = sub {
2485 my ($conf, $pending) = @_;
2489 # add description as comment to top of file
2490 if (defined(my $descr = $conf->{description
})) {
2492 foreach my $cl (split(/\n/, $descr)) {
2493 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2496 $raw .= "#\n" if $pending;
2500 foreach my $key (sort keys %$conf) {
2501 next if $key =~ /^(digest|description|pending|cloudinit|snapshots)$/;
2502 $raw .= "$key: $conf->{$key}\n";
2507 my $raw = &$generate_raw_config($conf);
2509 if (scalar(keys %{$conf->{pending
}})){
2510 $raw .= "\n[PENDING]\n";
2511 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2514 if (scalar(keys %{$conf->{cloudinit
}}) && PVE
::QemuConfig-
>has_cloudinit($conf)){
2515 $raw .= "\n[special:cloudinit]\n";
2516 $raw .= &$generate_raw_config($conf->{cloudinit
});
2519 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2520 $raw .= "\n[$snapname]\n";
2521 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2531 # we use static defaults from our JSON schema configuration
2532 foreach my $key (keys %$confdesc) {
2533 if (defined(my $default = $confdesc->{$key}->{default})) {
2534 $res->{$key} = $default;
2542 my $vmlist = PVE
::Cluster
::get_vmlist
();
2544 return $res if !$vmlist || !$vmlist->{ids
};
2545 my $ids = $vmlist->{ids
};
2546 my $nodename = nodename
();
2548 foreach my $vmid (keys %$ids) {
2549 my $d = $ids->{$vmid};
2550 next if !$d->{node
} || $d->{node
} ne $nodename;
2551 next if !$d->{type
} || $d->{type
} ne 'qemu';
2552 $res->{$vmid}->{exists} = 1;
2557 # test if VM uses local resources (to prevent migration)
2558 sub check_local_resources
{
2559 my ($conf, $noerr) = @_;
2562 my $mapped_res = [];
2564 my $nodelist = PVE
::Cluster
::get_nodelist
();
2565 my $pci_map = PVE
::Mapping
::PCI
::config
();
2566 my $usb_map = PVE
::Mapping
::USB
::config
();
2568 my $missing_mappings_by_node = { map { $_ => [] } @$nodelist };
2570 my $add_missing_mapping = sub {
2571 my ($type, $key, $id) = @_;
2572 for my $node (@$nodelist) {
2574 if ($type eq 'pci') {
2575 $entry = PVE
::Mapping
::PCI
::get_node_mapping
($pci_map, $id, $node);
2576 } elsif ($type eq 'usb') {
2577 $entry = PVE
::Mapping
::USB
::get_node_mapping
($usb_map, $id, $node);
2579 if (!scalar($entry->@*)) {
2580 push @{$missing_mappings_by_node->{$node}}, $key;
2585 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2586 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2588 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2590 foreach my $k (keys %$conf) {
2591 if ($k =~ m/^usb/) {
2592 my $entry = parse_property_string
('pve-qm-usb', $conf->{$k});
2593 next if $entry->{host
} && $entry->{host
} =~ m/^spice$/i;
2594 if ($entry->{mapping
}) {
2595 $add_missing_mapping->('usb', $k, $entry->{mapping
});
2596 push @$mapped_res, $k;
2599 if ($k =~ m/^hostpci/) {
2600 my $entry = parse_property_string
('pve-qm-hostpci', $conf->{$k});
2601 if ($entry->{mapping
}) {
2602 $add_missing_mapping->('pci', $k, $entry->{mapping
});
2603 push @$mapped_res, $k;
2606 # sockets are safe: they will recreated be on the target side post-migrate
2607 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2608 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2611 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2613 return wantarray ?
(\
@loc_res, $mapped_res, $missing_mappings_by_node) : \
@loc_res;
2616 # check if used storages are available on all nodes (use by migrate)
2617 sub check_storage_availability
{
2618 my ($storecfg, $conf, $node) = @_;
2620 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2621 my ($ds, $drive) = @_;
2623 my $volid = $drive->{file
};
2626 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2629 # check if storage is available on both nodes
2630 my $scfg = PVE
::Storage
::storage_check_enabled
($storecfg, $sid);
2631 PVE
::Storage
::storage_check_enabled
($storecfg, $sid, $node);
2633 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $volid);
2635 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2636 if !$scfg->{content
}->{$vtype};
2640 # list nodes where all VM images are available (used by has_feature API)
2642 my ($conf, $storecfg) = @_;
2644 my $nodelist = PVE
::Cluster
::get_nodelist
();
2645 my $nodehash = { map { $_ => 1 } @$nodelist };
2646 my $nodename = nodename
();
2648 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2649 my ($ds, $drive) = @_;
2651 my $volid = $drive->{file
};
2654 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2656 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2657 if ($scfg->{disable
}) {
2659 } elsif (my $avail = $scfg->{nodes
}) {
2660 foreach my $node (keys %$nodehash) {
2661 delete $nodehash->{$node} if !$avail->{$node};
2663 } elsif (!$scfg->{shared
}) {
2664 foreach my $node (keys %$nodehash) {
2665 delete $nodehash->{$node} if $node ne $nodename
2674 sub check_local_storage_availability
{
2675 my ($conf, $storecfg) = @_;
2677 my $nodelist = PVE
::Cluster
::get_nodelist
();
2678 my $nodehash = { map { $_ => {} } @$nodelist };
2680 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2681 my ($ds, $drive) = @_;
2683 my $volid = $drive->{file
};
2686 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2688 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2690 if ($scfg->{disable
}) {
2691 foreach my $node (keys %$nodehash) {
2692 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2694 } elsif (my $avail = $scfg->{nodes
}) {
2695 foreach my $node (keys %$nodehash) {
2696 if (!$avail->{$node}) {
2697 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2704 foreach my $node (values %$nodehash) {
2705 if (my $unavail = $node->{unavailable_storages
}) {
2706 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2713 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2715 my ($vmid, $nocheck, $node) = @_;
2717 # $nocheck is set when called during a migration, in which case the config
2718 # file might still or already reside on the *other* node
2719 # - because rename has already happened, and current node is source
2720 # - because rename hasn't happened yet, and current node is target
2721 # - because rename has happened, current node is target, but hasn't yet
2723 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2724 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2729 my $vzlist = config_list
();
2731 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2733 while (defined(my $de = $fd->read)) {
2734 next if $de !~ m/^(\d+)\.pid$/;
2736 next if !defined($vzlist->{$vmid});
2737 if (my $pid = check_running
($vmid)) {
2738 $vzlist->{$vmid}->{pid
} = $pid;
2745 our $vmstatus_return_properties = {
2746 vmid
=> get_standard_option
('pve-vmid'),
2748 description
=> "QEMU process status.",
2750 enum
=> ['stopped', 'running'],
2753 description
=> "Maximum memory in bytes.",
2756 renderer
=> 'bytes',
2759 description
=> "Root disk size in bytes.",
2762 renderer
=> 'bytes',
2765 description
=> "VM name.",
2770 description
=> "VM run state from the 'query-status' QMP monitor command.",
2775 description
=> "PID of running qemu process.",
2780 description
=> "Uptime.",
2783 renderer
=> 'duration',
2786 description
=> "Maximum usable CPUs.",
2791 description
=> "The current config lock, if any.",
2796 description
=> "The current configured tags, if any",
2800 'running-machine' => {
2801 description
=> "The currently running machine type (if running).",
2806 description
=> "The currently running QEMU version (if running).",
2812 my $last_proc_pid_stat;
2814 # get VM status information
2815 # This must be fast and should not block ($full == false)
2816 # We only query KVM using QMP if $full == true (this can be slow)
2818 my ($opt_vmid, $full) = @_;
2822 my $storecfg = PVE
::Storage
::config
();
2824 my $list = vzlist
();
2825 my $defaults = load_defaults
();
2827 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
(1);
2829 my $cpucount = $cpuinfo->{cpus
} || 1;
2831 foreach my $vmid (keys %$list) {
2832 next if $opt_vmid && ($vmid ne $opt_vmid);
2834 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2836 my $d = { vmid
=> int($vmid) };
2837 $d->{pid
} = int($list->{$vmid}->{pid
}) if $list->{$vmid}->{pid
};
2839 # fixme: better status?
2840 $d->{status
} = $list->{$vmid}->{pid
} ?
'running' : 'stopped';
2842 my $size = PVE
::QemuServer
::Drive
::bootdisk_size
($storecfg, $conf);
2843 if (defined($size)) {
2844 $d->{disk
} = 0; # no info available
2845 $d->{maxdisk
} = $size;
2851 $d->{cpus
} = ($conf->{sockets
} || $defaults->{sockets
})
2852 * ($conf->{cores
} || $defaults->{cores
});
2853 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
2854 $d->{cpus
} = $conf->{vcpus
} if $conf->{vcpus
};
2856 $d->{name
} = $conf->{name
} || "VM $vmid";
2857 $d->{maxmem
} = get_current_memory
($conf->{memory
})*(1024*1024);
2859 if ($conf->{balloon
}) {
2860 $d->{balloon_min
} = $conf->{balloon
}*(1024*1024);
2861 $d->{shares
} = defined($conf->{shares
}) ?
$conf->{shares
}
2862 : $defaults->{shares
};
2873 $d->{diskwrite
} = 0;
2875 $d->{template
} = 1 if PVE
::QemuConfig-
>is_template($conf);
2877 $d->{serial
} = 1 if conf_has_serial
($conf);
2878 $d->{lock} = $conf->{lock} if $conf->{lock};
2879 $d->{tags
} = $conf->{tags
} if defined($conf->{tags
});
2884 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
2885 foreach my $dev (keys %$netdev) {
2886 next if $dev !~ m/^tap([1-9]\d*)i/;
2888 my $d = $res->{$vmid};
2891 $d->{netout
} += $netdev->{$dev}->{receive
};
2892 $d->{netin
} += $netdev->{$dev}->{transmit
};
2895 $d->{nics
}->{$dev}->{netout
} = int($netdev->{$dev}->{receive
});
2896 $d->{nics
}->{$dev}->{netin
} = int($netdev->{$dev}->{transmit
});
2901 my $ctime = gettimeofday
;
2903 foreach my $vmid (keys %$list) {
2905 my $d = $res->{$vmid};
2906 my $pid = $d->{pid
};
2909 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
2910 next if !$pstat; # not running
2912 my $used = $pstat->{utime} + $pstat->{stime
};
2914 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
2916 if ($pstat->{vsize
}) {
2917 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
2920 my $old = $last_proc_pid_stat->{$pid};
2922 $last_proc_pid_stat->{$pid} = {
2930 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
2932 if ($dtime > 1000) {
2933 my $dutime = $used - $old->{used
};
2935 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
2936 $last_proc_pid_stat->{$pid} = {
2942 $d->{cpu
} = $old->{cpu
};
2946 return $res if !$full;
2948 my $qmpclient = PVE
::QMPClient-
>new();
2950 my $ballooncb = sub {
2951 my ($vmid, $resp) = @_;
2953 my $info = $resp->{'return'};
2954 return if !$info->{max_mem
};
2956 my $d = $res->{$vmid};
2958 # use memory assigned to VM
2959 $d->{maxmem
} = $info->{max_mem
};
2960 $d->{balloon
} = $info->{actual
};
2962 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
2963 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
2964 $d->{freemem
} = $info->{free_mem
};
2967 $d->{ballooninfo
} = $info;
2970 my $blockstatscb = sub {
2971 my ($vmid, $resp) = @_;
2972 my $data = $resp->{'return'} || [];
2973 my $totalrdbytes = 0;
2974 my $totalwrbytes = 0;
2976 for my $blockstat (@$data) {
2977 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
2978 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
2980 $blockstat->{device
} =~ s/drive-//;
2981 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
2983 $res->{$vmid}->{diskread
} = $totalrdbytes;
2984 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
2987 my $machinecb = sub {
2988 my ($vmid, $resp) = @_;
2989 my $data = $resp->{'return'} || [];
2991 $res->{$vmid}->{'running-machine'} =
2992 PVE
::QemuServer
::Machine
::current_from_query_machines
($data);
2995 my $versioncb = sub {
2996 my ($vmid, $resp) = @_;
2997 my $data = $resp->{'return'} // {};
2998 my $version = 'unknown';
3000 if (my $v = $data->{qemu
}) {
3001 $version = $v->{major
} . "." . $v->{minor
} . "." . $v->{micro
};
3004 $res->{$vmid}->{'running-qemu'} = $version;
3007 my $statuscb = sub {
3008 my ($vmid, $resp) = @_;
3010 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
3011 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
3012 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
3013 # this fails if ballon driver is not loaded, so this must be
3014 # the last commnand (following command are aborted if this fails).
3015 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
3017 my $status = 'unknown';
3018 if (!defined($status = $resp->{'return'}->{status
})) {
3019 warn "unable to get VM status\n";
3023 $res->{$vmid}->{qmpstatus
} = $resp->{'return'}->{status
};
3026 foreach my $vmid (keys %$list) {
3027 next if $opt_vmid && ($vmid ne $opt_vmid);
3028 next if !$res->{$vmid}->{pid
}; # not running
3029 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
3032 $qmpclient->queue_execute(undef, 2);
3034 foreach my $vmid (keys %$list) {
3035 next if $opt_vmid && ($vmid ne $opt_vmid);
3036 next if !$res->{$vmid}->{pid
}; #not running
3038 # we can't use the $qmpclient since it might have already aborted on
3039 # 'query-balloon', but this might also fail for older versions...
3040 my $qemu_support = eval { mon_cmd
($vmid, "query-proxmox-support") };
3041 $res->{$vmid}->{'proxmox-support'} = $qemu_support // {};
3044 foreach my $vmid (keys %$list) {
3045 next if $opt_vmid && ($vmid ne $opt_vmid);
3046 $res->{$vmid}->{qmpstatus
} = $res->{$vmid}->{status
} if !$res->{$vmid}->{qmpstatus
};
3052 sub conf_has_serial
{
3055 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3056 if ($conf->{"serial$i"}) {
3064 sub conf_has_audio
{
3065 my ($conf, $id) = @_;
3068 my $audio = $conf->{"audio$id"};
3069 return if !defined($audio);
3071 my $audioproperties = parse_property_string
($audio_fmt, $audio);
3072 my $audiodriver = $audioproperties->{driver
} // 'spice';
3075 dev
=> $audioproperties->{device
},
3076 dev_id
=> "audiodev$id",
3077 backend
=> $audiodriver,
3078 backend_id
=> "$audiodriver-backend${id}",
3083 my ($audio, $audiopciaddr, $machine_version) = @_;
3087 my $id = $audio->{dev_id
};
3089 if (min_version
($machine_version, 4, 2)) {
3090 $audiodev = ",audiodev=$audio->{backend_id}";
3093 if ($audio->{dev
} eq 'AC97') {
3094 push @$devs, '-device', "AC97,id=${id}${audiopciaddr}$audiodev";
3095 } elsif ($audio->{dev
} =~ /intel\-hda$/) {
3096 push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
3097 push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0$audiodev";
3098 push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1$audiodev";
3100 die "unkown audio device '$audio->{dev}', implement me!";
3103 push @$devs, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
3111 socket => "/var/run/qemu-server/$vmid.swtpm",
3112 pid
=> "/var/run/qemu-server/$vmid.swtpm.pid",
3116 sub add_tpm_device
{
3117 my ($vmid, $devices, $conf) = @_;
3119 return if !$conf->{tpmstate0
};
3121 my $paths = get_tpm_paths
($vmid);
3123 push @$devices, "-chardev", "socket,id=tpmchar,path=$paths->{socket}";
3124 push @$devices, "-tpmdev", "emulator,id=tpmdev,chardev=tpmchar";
3125 push @$devices, "-device", "tpm-tis,tpmdev=tpmdev";
3129 my ($storecfg, $vmid, $tpmdrive, $migration) = @_;
3131 return if !$tpmdrive;
3134 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
3135 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
3137 $state = PVE
::Storage
::map_volume
($storecfg, $tpm->{file
});
3139 $state = $tpm->{file
};
3142 my $paths = get_tpm_paths
($vmid);
3144 # during migration, we will get state from remote
3147 # run swtpm_setup to create a new TPM state if it doesn't exist yet
3154 "--create-platform-cert",
3157 "/etc/swtpm_setup.conf", # do not use XDG configs
3159 "0", # force creation as root, error if not possible
3160 "--not-overwrite", # ignore existing state, do not modify
3163 push @$setup_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3164 # TPM 2.0 supports ECC crypto, use if possible
3165 push @$setup_cmd, "--ecc" if $tpm->{version
} eq 'v2.0';
3167 run_command
($setup_cmd, outfunc
=> sub {
3168 print "swtpm_setup: $1\n";
3172 # Used to distinguish different invocations in the log.
3173 my $log_prefix = "[id=" . int(time()) . "] ";
3175 my $emulator_cmd = [
3179 "backend-uri=file://$state,mode=0600",
3181 "type=unixio,path=$paths->{socket},mode=0600",
3183 "file=$paths->{pid}",
3184 "--terminate", # terminate on QEMU disconnect
3187 "file=/run/qemu-server/$vmid-swtpm.log,level=1,prefix=$log_prefix",
3189 push @$emulator_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3190 run_command
($emulator_cmd, outfunc
=> sub { print $1; });
3192 my $tries = 100; # swtpm may take a bit to start before daemonizing, wait up to 5s for pid
3193 while (! -e
$paths->{pid
}) {
3194 die "failed to start swtpm: pid file '$paths->{pid}' wasn't created.\n" if --$tries == 0;
3198 # return untainted PID of swtpm daemon so it can be killed on error
3199 file_read_firstline
($paths->{pid
}) =~ m/(\d+)/;
3203 sub vga_conf_has_spice
{
3206 my $vgaconf = parse_vga
($vga);
3207 my $vgatype = $vgaconf->{type
};
3208 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
3215 return $conf->{arch
} // get_host_arch
();
3218 my $default_machines = {
3223 sub get_installed_machine_version
{
3224 my ($kvmversion) = @_;
3225 $kvmversion = kvm_user_version
() if !defined($kvmversion);
3226 $kvmversion =~ m/^(\d+\.\d+)/;
3230 sub windows_get_pinned_machine_version
{
3231 my ($machine, $base_version, $kvmversion) = @_;
3233 my $pin_version = $base_version;
3234 if (!defined($base_version) ||
3235 !PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($base_version, $kvmversion)
3237 $pin_version = get_installed_machine_version
($kvmversion);
3239 if (!$machine || $machine eq 'pc') {
3240 $machine = "pc-i440fx-$pin_version";
3241 } elsif ($machine eq 'q35') {
3242 $machine = "pc-q35-$pin_version";
3243 } elsif ($machine eq 'virt') {
3244 $machine = "virt-$pin_version";
3246 warn "unknown machine type '$machine', not touching that!\n";
3252 sub get_vm_machine
{
3253 my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
3255 my $machine_conf = PVE
::QemuServer
::Machine
::parse_machine
($conf->{machine
});
3256 my $machine = $forcemachine || $machine_conf->{type
};
3258 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
3259 $kvmversion //= kvm_user_version
();
3260 # we must pin Windows VMs without a specific version to 5.1, as 5.2 fixed a bug in ACPI
3261 # layout which confuses windows quite a bit and may result in various regressions..
3262 # see: https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg08484.html
3263 if (windows_version
($conf->{ostype
})) {
3264 $machine = windows_get_pinned_machine_version
($machine, '5.1', $kvmversion);
3267 $machine ||= $default_machines->{$arch};
3268 if ($add_pve_version) {
3269 my $pvever = PVE
::QemuServer
::Machine
::get_pve_version
($kvmversion);
3270 $machine .= "+pve$pvever";
3274 if ($add_pve_version && $machine !~ m/\+pve\d+?(?:\.pxe)?$/) {
3275 my $is_pxe = $machine =~ m/^(.*?)\.pxe$/;
3276 $machine = $1 if $is_pxe;
3278 # for version-pinned machines that do not include a pve-version (e.g.
3279 # pc-q35-4.1), we assume 0 to keep them stable in case we bump
3280 $machine .= '+pve0';
3282 $machine .= '.pxe' if $is_pxe;
3288 sub get_ovmf_files
($$$) {
3289 my ($arch, $efidisk, $smm) = @_;
3291 my $types = $OVMF->{$arch}
3292 or die "no OVMF images known for architecture '$arch'\n";
3294 my $type = 'default';
3295 if ($arch eq 'x86_64') {
3296 if (defined($efidisk->{efitype
}) && $efidisk->{efitype
} eq '4m') {
3297 $type = $smm ?
"4m" : "4m-no-smm";
3298 $type .= '-ms' if $efidisk->{'pre-enrolled-keys'};
3300 # TODO: log_warn about use of legacy images for x86_64 with Promxox VE 9
3304 my ($ovmf_code, $ovmf_vars) = $types->{$type}->@*;
3305 die "EFI base image '$ovmf_code' not found\n" if ! -f
$ovmf_code;
3306 die "EFI vars image '$ovmf_vars' not found\n" if ! -f
$ovmf_vars;
3308 return ($ovmf_code, $ovmf_vars);
3312 aarch64
=> '/usr/bin/qemu-system-aarch64',
3313 x86_64
=> '/usr/bin/qemu-system-x86_64',
3315 sub get_command_for_arch
($) {
3317 return '/usr/bin/kvm' if is_native_arch
($arch);
3319 my $cmd = $Arch2Qemu->{$arch}
3320 or die "don't know how to emulate architecture '$arch'\n";
3324 # To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
3325 # to use in a QEMU command line (-cpu element), first array_intersect the result
3326 # of query_supported_ with query_understood_. This is necessary because:
3328 # a) query_understood_ returns flags the host cannot use and
3329 # b) query_supported_ (rather the QMP call) doesn't actually return CPU
3330 # flags, but CPU settings - with most of them being flags. Those settings
3331 # (and some flags, curiously) cannot be specified as a "-cpu" argument.
3333 # query_supported_ needs to start up to 2 temporary VMs and is therefore rather
3334 # expensive. If you need the value returned from this, you can get it much
3335 # cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags-$accel') with
3336 # $accel being 'kvm' or 'tcg'.
3338 # pvestatd calls this function on startup and whenever the QEMU/KVM version
3339 # changes, automatically populating pmxcfs.
3341 # Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
3342 # since kvm and tcg machines support different flags
3344 sub query_supported_cpu_flags
{
3347 $arch //= get_host_arch
();
3348 my $default_machine = $default_machines->{$arch};
3352 # FIXME: Once this is merged, the code below should work for ARM as well:
3353 # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
3354 die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
3357 my $kvm_supported = defined(kvm_version
());
3358 my $qemu_cmd = get_command_for_arch
($arch);
3360 my $pidfile = PVE
::QemuServer
::Helpers
::pidfile_name
($fakevmid);
3362 # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
3363 my $query_supported_run_qemu = sub {
3369 '-machine', $default_machine,
3371 '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
3372 '-mon', 'chardev=qmp,mode=control',
3373 '-pidfile', $pidfile,
3378 push @$cmd, '-accel', 'tcg';
3381 my $rc = run_command
($cmd, noerr
=> 1, quiet
=> 0);
3382 die "QEMU flag querying VM exited with code " . $rc if $rc;
3385 my $cmd_result = mon_cmd
(
3387 'query-cpu-model-expansion',
3389 model
=> { name
=> 'host' }
3392 my $props = $cmd_result->{model
}->{props
};
3393 foreach my $prop (keys %$props) {
3394 next if $props->{$prop} ne '1';
3395 # QEMU returns some flags multiple times, with '_', '.' or '-'
3396 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
3397 # We only keep those with underscores, to match /proc/cpuinfo
3398 $prop =~ s/\.|-/_/g;
3399 $flags->{$prop} = 1;
3404 # force stop with 10 sec timeout and 'nocheck', always stop, even if QMP failed
3405 vm_stop
(undef, $fakevmid, 1, 1, 10, 0, 1);
3409 return [ sort keys %$flags ];
3412 # We need to query QEMU twice, since KVM and TCG have different supported flags
3413 PVE
::QemuConfig-
>lock_config($fakevmid, sub {
3414 $flags->{tcg
} = eval { $query_supported_run_qemu->(0) };
3415 warn "warning: failed querying supported tcg flags: $@\n" if $@;
3417 if ($kvm_supported) {
3418 $flags->{kvm
} = eval { $query_supported_run_qemu->(1) };
3419 warn "warning: failed querying supported kvm flags: $@\n" if $@;
3426 # Understood CPU flags are written to a file at 'pve-qemu' compile time
3427 my $understood_cpu_flag_dir = "/usr/share/kvm";
3428 sub query_understood_cpu_flags
{
3429 my $arch = get_host_arch
();
3430 my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
3432 die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
3435 my $raw = file_get_contents
($filepath);
3436 $raw =~ s/^\s+|\s+$//g;
3437 my @flags = split(/\s+/, $raw);
3442 # Since commit 277d33454f77ec1d1e0bc04e37621e4dd2424b67 in pve-qemu, smm is not off by default
3443 # anymore. But smm=off seems to be required when using SeaBIOS and serial display.
3444 my sub should_disable_smm
{
3445 my ($conf, $vga, $machine) = @_;
3447 return if $machine =~ m/^virt/; # there is no smm flag that could be disabled
3449 return (!defined($conf->{bios
}) || $conf->{bios
} eq 'seabios') &&
3450 $vga->{type
} && $vga->{type
} =~ m/^(serial\d+|none)$/;
3453 my sub print_ovmf_drive_commandlines
{
3454 my ($conf, $storecfg, $vmid, $arch, $q35, $version_guard) = @_;
3456 my $d = $conf->{efidisk0
} ? parse_drive
('efidisk0', $conf->{efidisk0
}) : undef;
3458 my ($ovmf_code, $ovmf_vars) = get_ovmf_files
($arch, $d, $q35);
3460 my $var_drive_str = "if=pflash,unit=1,id=drive-efidisk0";
3462 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($d->{file
}, 1);
3463 my ($path, $format) = $d->@{'file', 'format'};
3465 $path = PVE
::Storage
::path
($storecfg, $d->{file
});
3466 if (!defined($format)) {
3467 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
3468 $format = qemu_img_format
($scfg, $volname);
3470 } elsif (!defined($format)) {
3471 die "efidisk format must be specified\n";
3473 # SPI flash does lots of read-modify-write OPs, without writeback this gets really slow #3329
3474 if ($path =~ m/^rbd:/) {
3475 $var_drive_str .= ',cache=writeback';
3476 $path .= ':rbd_cache_policy=writeback'; # avoid write-around, we *need* to cache writes too
3478 $var_drive_str .= ",format=$format,file=$path";
3480 $var_drive_str .= ",size=" . (-s
$ovmf_vars) if $format eq 'raw' && $version_guard->(4, 1, 2);
3481 $var_drive_str .= ',readonly=on' if drive_is_read_only
($conf, $d);
3483 log_warn
("no efidisk configured! Using temporary efivars disk.");
3484 my $path = "/tmp/$vmid-ovmf.fd";
3485 PVE
::Tools
::file_copy
($ovmf_vars, $path, -s
$ovmf_vars);
3486 $var_drive_str .= ",format=raw,file=$path";
3487 $var_drive_str .= ",size=" . (-s
$ovmf_vars) if $version_guard->(4, 1, 2);
3490 return ("if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code", $var_drive_str);
3493 sub config_to_command
{
3494 my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
3495 $live_restore_backing) = @_;
3497 my ($globalFlags, $machineFlags, $rtcFlags) = ([], [], []);
3500 my $ostype = $conf->{ostype
};
3501 my $winversion = windows_version
($ostype);
3502 my $kvm = $conf->{kvm
};
3503 my $nodename = nodename
();
3505 my $machine_conf = PVE
::QemuServer
::Machine
::parse_machine
($conf->{machine
});
3507 my $arch = get_vm_arch
($conf);
3508 my $kvm_binary = get_command_for_arch
($arch);
3509 my $kvmver = kvm_user_version
($kvm_binary);
3511 if (!$kvmver || $kvmver !~ m/^(\d+)\.(\d+)/ || $1 < 3) {
3512 $kvmver //= "undefined";
3513 die "Detected old QEMU binary ('$kvmver', at least 3.0 is required)\n";
3516 my $add_pve_version = min_version
($kvmver, 4, 1);
3518 my $machine_type = get_vm_machine
($conf, $forcemachine, $arch, $add_pve_version);
3519 my $machine_version = extract_version
($machine_type, $kvmver);
3520 $kvm //= 1 if is_native_arch
($arch);
3522 $machine_version =~ m/(\d+)\.(\d+)/;
3523 my ($machine_major, $machine_minor) = ($1, $2);
3525 if ($kvmver =~ m/^\d+\.\d+\.(\d+)/ && $1 >= 90) {
3526 warn "warning: Installed QEMU version ($kvmver) is a release candidate, ignoring version checks\n";
3527 } elsif (!min_version
($kvmver, $machine_major, $machine_minor)) {
3528 die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type',"
3529 ." please upgrade node '$nodename'\n"
3530 } elsif (!PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($machine_version, $kvmver)) {
3531 my $max_pve_version = PVE
::QemuServer
::Machine
::get_pve_version
($machine_version);
3532 die "Installed qemu-server (max feature level for $machine_major.$machine_minor is"
3533 ." pve$max_pve_version) is too old to run machine type '$machine_type', please upgrade"
3534 ." node '$nodename'\n";
3537 # if a specific +pve version is required for a feature, use $version_guard
3538 # instead of min_version to allow machines to be run with the minimum
3540 my $required_pve_version = 0;
3541 my $version_guard = sub {
3542 my ($major, $minor, $pve) = @_;
3543 return 0 if !min_version
($machine_version, $major, $minor, $pve);
3544 my $max_pve = PVE
::QemuServer
::Machine
::get_pve_version
("$major.$minor");
3545 return 1 if min_version
($machine_version, $major, $minor, $max_pve+1);
3546 $required_pve_version = $pve if $pve && $pve > $required_pve_version;
3550 if ($kvm && !defined kvm_version
()) {
3551 die "KVM virtualisation configured, but not available. Either disable in VM configuration"
3552 ." or enable in BIOS.\n";
3555 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3556 my $hotplug_features = parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
3557 my $use_old_bios_files = undef;
3558 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3561 if ($conf->{affinity
}) {
3562 push @$cmd, '/usr/bin/taskset', '--cpu-list', '--all-tasks', $conf->{affinity
};
3565 push @$cmd, $kvm_binary;
3567 push @$cmd, '-id', $vmid;
3569 my $vmname = $conf->{name
} || "vm$vmid";
3571 push @$cmd, '-name', "$vmname,debug-threads=on";
3573 push @$cmd, '-no-shutdown';
3577 my $qmpsocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid);
3578 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server=on,wait=off";
3579 push @$cmd, '-mon', "chardev=qmp,mode=control";
3581 if (min_version
($machine_version, 2, 12)) {
3582 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3583 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3586 push @$cmd, '-pidfile' , PVE
::QemuServer
::Helpers
::pidfile_name
($vmid);
3588 push @$cmd, '-daemonize';
3590 if ($conf->{smbios1
}) {
3591 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
3592 if ($smbios_conf->{base64
}) {
3593 # Do not pass base64 flag to qemu
3594 delete $smbios_conf->{base64
};
3595 my $smbios_string = "";
3596 foreach my $key (keys %$smbios_conf) {
3598 if ($key eq "uuid") {
3599 $value = $smbios_conf->{uuid
}
3601 $value = decode_base64
($smbios_conf->{$key});
3603 # qemu accepts any binary data, only commas need escaping by double comma
3605 $smbios_string .= "," . $key . "=" . $value if $value;
3607 push @$cmd, '-smbios', "type=1" . $smbios_string;
3609 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3613 if ($conf->{bios
} && $conf->{bios
} eq 'ovmf') {
3614 die "OVMF (UEFI) BIOS is not supported on 32-bit CPU types\n"
3615 if !$forcecpu && get_cpu_bitness
($conf->{cpu
}, $arch) == 32;
3617 my ($code_drive_str, $var_drive_str) =
3618 print_ovmf_drive_commandlines
($conf, $storecfg, $vmid, $arch, $q35, $version_guard);
3619 push $cmd->@*, '-drive', $code_drive_str;
3620 push $cmd->@*, '-drive', $var_drive_str;
3623 if ($q35) { # tell QEMU to load q35 config early
3624 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3625 if (min_version
($machine_version, 4, 0)) {
3626 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3628 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3632 if (defined(my $fixups = qemu_created_version_fixups
($conf, $forcemachine, $kvmver))) {
3633 push @$cmd, $fixups->@*;
3636 if ($conf->{vmgenid
}) {
3637 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid
};
3640 # add usb controllers
3641 my @usbcontrollers = PVE
::QemuServer
::USB
::get_usb_controllers
(
3642 $conf, $bridges, $arch, $machine_type, $machine_version);
3643 push @$devices, @usbcontrollers if @usbcontrollers;
3644 my $vga = parse_vga
($conf->{vga
});
3646 my $qxlnum = vga_conf_has_spice
($conf->{vga
});
3647 $vga->{type
} = 'qxl' if $qxlnum;
3649 if (!$vga->{type
}) {
3650 if ($arch eq 'aarch64') {
3651 $vga->{type
} = 'virtio';
3652 } elsif (min_version
($machine_version, 2, 9)) {
3653 $vga->{type
} = (!$winversion || $winversion >= 6) ?
'std' : 'cirrus';
3655 $vga->{type
} = ($winversion >= 6) ?
'std' : 'cirrus';
3659 # enable absolute mouse coordinates (needed by vnc)
3660 my $tablet = $conf->{tablet
};
3661 if (!defined($tablet)) {
3662 $tablet = $defaults->{tablet
};
3663 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3664 $tablet = 0 if $vga->{type
} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3668 push @$devices, '-device', print_tabletdevice_full
($conf, $arch) if $tablet;
3669 my $kbd = print_keyboarddevice_full
($conf, $arch);
3670 push @$devices, '-device', $kbd if defined($kbd);
3673 my $bootorder = device_bootorder
($conf);
3675 # host pci device passthrough
3676 my ($kvm_off, $gpu_passthrough, $legacy_igd, $pci_devices) = PVE
::QemuServer
::PCI
::print_hostpci_devices
(
3677 $vmid, $conf, $devices, $vga, $winversion, $bridges, $arch, $machine_type, $bootorder);
3680 my $usb_dev_features = {};
3681 $usb_dev_features->{spice_usb3
} = 1 if min_version
($machine_version, 4, 0);
3683 my @usbdevices = PVE
::QemuServer
::USB
::get_usb_devices
(
3684 $conf, $usb_dev_features, $bootorder, $machine_version);
3685 push @$devices, @usbdevices if @usbdevices;
3688 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3689 my $path = $conf->{"serial$i"} or next;
3690 if ($path eq 'socket') {
3691 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3692 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
3693 # On aarch64, serial0 is the UART device. QEMU only allows
3694 # connecting UART devices via the '-serial' command line, as
3695 # the device has a fixed slot on the hardware...
3696 if ($arch eq 'aarch64' && $i == 0) {
3697 push @$devices, '-serial', "chardev:serial$i";
3699 push @$devices, '-device', "isa-serial,chardev=serial$i";
3702 die "no such serial device\n" if ! -c
$path;
3703 push @$devices, '-chardev', "serial,id=serial$i,path=$path";
3704 push @$devices, '-device', "isa-serial,chardev=serial$i";
3709 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3710 if (my $path = $conf->{"parallel$i"}) {
3711 die "no such parallel device\n" if ! -c
$path;
3712 my $devtype = $path =~ m!^/dev/usb/lp! ?
'serial' : 'parallel';
3713 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3714 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3718 if (min_version
($machine_version, 4, 0) && (my $audio = conf_has_audio
($conf))) {
3719 my $audiopciaddr = print_pci_addr
("audio0", $bridges, $arch, $machine_type);
3720 my $audio_devs = audio_devs
($audio, $audiopciaddr, $machine_version);
3721 push @$devices, @$audio_devs;
3724 # Add a TPM only if the VM is not a template,
3725 # to support backing up template VMs even if the TPM disk is write-protected.
3726 add_tpm_device
($vmid, $devices, $conf) if (!PVE
::QemuConfig-
>is_template($conf));
3729 $sockets = $conf->{smp
} if $conf->{smp
}; # old style - no longer iused
3730 $sockets = $conf->{sockets
} if $conf->{sockets
};
3732 my $cores = $conf->{cores
} || 1;
3734 my $maxcpus = $sockets * $cores;
3736 my $vcpus = $conf->{vcpus
} ?
$conf->{vcpus
} : $maxcpus;
3738 my $allowed_vcpus = $cpuinfo->{cpus
};
3740 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n" if ($allowed_vcpus < $maxcpus);
3742 if ($hotplug_features->{cpu
} && min_version
($machine_version, 2, 7)) {
3743 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3744 for (my $i = 2; $i <= $vcpus; $i++) {
3745 my $cpustr = print_cpu_device
($conf, $arch, $i);
3746 push @$cmd, '-device', $cpustr;
3751 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3753 push @$cmd, '-nodefaults';
3755 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3757 push $machineFlags->@*, 'acpi=off' if defined($conf->{acpi
}) && $conf->{acpi
} == 0;
3759 push @$cmd, '-no-reboot' if defined($conf->{reboot
}) && $conf->{reboot
} == 0;
3761 if ($vga->{type
} && $vga->{type
} !~ m/^serial\d+$/ && $vga->{type
} ne 'none'){
3762 push @$devices, '-device', print_vga_device
(
3763 $conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
3765 push @$cmd, '-display', 'egl-headless,gl=core' if $vga->{type
} eq 'virtio-gl'; # VIRGL
3767 my $socket = PVE
::QemuServer
::Helpers
::vnc_socket
($vmid);
3768 push @$cmd, '-vnc', "unix:$socket,password=on";
3770 push @$cmd, '-vga', 'none' if $vga->{type
} eq 'none';
3771 push @$cmd, '-nographic';
3775 my $tdf = defined($conf->{tdf
}) ?
$conf->{tdf
} : $defaults->{tdf
};
3776 my $useLocaltime = $conf->{localtime};
3778 if ($winversion >= 5) { # windows
3779 $useLocaltime = 1 if !defined($conf->{localtime});
3781 # use time drift fix when acpi is enabled
3782 if (!(defined($conf->{acpi
}) && $conf->{acpi
} == 0)) {
3783 $tdf = 1 if !defined($conf->{tdf
});
3787 if ($winversion >= 6) {
3788 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3789 push @$machineFlags, 'hpet=off';
3792 push @$rtcFlags, 'driftfix=slew' if $tdf;
3794 if ($conf->{startdate
} && $conf->{startdate
} ne 'now') {
3795 push @$rtcFlags, "base=$conf->{startdate}";
3796 } elsif ($useLocaltime) {
3797 push @$rtcFlags, 'base=localtime';
3801 push @$cmd, '-cpu', $forcecpu;
3803 push @$cmd, get_cpu_options
($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
3806 PVE
::QemuServer
::Memory
::config
(
3807 $conf, $vmid, $sockets, $cores, $hotplug_features->{memory
}, $cmd);
3809 push @$cmd, '-S' if $conf->{freeze
};
3811 push @$cmd, '-k', $conf->{keyboard
} if defined($conf->{keyboard
});
3813 my $guest_agent = parse_guest_agent
($conf);
3815 if ($guest_agent->{enabled
}) {
3816 my $qgasocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid, 1);
3817 push @$devices, '-chardev', "socket,path=$qgasocket,server=on,wait=off,id=qga0";
3819 if (!$guest_agent->{type
} || $guest_agent->{type
} eq 'virtio') {
3820 my $pciaddr = print_pci_addr
("qga0", $bridges, $arch, $machine_type);
3821 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3822 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3823 } elsif ($guest_agent->{type
} eq 'isa') {
3824 push @$devices, '-device', "isa-serial,chardev=qga0";
3828 my $rng = $conf->{rng0
} ? parse_rng
($conf->{rng0
}) : undef;
3829 if ($rng && $version_guard->(4, 1, 2)) {
3830 check_rng_source
($rng->{source
});
3832 my $max_bytes = $rng->{max_bytes
} // $rng_fmt->{max_bytes
}->{default};
3833 my $period = $rng->{period
} // $rng_fmt->{period
}->{default};
3834 my $limiter_str = "";
3836 $limiter_str = ",max-bytes=$max_bytes,period=$period";
3839 my $rng_addr = print_pci_addr
("rng0", $bridges, $arch, $machine_type);
3840 push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
3841 push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
3846 assert_clipboard_config
($vga);
3847 my $is_spice = $qxlnum || $vga->{type
} =~ /^virtio/;
3849 if ($is_spice || ($vga->{'clipboard'} && $vga->{'clipboard'} eq 'vnc')) {
3852 for (my $i = 1; $i < $qxlnum; $i++){
3853 push @$devices, '-device', print_vga_device
(
3854 $conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
3857 # assume other OS works like Linux
3858 my ($ram, $vram) = ("134217728", "67108864");
3859 if ($vga->{memory
}) {
3860 $ram = PVE
::Tools
::convert_size
($qxlnum*4*$vga->{memory
}, 'mb' => 'b');
3861 $vram = PVE
::Tools
::convert_size
($qxlnum*2*$vga->{memory
}, 'mb' => 'b');
3863 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3864 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3868 my $pciaddr = print_pci_addr
("spice", $bridges, $arch, $machine_type);
3870 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3871 if ($vga->{'clipboard'} && $vga->{'clipboard'} eq 'vnc') {
3872 push @$devices, '-chardev', 'qemu-vdagent,id=vdagent,name=vdagent,clipboard=on';
3874 push @$devices, '-chardev', 'spicevmc,id=vdagent,name=vdagent';
3876 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3879 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
3880 my @nodeaddrs = PVE
::Tools
::getaddrinfo_all
('localhost', family
=> $pfamily);
3881 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3883 my $localhost = PVE
::Network
::addr_to_ip
($nodeaddrs[0]->{addr
});
3884 $spice_port = PVE
::Tools
::next_spice_port
($pfamily, $localhost);
3886 my $spice_enhancement_str = $conf->{spice_enhancements
} // '';
3887 my $spice_enhancement = parse_property_string
($spice_enhancements_fmt, $spice_enhancement_str);
3888 if ($spice_enhancement->{foldersharing
}) {
3889 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3890 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3893 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3894 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}"
3895 if $spice_enhancement->{videostreaming
};
3896 push @$devices, '-spice', "$spice_opts";
3900 # enable balloon by default, unless explicitly disabled
3901 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
3902 my $pciaddr = print_pci_addr
("balloon0", $bridges, $arch, $machine_type);
3903 my $ballooncmd = "virtio-balloon-pci,id=balloon0$pciaddr";
3904 $ballooncmd .= ",free-page-reporting=on" if min_version
($machine_version, 6, 2);
3905 push @$devices, '-device', $ballooncmd;
3908 if ($conf->{watchdog
}) {
3909 my $wdopts = parse_watchdog
($conf->{watchdog
});
3910 my $pciaddr = print_pci_addr
("watchdog", $bridges, $arch, $machine_type);
3911 my $watchdog = $wdopts->{model
} || 'i6300esb';
3912 push @$devices, '-device', "$watchdog$pciaddr";
3913 push @$devices, '-watchdog-action', $wdopts->{action
} if $wdopts->{action
};
3917 my $scsicontroller = {};
3918 my $ahcicontroller = {};
3919 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : $defaults->{scsihw
};
3921 # Add iscsi initiator name if available
3922 if (my $initiator = get_initiator_name
()) {
3923 push @$devices, '-iscsi', "initiator-name=$initiator";
3926 PVE
::QemuConfig-
>foreach_volume($conf, sub {
3927 my ($ds, $drive) = @_;
3929 if (PVE
::Storage
::parse_volume_id
($drive->{file
}, 1)) {
3930 check_volume_storage_type
($storecfg, $drive->{file
});
3931 push @$vollist, $drive->{file
};
3934 # ignore efidisk here, already added in bios/fw handling code above
3935 return if $drive->{interface
} eq 'efidisk';
3937 return if $drive->{interface
} eq 'tpmstate';
3939 $use_virtio = 1 if $ds =~ m/^virtio/;
3941 $drive->{bootindex
} = $bootorder->{$ds} if $bootorder->{$ds};
3943 if ($drive->{interface
} eq 'virtio'){
3944 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread
};
3947 if ($drive->{interface
} eq 'scsi') {
3949 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
3951 die "scsi$drive->{index}: machine version 4.1~pve2 or higher is required to use more than 14 SCSI disks\n"
3952 if $drive->{index} > 13 && !&$version_guard(4, 1, 2);
3954 my $pciaddr = print_pci_addr
("$controller_prefix$controller", $bridges, $arch, $machine_type);
3955 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ?
"virtio-scsi-pci" : $scsihw;
3958 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{iothread
}){
3959 $iothread .= ",iothread=iothread-$controller_prefix$controller";
3960 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
3961 } elsif ($drive->{iothread
}) {
3963 "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n"
3968 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{queues
}){
3969 $queues = ",num_queues=$drive->{queues}";
3972 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues"
3973 if !$scsicontroller->{$controller};
3974 $scsicontroller->{$controller}=1;
3977 if ($drive->{interface
} eq 'sata') {
3978 my $controller = int($drive->{index} / $PVE::QemuServer
::Drive
::MAX_SATA_DISKS
);
3979 my $pciaddr = print_pci_addr
("ahci$controller", $bridges, $arch, $machine_type);
3980 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr"
3981 if !$ahcicontroller->{$controller};
3982 $ahcicontroller->{$controller}=1;
3985 my $live_restore = $live_restore_backing->{$ds};
3986 my $live_blockdev_name = undef;
3987 if ($live_restore) {
3988 $live_blockdev_name = $live_restore->{name
};
3989 push @$devices, '-blockdev', $live_restore->{blockdev
};
3992 my $drive_cmd = print_drive_commandline_full
(
3993 $storecfg, $vmid, $drive, $live_blockdev_name, min_version
($kvmver, 6, 0));
3995 # extra protection for templates, but SATA and IDE don't support it..
3996 $drive_cmd .= ',readonly=on' if drive_is_read_only
($conf, $drive);
3998 push @$devices, '-drive',$drive_cmd;
3999 push @$devices, '-device', print_drivedevice_full
(
4000 $storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
4003 for (my $i = 0; $i < $MAX_NETS; $i++) {
4004 my $netname = "net$i";
4006 next if !$conf->{$netname};
4007 my $d = parse_net
($conf->{$netname});
4009 # save the MAC addr here (could be auto-gen. in some odd setups) for FDB registering later?
4011 $use_virtio = 1 if $d->{model
} eq 'virtio';
4013 $d->{bootindex
} = $bootorder->{$netname} if $bootorder->{$netname};
4015 my $netdevfull = print_netdev_full
($vmid, $conf, $arch, $d, $netname);
4016 push @$devices, '-netdev', $netdevfull;
4018 my $netdevicefull = print_netdevice_full
(
4019 $vmid, $conf, $d, $netname, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version);
4021 push @$devices, '-device', $netdevicefull;
4024 if ($conf->{ivshmem
}) {
4025 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
4029 $bus = print_pcie_addr
("ivshmem");
4031 $bus = print_pci_addr
("ivshmem", $bridges, $arch, $machine_type);
4034 my $ivshmem_name = $ivshmem->{name
} // $vmid;
4035 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
4037 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
4038 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path"
4039 .",size=$ivshmem->{size}M";
4042 # pci.4 is nested in pci.1
4043 $bridges->{1} = 1 if $bridges->{4};
4045 if (!$q35) { # add pci bridges
4046 if (min_version
($machine_version, 2, 3)) {
4050 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
4053 for my $k (sort {$b cmp $a} keys %$bridges) {
4054 next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
4057 if ($k == 2 && $legacy_igd) {
4060 my $pciaddr = print_pci_addr
("pci.$k_name", undef, $arch, $machine_type);
4061 my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
4063 if ($q35) { # add after -readconfig pve-q35.cfg
4064 splice @$devices, 2, 0, '-device', $devstr;
4066 unshift @$devices, '-device', $devstr if $k > 0;
4071 push @$machineFlags, 'accel=tcg';
4074 push @$machineFlags, 'smm=off' if should_disable_smm
($conf, $vga, $machine_type);
4076 my $machine_type_min = $machine_type;
4077 if ($add_pve_version) {
4078 $machine_type_min =~ s/\+pve\d+$//;
4079 $machine_type_min .= "+pve$required_pve_version";
4081 push @$machineFlags, "type=${machine_type_min}";
4083 push @$cmd, @$devices;
4084 push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
4085 push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
4086 push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags);
4088 if (my $vmstate = $conf->{vmstate
}) {
4089 my $statepath = PVE
::Storage
::path
($storecfg, $vmstate);
4090 push @$vollist, $vmstate;
4091 push @$cmd, '-loadstate', $statepath;
4092 print "activating and using '$vmstate' as vmstate\n";
4095 if (PVE
::QemuConfig-
>is_template($conf)) {
4096 # needed to workaround base volumes being read-only
4097 push @$cmd, '-snapshot';
4101 if ($conf->{args
}) {
4102 my $aa = PVE
::Tools
::split_args
($conf->{args
});
4106 return wantarray ?
($cmd, $vollist, $spice_port, $pci_devices) : $cmd;
4109 sub check_rng_source
{
4112 # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
4113 die "cannot create VirtIO RNG device: source file '$source' doesn't exist\n"
4116 my $rng_current = '/sys/devices/virtual/misc/hw_random/rng_current';
4117 if ($source eq '/dev/hwrng' && file_read_firstline
($rng_current) eq 'none') {
4118 # Needs to abort, otherwise QEMU crashes on first rng access. Note that rng_current cannot
4119 # be changed to 'none' manually, so once the VM is past this point, it's no longer an issue.
4120 die "Cannot start VM with passed-through RNG device: '/dev/hwrng' exists, but"
4121 ." '$rng_current' is set to 'none'. Ensure that a compatible hardware-RNG is attached"
4129 my $res = mon_cmd
($vmid, 'query-spice');
4131 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
4134 sub vm_devices_list
{
4137 my $res = mon_cmd
($vmid, 'query-pci');
4138 my $devices_to_check = [];
4140 foreach my $pcibus (@$res) {
4141 push @$devices_to_check, @{$pcibus->{devices
}},
4144 while (@$devices_to_check) {
4146 for my $d (@$devices_to_check) {
4147 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
4148 next if !$d->{'pci_bridge'} || !$d->{'pci_bridge'}->{devices
};
4150 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices
}});
4151 push @$to_check, @{$d->{'pci_bridge'}->{devices
}};
4153 $devices_to_check = $to_check;
4156 my $resblock = mon_cmd
($vmid, 'query-block');
4157 foreach my $block (@$resblock) {
4158 if($block->{device
} =~ m/^drive-(\S+)/){
4163 my $resmice = mon_cmd
($vmid, 'query-mice');
4164 foreach my $mice (@$resmice) {
4165 if ($mice->{name
} eq 'QEMU HID Tablet') {
4166 $devices->{tablet
} = 1;
4171 # for usb devices there is no query-usb
4172 # but we can iterate over the entries in
4173 # qom-list path=/machine/peripheral
4174 my $resperipheral = mon_cmd
($vmid, 'qom-list', path
=> '/machine/peripheral');
4175 foreach my $per (@$resperipheral) {
4176 if ($per->{name
} =~ m/^usb(?:redirdev)?\d+$/) {
4177 $devices->{$per->{name
}} = 1;
4185 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4187 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
4189 my $devices_list = vm_devices_list
($vmid);
4190 return 1 if defined($devices_list->{$deviceid});
4192 # add PCI bridge if we need it for the device
4193 qemu_add_pci_bridge
($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type);
4195 if ($deviceid eq 'tablet') {
4196 qemu_deviceadd
($vmid, print_tabletdevice_full
($conf, $arch));
4197 } elsif ($deviceid eq 'keyboard') {
4198 qemu_deviceadd
($vmid, print_keyboarddevice_full
($conf, $arch));
4199 } elsif ($deviceid =~ m/^usbredirdev(\d+)$/) {
4201 qemu_spice_usbredir_chardev_add
($vmid, "usbredirchardev$id");
4202 qemu_deviceadd
($vmid, PVE
::QemuServer
::USB
::print_spice_usbdevice
($id, "xhci", $id + 1));
4203 } elsif ($deviceid =~ m/^usb(\d+)$/) {
4204 qemu_deviceadd
($vmid, PVE
::QemuServer
::USB
::print_usbdevice_full
($conf, $deviceid, $device, {}, $1 + 1));
4205 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4206 qemu_iothread_add
($vmid, $deviceid, $device);
4208 qemu_driveadd
($storecfg, $vmid, $device);
4209 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4211 qemu_deviceadd
($vmid, $devicefull);
4212 eval { qemu_deviceaddverify
($vmid, $deviceid); };
4214 eval { qemu_drivedel
($vmid, $deviceid); };
4218 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4219 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : "lsi";
4220 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4221 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ?
"virtio-scsi-pci" : $scsihw;
4223 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
4225 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread
}) {
4226 qemu_iothread_add
($vmid, $deviceid, $device);
4227 $devicefull .= ",iothread=iothread-$deviceid";
4230 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues
}) {
4231 $devicefull .= ",num_queues=$device->{queues}";
4234 qemu_deviceadd
($vmid, $devicefull);
4235 qemu_deviceaddverify
($vmid, $deviceid);
4236 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4237 qemu_findorcreatescsihw
($storecfg,$conf, $vmid, $device, $arch, $machine_type);
4238 qemu_driveadd
($storecfg, $vmid, $device);
4240 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4241 eval { qemu_deviceadd
($vmid, $devicefull); };
4243 eval { qemu_drivedel
($vmid, $deviceid); };
4247 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4248 return if !qemu_netdevadd
($vmid, $conf, $arch, $device, $deviceid);
4250 my $machine_type = PVE
::QemuServer
::Machine
::qemu_machine_pxe
($vmid, $conf);
4251 my $machine_version = PVE
::QemuServer
::Machine
::extract_version
($machine_type);
4252 my $use_old_bios_files = undef;
4253 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
4255 my $netdevicefull = print_netdevice_full
(
4256 $vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type, $machine_version);
4257 qemu_deviceadd
($vmid, $netdevicefull);
4259 qemu_deviceaddverify
($vmid, $deviceid);
4260 qemu_set_link_status
($vmid, $deviceid, !$device->{link_down
});
4263 eval { qemu_netdevdel
($vmid, $deviceid); };
4267 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
4269 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4270 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
4272 qemu_deviceadd
($vmid, $devicefull);
4273 qemu_deviceaddverify
($vmid, $deviceid);
4275 die "can't hotplug device '$deviceid'\n";
4281 # fixme: this should raise exceptions on error!
4282 sub vm_deviceunplug
{
4283 my ($vmid, $conf, $deviceid) = @_;
4285 my $devices_list = vm_devices_list
($vmid);
4286 return 1 if !defined($devices_list->{$deviceid});
4288 my $bootdisks = PVE
::QemuServer
::Drive
::get_bootdisks
($conf);
4289 die "can't unplug bootdisk '$deviceid'\n" if grep {$_ eq $deviceid} @$bootdisks;
4291 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard' || $deviceid eq 'xhci') {
4292 qemu_devicedel
($vmid, $deviceid);
4293 } elsif ($deviceid =~ m/^usbredirdev\d+$/) {
4294 qemu_devicedel
($vmid, $deviceid);
4295 qemu_devicedelverify
($vmid, $deviceid);
4296 } elsif ($deviceid =~ m/^usb\d+$/) {
4297 qemu_devicedel
($vmid, $deviceid);
4298 qemu_devicedelverify
($vmid, $deviceid);
4299 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4300 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4302 qemu_devicedel
($vmid, $deviceid);
4303 qemu_devicedelverify
($vmid, $deviceid);
4304 qemu_drivedel
($vmid, $deviceid);
4305 qemu_iothread_del
($vmid, $deviceid, $device);
4306 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4307 qemu_devicedel
($vmid, $deviceid);
4308 qemu_devicedelverify
($vmid, $deviceid);
4309 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4310 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4312 qemu_devicedel
($vmid, $deviceid);
4313 qemu_devicedelverify
($vmid, $deviceid);
4314 qemu_drivedel
($vmid, $deviceid);
4315 qemu_deletescsihw
($conf, $vmid, $deviceid);
4317 qemu_iothread_del
($vmid, "virtioscsi$device->{index}", $device)
4318 if $conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single');
4319 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4320 qemu_devicedel
($vmid, $deviceid);
4321 qemu_devicedelverify
($vmid, $deviceid);
4322 qemu_netdevdel
($vmid, $deviceid);
4324 die "can't unplug device '$deviceid'\n";
4330 sub qemu_spice_usbredir_chardev_add
{
4331 my ($vmid, $id) = @_;
4333 mon_cmd
($vmid, "chardev-add" , (
4344 sub qemu_iothread_add
{
4345 my ($vmid, $deviceid, $device) = @_;
4347 if ($device->{iothread
}) {
4348 my $iothreads = vm_iothreads_list
($vmid);
4349 qemu_objectadd
($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4353 sub qemu_iothread_del
{
4354 my ($vmid, $deviceid, $device) = @_;
4356 if ($device->{iothread
}) {
4357 my $iothreads = vm_iothreads_list
($vmid);
4358 qemu_objectdel
($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4363 my ($storecfg, $vmid, $device) = @_;
4365 my $kvmver = get_running_qemu_version
($vmid);
4366 my $io_uring = min_version
($kvmver, 6, 0);
4367 my $drive = print_drive_commandline_full
($storecfg, $vmid, $device, undef, $io_uring);
4368 $drive =~ s/\\/\\\\/g;
4369 my $ret = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "drive_add auto \"$drive\"");
4371 # If the command succeeds qemu prints: "OK
"
4372 return 1 if $ret =~ m/OK/s;
4374 die "adding drive failed
: $ret\n";
4378 my ($vmid, $deviceid) = @_;
4380 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-
$deviceid");
4383 return 1 if $ret eq "";
4385 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4386 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4388 die "deleting drive
$deviceid failed
: $ret\n";
4391 sub qemu_deviceaddverify {
4392 my ($vmid, $deviceid) = @_;
4394 for (my $i = 0; $i <= 5; $i++) {
4395 my $devices_list = vm_devices_list($vmid);
4396 return 1 if defined($devices_list->{$deviceid});
4400 die "error on hotplug device
'$deviceid'\n";
4404 sub qemu_devicedelverify {
4405 my ($vmid, $deviceid) = @_;
4407 # need to verify that the device is correctly removed as device_del
4408 # is async and empty return is not reliable
4410 for (my $i = 0; $i <= 5; $i++) {
4411 my $devices_list = vm_devices_list($vmid);
4412 return 1 if !defined($devices_list->{$deviceid});
4416 die "error on hot-unplugging device
'$deviceid'\n";
4419 sub qemu_findorcreatescsihw {
4420 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4422 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4424 my $scsihwid="$controller_prefix$controller";
4425 my $devices_list = vm_devices_list($vmid);
4427 if (!defined($devices_list->{$scsihwid})) {
4428 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4434 sub qemu_deletescsihw {
4435 my ($conf, $vmid, $opt) = @_;
4437 my $device = parse_drive($opt, $conf->{$opt});
4439 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4440 vm_deviceunplug($vmid, $conf, "virtioscsi
$device->{index}");
4444 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4446 my $devices_list = vm_devices_list($vmid);
4447 foreach my $opt (keys %{$devices_list}) {
4448 if (is_valid_drivename($opt)) {
4449 my $drive = parse_drive($opt, $conf->{$opt});
4450 if ($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4456 my $scsihwid="scsihw
$controller";
4458 vm_deviceunplug($vmid, $conf, $scsihwid);
4463 sub qemu_add_pci_bridge {
4464 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4470 print_pci_addr($device, $bridges, $arch, $machine_type);
4472 while (my ($k, $v) = each %$bridges) {
4475 return 1 if !defined($bridgeid) || $bridgeid < 1;
4477 my $bridge = "pci
.$bridgeid";
4478 my $devices_list = vm_devices_list($vmid);
4480 if (!defined($devices_list->{$bridge})) {
4481 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4487 sub qemu_set_link_status {
4488 my ($vmid, $device, $up) = @_;
4490 mon_cmd($vmid, "set_link
", name => $device,
4491 up => $up ? JSON::true : JSON::false);
4494 sub qemu_netdevadd {
4495 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4497 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4498 my %options = split(/[=,]/, $netdev);
4500 if (defined(my $vhost = $options{vhost})) {
4501 $options{vhost} = JSON::boolean(PVE::JSONSchema::parse_boolean($vhost));
4504 if (defined(my $queues = $options{queues})) {
4505 $options{queues} = $queues + 0;
4508 mon_cmd($vmid, "netdev_add
", %options);
4512 sub qemu_netdevdel {
4513 my ($vmid, $deviceid) = @_;
4515 mon_cmd($vmid, "netdev_del
", id => $deviceid);
4518 sub qemu_usb_hotplug {
4519 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4523 # remove the old one first
4524 vm_deviceunplug($vmid, $conf, $deviceid);
4526 # check if xhci controller is necessary and available
4527 my $devicelist = vm_devices_list($vmid);
4529 if (!$devicelist->{xhci}) {
4530 my $pciaddr = print_pci_addr("xhci
", undef, $arch, $machine_type);
4531 qemu_deviceadd($vmid, PVE::QemuServer::USB::print_qemu_xhci_controller($pciaddr));
4535 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type);
4538 sub qemu_cpu_hotplug {
4539 my ($vmid, $conf, $vcpus) = @_;
4541 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
4544 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4545 $sockets = $conf->{sockets} if $conf->{sockets};
4546 my $cores = $conf->{cores} || 1;
4547 my $maxcpus = $sockets * $cores;
4549 $vcpus = $maxcpus if !$vcpus;
4551 die "you can
't add more vcpus than maxcpus\n"
4552 if $vcpus > $maxcpus;
4554 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4556 if ($vcpus < $currentvcpus) {
4558 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4560 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4561 qemu_devicedel($vmid, "cpu$i");
4563 my $currentrunningvcpus = undef;
4565 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4566 last if scalar(@{$currentrunningvcpus}) == $i-1;
4567 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4571 #update conf after each succesfull cpu unplug
4572 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4573 PVE::QemuConfig->write_config($vmid, $conf);
4576 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4582 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4583 die "vcpus in running vm does not match its configuration\n"
4584 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4586 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4587 my $arch = get_vm_arch($conf);
4589 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4590 my $cpustr = print_cpu_device($conf, $arch, $i);
4591 qemu_deviceadd($vmid, $cpustr);
4594 my $currentrunningvcpus = undef;
4596 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4597 last if scalar(@{$currentrunningvcpus}) == $i;
4598 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4602 #update conf after each succesfull cpu hotplug
4603 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4604 PVE::QemuConfig->write_config($vmid, $conf);
4608 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4609 mon_cmd($vmid, "cpu-add", id => int($i));
4614 sub qemu_block_set_io_throttle {
4615 my ($vmid, $deviceid,
4616 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4617 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4618 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4619 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4621 return if !check_running($vmid) ;
4623 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4625 bps_rd => int($bps_rd),
4626 bps_wr => int($bps_wr),
4628 iops_rd => int($iops_rd),
4629 iops_wr => int($iops_wr),
4630 bps_max => int($bps_max),
4631 bps_rd_max => int($bps_rd_max),
4632 bps_wr_max => int($bps_wr_max),
4633 iops_max => int($iops_max),
4634 iops_rd_max => int($iops_rd_max),
4635 iops_wr_max => int($iops_wr_max),
4636 bps_max_length => int($bps_max_length),
4637 bps_rd_max_length => int($bps_rd_max_length),
4638 bps_wr_max_length => int($bps_wr_max_length),
4639 iops_max_length => int($iops_max_length),
4640 iops_rd_max_length => int($iops_rd_max_length),
4641 iops_wr_max_length => int($iops_wr_max_length),
4646 sub qemu_block_resize {
4647 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4649 my $running = check_running($vmid);
4651 PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4653 return if !$running;
4655 my $padding = (1024 - $size % 1024) % 1024;
4656 $size = $size + $padding;
4661 device => $deviceid,
4667 sub qemu_volume_snapshot {
4668 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4670 my $running = check_running($vmid);
4672 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4673 mon_cmd($vmid, 'blockdev-snapshot-internal-sync
', device => $deviceid, name => $snap);
4675 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4679 sub qemu_volume_snapshot_delete {
4680 my ($vmid, $storecfg, $volid, $snap) = @_;
4682 my $running = check_running($vmid);
4683 my $attached_deviceid;
4686 my $conf = PVE::QemuConfig->load_config($vmid);
4687 PVE::QemuConfig->foreach_volume($conf, sub {
4688 my ($ds, $drive) = @_;
4689 $attached_deviceid = "drive-$ds" if $drive->{file} eq $volid;
4693 if ($attached_deviceid && do_snapshots_with_qemu($storecfg, $volid, $attached_deviceid)) {
4696 'blockdev-snapshot-delete-internal-sync
',
4697 device => $attached_deviceid,
4701 PVE::Storage::volume_snapshot_delete(
4702 $storecfg, $volid, $snap, $attached_deviceid ? 1 : undef);
4706 sub set_migration_caps {
4707 my ($vmid, $savevm) = @_;
4709 my $qemu_support = eval { mon_cmd($vmid, "query-proxmox-support") };
4711 my $bitmap_prop = $savevm ? 'pbs-dirty-bitmap-savevm
' : 'pbs-dirty-bitmap-migration
';
4712 my $dirty_bitmaps = $qemu_support->{$bitmap_prop} ? 1 : 0;
4717 "auto-converge" => 1,
4719 "x-rdma-pin-all" => 0,
4722 "dirty-bitmaps" => $dirty_bitmaps,
4725 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4727 for my $supported_capability (@$supported_capabilities) {
4729 capability => $supported_capability->{capability},
4730 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4734 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4738 my ($conf, $func, @param) = @_;
4742 my $test_volid = sub {
4743 my ($key, $drive, $snapname, $pending) = @_;
4745 my $volid = $drive->{file};
4748 $volhash->{$volid}->{cdrom} //= 1;
4749 $volhash->{$volid}->{cdrom} = 0 if !drive_is_cdrom($drive);
4751 my $replicate = $drive->{replicate} // 1;
4752 $volhash->{$volid}->{replicate} //= 0;
4753 $volhash->{$volid}->{replicate} = 1 if $replicate;
4755 $volhash->{$volid}->{shared} //= 0;
4756 $volhash->{$volid}->{shared} = 1 if $drive->{shared};
4758 $volhash->{$volid}->{is_unused} //= 0;
4759 $volhash->{$volid}->{is_unused} = 1 if $key =~ /^unused\d+$/;
4761 $volhash->{$volid}->{is_attached} //= 0;
4762 $volhash->{$volid}->{is_attached} = 1
4763 if !$volhash->{$volid}->{is_unused} && !defined($snapname) && !$pending;
4765 $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
4766 if defined($snapname);
4768 $volhash->{$volid}->{referenced_in_pending} = 1 if $pending;
4770 my $size = $drive->{size};
4771 $volhash->{$volid}->{size} //= $size if $size;
4773 $volhash->{$volid}->{is_vmstate} //= 0;
4774 $volhash->{$volid}->{is_vmstate} = 1 if $key eq 'vmstate
';
4776 $volhash->{$volid}->{is_tpmstate} //= 0;
4777 $volhash->{$volid}->{is_tpmstate} = 1 if $key eq 'tpmstate0
';
4779 $volhash->{$volid}->{drivename} = $key if is_valid_drivename($key);
4782 my $include_opts = {
4783 extra_keys => ['vmstate
'],
4784 include_unused => 1,
4787 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $test_volid);
4789 PVE::QemuConfig->foreach_volume_full($conf->{pending}, $include_opts, $test_volid, undef, 1)
4790 if defined($conf->{pending}) && $conf->{pending}->%*;
4792 foreach my $snapname (keys %{$conf->{snapshots}}) {
4793 my $snap = $conf->{snapshots}->{$snapname};
4794 PVE::QemuConfig->foreach_volume_full($snap, $include_opts, $test_volid, $snapname);
4797 foreach my $volid (keys %$volhash) {
4798 &$func($volid, $volhash->{$volid}, @param);
4802 my $fast_plug_option = {
4806 'migrate_downtime
' => 1,
4807 'migrate_speed
' => 1,
4814 'vmstatestorage
' => 1,
4817 for my $opt (keys %$confdesc_cloudinit) {
4818 $fast_plug_option->{$opt} = 1;
4821 # hotplug changes in [PENDING]
4822 # $selection hash can be used to only apply specified options, for
4823 # example: { cores => 1 } (only apply changed 'cores
')
4824 # $errors ref is used to return error messages
4825 sub vmconfig_hotplug_pending {
4826 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4828 my $defaults = load_defaults();
4829 my $arch = get_vm_arch($conf);
4830 my $machine_type = get_vm_machine($conf, undef, $arch);
4832 # commit values which do not have any impact on running VM first
4833 # Note: those option cannot raise errors, we we do not care about
4834 # $selection and always apply them.
4836 my $add_error = sub {
4837 my ($opt, $msg) = @_;
4838 $errors->{$opt} = "hotplug problem - $msg";
4841 my $cloudinit_pending_properties = PVE::QemuServer::cloudinit_pending_properties();
4843 my $cloudinit_record_changed = sub {
4844 my ($conf, $opt, $old, $new) = @_;
4845 return if !$cloudinit_pending_properties->{$opt};
4847 my $ci = ($conf->{cloudinit} //= {});
4849 my $recorded = $ci->{$opt};
4850 my %added = map { $_ => 1 } PVE::Tools::split_list(delete($ci->{added}) // '');
4852 if (defined($new)) {
4853 if (defined($old)) {
4854 # an existing value is being modified
4855 if (defined($recorded)) {
4856 # the value was already not in sync
4857 if ($new eq $recorded) {
4858 # a value is being reverted to the cloud-init state:
4860 delete $added{$opt};
4862 # the value was changed multiple times, do nothing
4864 } elsif ($added{$opt}) {
4865 # the value had been marked as added and is being changed, do nothing
4867 # the value is new, record it:
4871 # a new value is being added
4872 if (defined($recorded)) {
4873 # it was already not in sync
4874 if ($new eq $recorded) {
4875 # a value is being reverted to the cloud-init state:
4877 delete $added{$opt};
4879 # the value had temporarily been removed, do nothing
4881 } elsif ($added{$opt}) {
4882 # the value had been marked as added already, do nothing
4884 # the value is new, add it
4888 } elsif (!defined($old)) {
4889 # a non-existent value is being removed? ignore...
4891 # a value is being deleted
4892 if (defined($recorded)) {
4893 # a value was already recorded, just keep it
4894 } elsif ($added{$opt}) {
4895 # the value was marked as added, remove it
4896 delete $added{$opt};
4898 # a previously unrecorded value is being removed, record the old value:
4903 my $added = join(',', sort keys %added);
4904 $ci->{added} = $added if length($added);
4908 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4909 if ($fast_plug_option->{$opt}) {
4910 my $new = delete $conf->{pending}->{$opt};
4911 $cloudinit_record_changed->($conf, $opt, $conf->{$opt}, $new);
4912 $conf->{$opt} = $new;
4918 PVE::QemuConfig->write_config($vmid, $conf);
4921 my $ostype = $conf->{ostype};
4922 my $version = extract_version($machine_type, get_running_qemu_version($vmid));
4923 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
4924 my $usb_hotplug = $hotplug_features->{usb}
4925 && min_version($version, 7, 1)
4926 && defined($ostype) && ($ostype eq 'l26
' || windows_version($ostype) > 7);
4928 my $cgroup = PVE::QemuServer::CGroup->new($vmid);
4929 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4931 foreach my $opt (sort keys %$pending_delete_hash) {
4932 next if $selection && !$selection->{$opt};
4933 my $force = $pending_delete_hash->{$opt}->{force};
4935 if ($opt eq 'hotplug
') {
4936 die "skip\n" if ($conf->{hotplug} =~ /(cpu|memory)/);
4937 } elsif ($opt eq 'tablet
') {
4938 die "skip\n" if !$hotplug_features->{usb};
4939 if ($defaults->{tablet}) {
4940 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4941 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4942 if $arch eq 'aarch64
';
4944 vm_deviceunplug($vmid, $conf, 'tablet
');
4945 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4947 } elsif ($opt =~ m/^usb(\d+)$/) {
4949 die "skip\n" if !$usb_hotplug;
4950 vm_deviceunplug($vmid, $conf, "usbredirdev$index"); # if it's a spice port
4951 vm_deviceunplug
($vmid, $conf, $opt);
4952 } elsif ($opt eq 'vcpus') {
4953 die "skip\n" if !$hotplug_features->{cpu
};
4954 qemu_cpu_hotplug
($vmid, $conf, undef);
4955 } elsif ($opt eq 'balloon') {
4956 # enable balloon device is not hotpluggable
4957 die "skip\n" if defined($conf->{balloon
}) && $conf->{balloon
} == 0;
4958 # here we reset the ballooning value to memory
4959 my $balloon = get_current_memory
($conf->{memory
});
4960 mon_cmd
($vmid, "balloon", value
=> $balloon*1024*1024);
4961 } elsif ($fast_plug_option->{$opt}) {
4963 } elsif ($opt =~ m/^net(\d+)$/) {
4964 die "skip\n" if !$hotplug_features->{network
};
4965 vm_deviceunplug
($vmid, $conf, $opt);
4967 my $net = PVE
::QemuServer
::parse_net
($conf->{$opt});
4968 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($net->{bridge
}, $net->{macaddr
}, $conf->{name
});
4970 } elsif (is_valid_drivename
($opt)) {
4971 die "skip\n" if !$hotplug_features->{disk
} || $opt =~ m/(ide|sata)(\d+)/;
4972 vm_deviceunplug
($vmid, $conf, $opt);
4973 vmconfig_delete_or_detach_drive
($vmid, $storecfg, $conf, $opt, $force);
4974 } elsif ($opt =~ m/^memory$/) {
4975 die "skip\n" if !$hotplug_features->{memory
};
4976 PVE
::QemuServer
::Memory
::qemu_memory_hotplug
($vmid, $conf);
4977 } elsif ($opt eq 'cpuunits') {
4978 $cgroup->change_cpu_shares(undef);
4979 } elsif ($opt eq 'cpulimit') {
4980 $cgroup->change_cpu_quota(undef, undef); # reset, cgroup module can better decide values
4986 &$add_error($opt, $err) if $err ne "skip\n";
4988 my $old = delete $conf->{$opt};
4989 $cloudinit_record_changed->($conf, $opt, $old, undef);
4990 PVE
::QemuConfig-
>remove_from_pending_delete($conf, $opt);
4995 foreach my $opt (keys %{$conf->{pending
}}) {
4996 next if $selection && !$selection->{$opt};
4997 my $value = $conf->{pending
}->{$opt};
4999 if ($opt eq 'hotplug') {
5000 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug
} =~ /memory/);
5001 die "skip\n" if ($value =~ /cpu/) || ($value !~ /cpu/ && $conf->{hotplug
} =~ /cpu/);
5002 } elsif ($opt eq 'tablet') {
5003 die "skip\n" if !$hotplug_features->{usb
};
5005 vm_deviceplug
($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
5006 vm_deviceplug
($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
5007 if $arch eq 'aarch64';
5008 } elsif ($value == 0) {
5009 vm_deviceunplug
($vmid, $conf, 'tablet');
5010 vm_deviceunplug
($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
5012 } elsif ($opt =~ m/^usb(\d+)$/) {
5014 die "skip\n" if !$usb_hotplug;
5015 my $d = eval { parse_property_string
('pve-qm-usb', $value) };
5017 if ($d->{host
} =~ m/^spice$/i) {
5018 $id = "usbredirdev$index";
5020 qemu_usb_hotplug
($storecfg, $conf, $vmid, $id, $d, $arch, $machine_type);
5021 } elsif ($opt eq 'vcpus') {
5022 die "skip\n" if !$hotplug_features->{cpu
};
5023 qemu_cpu_hotplug
($vmid, $conf, $value);
5024 } elsif ($opt eq 'balloon') {
5025 # enable/disable balloning device is not hotpluggable
5026 my $old_balloon_enabled = !!(!defined($conf->{balloon
}) || $conf->{balloon
});
5027 my $new_balloon_enabled = !!(!defined($conf->{pending
}->{balloon
}) || $conf->{pending
}->{balloon
});
5028 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
5030 # allow manual ballooning if shares is set to zero
5031 if ((defined($conf->{shares
}) && ($conf->{shares
} == 0))) {
5032 my $memory = get_current_memory
($conf->{memory
});
5033 my $balloon = $conf->{pending
}->{balloon
} || $memory;
5034 mon_cmd
($vmid, "balloon", value
=> $balloon*1024*1024);
5036 } elsif ($opt =~ m/^net(\d+)$/) {
5037 # some changes can be done without hotplug
5038 vmconfig_update_net
($storecfg, $conf, $hotplug_features->{network
},
5039 $vmid, $opt, $value, $arch, $machine_type);
5040 } elsif (is_valid_drivename
($opt)) {
5041 die "skip\n" if $opt eq 'efidisk0' || $opt eq 'tpmstate0';
5042 # some changes can be done without hotplug
5043 my $drive = parse_drive
($opt, $value);
5044 if (drive_is_cloudinit
($drive)) {
5045 $cloudinit_opt = [$opt, $drive];
5046 # apply all the other changes first, then generate the cloudinit disk
5049 vmconfig_update_disk
($storecfg, $conf, $hotplug_features->{disk
},
5050 $vmid, $opt, $value, $arch, $machine_type);
5051 } elsif ($opt =~ m/^memory$/) { #dimms
5052 die "skip\n" if !$hotplug_features->{memory
};
5053 $value = PVE
::QemuServer
::Memory
::qemu_memory_hotplug
($vmid, $conf, $value);
5054 } elsif ($opt eq 'cpuunits') {
5055 my $new_cpuunits = PVE
::CGroup
::clamp_cpu_shares
($conf->{pending
}->{$opt}); #clamp
5056 $cgroup->change_cpu_shares($new_cpuunits);
5057 } elsif ($opt eq 'cpulimit') {
5058 my $cpulimit = $conf->{pending
}->{$opt} == 0 ?
-1 : int($conf->{pending
}->{$opt} * 100000);
5059 $cgroup->change_cpu_quota($cpulimit, 100000);
5060 } elsif ($opt eq 'agent') {
5061 vmconfig_update_agent
($conf, $opt, $value);
5063 die "skip\n"; # skip non-hot-pluggable options
5067 &$add_error($opt, $err) if $err ne "skip\n";
5069 $cloudinit_record_changed->($conf, $opt, $conf->{$opt}, $value);
5070 $conf->{$opt} = $value;
5071 delete $conf->{pending
}->{$opt};
5075 if (defined($cloudinit_opt)) {
5076 my ($opt, $drive) = @$cloudinit_opt;
5077 my $value = $conf->{pending
}->{$opt};
5079 my $temp = {%$conf, $opt => $value};
5080 PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($temp, $vmid);
5081 vmconfig_update_disk
($storecfg, $conf, $hotplug_features->{disk
},
5082 $vmid, $opt, $value, $arch, $machine_type);
5085 &$add_error($opt, $err) if $err ne "skip\n";
5087 $conf->{$opt} = $value;
5088 delete $conf->{pending
}->{$opt};
5092 # unplug xhci controller if no usb device is left
5095 for (my $i = 0; $i < $PVE::QemuServer
::USB
::MAX_USB_DEVICES
; $i++) {
5096 next if !defined($conf->{"usb$i"});
5101 vm_deviceunplug
($vmid, $conf, 'xhci');
5105 PVE
::QemuConfig-
>write_config($vmid, $conf);
5107 if ($hotplug_features->{cloudinit
} && PVE
::QemuServer
::Cloudinit
::has_changes
($conf)) {
5108 PVE
::QemuServer
::vmconfig_update_cloudinit_drive
($storecfg, $conf, $vmid);
5112 sub try_deallocate_drive
{
5113 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
5115 if (($force || $key =~ /^unused/) && !drive_is_cdrom
($drive, 1)) {
5116 my $volid = $drive->{file
};
5117 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
5118 my $sid = PVE
::Storage
::parse_volume_id
($volid);
5119 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
5121 # check if the disk is really unused
5122 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
5123 if PVE
::QemuServer
::Drive
::is_volume_in_use
($storecfg, $conf, $key, $volid);
5124 PVE
::Storage
::vdisk_free
($storecfg, $volid);
5127 # If vm is not owner of this disk remove from config
5135 sub vmconfig_delete_or_detach_drive
{
5136 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
5138 my $drive = parse_drive
($opt, $conf->{$opt});
5140 my $rpcenv = PVE
::RPCEnvironment
::get
();
5141 my $authuser = $rpcenv->get_user();
5144 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
5145 try_deallocate_drive
($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
5147 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $drive);
5153 sub vmconfig_apply_pending
{
5154 my ($vmid, $conf, $storecfg, $errors, $skip_cloud_init) = @_;
5156 return if !scalar(keys %{$conf->{pending
}});
5158 my $add_apply_error = sub {
5159 my ($opt, $msg) = @_;
5160 my $err_msg = "unable to apply pending change $opt : $msg";
5161 $errors->{$opt} = $err_msg;
5167 my $pending_delete_hash = PVE
::QemuConfig-
>parse_pending_delete($conf->{pending
}->{delete});
5168 foreach my $opt (sort keys %$pending_delete_hash) {
5169 my $force = $pending_delete_hash->{$opt}->{force
};
5171 if ($opt =~ m/^unused/) {
5172 die "internal error";
5173 } elsif (defined($conf->{$opt}) && is_valid_drivename
($opt)) {
5174 vmconfig_delete_or_detach_drive
($vmid, $storecfg, $conf, $opt, $force);
5175 } elsif (defined($conf->{$opt}) && $opt =~ m/^net\d+$/) {
5177 my $net = PVE
::QemuServer
::parse_net
($conf->{$opt});
5178 eval { PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($net->{bridge
}, $net->{macaddr
}, $conf->{name
}) };
5184 $add_apply_error->($opt, $err);
5186 PVE
::QemuConfig-
>remove_from_pending_delete($conf, $opt);
5187 delete $conf->{$opt};
5191 PVE
::QemuConfig-
>cleanup_pending($conf);
5193 my $generate_cloudinit = $skip_cloud_init ?
0 : undef;
5195 foreach my $opt (keys %{$conf->{pending
}}) { # add/change
5196 next if $opt eq 'delete'; # just to be sure
5198 if (defined($conf->{$opt}) && is_valid_drivename
($opt)) {
5199 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, parse_drive
($opt, $conf->{$opt}))
5200 } elsif (defined($conf->{pending
}->{$opt}) && $opt =~ m/^net\d+$/) {
5201 return if !$have_sdn; # return from eval if SDN is not available
5203 my $new_net = PVE
::QemuServer
::parse_net
($conf->{pending
}->{$opt});
5204 if ($conf->{$opt}) {
5205 my $old_net = PVE
::QemuServer
::parse_net
($conf->{$opt});
5207 if (defined($old_net->{bridge
}) && defined($old_net->{macaddr
}) && (
5208 safe_string_ne
($old_net->{bridge
}, $new_net->{bridge
}) ||
5209 safe_string_ne
($old_net->{macaddr
}, $new_net->{macaddr
})
5211 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($old_net->{bridge
}, $old_net->{macaddr
}, $conf->{name
});
5214 #fixme: reuse ip if mac change && same bridge
5215 PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($new_net->{bridge
}, $conf->{name
}, $new_net->{macaddr
}, $vmid, undef, 1);
5219 $add_apply_error->($opt, $err);
5222 if (is_valid_drivename
($opt)) {
5223 my $drive = parse_drive
($opt, $conf->{pending
}->{$opt});
5224 $generate_cloudinit //= 1 if drive_is_cloudinit
($drive);
5227 $conf->{$opt} = delete $conf->{pending
}->{$opt};
5231 # write all changes at once to avoid unnecessary i/o
5232 PVE
::QemuConfig-
>write_config($vmid, $conf);
5233 if ($generate_cloudinit) {
5234 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5235 # After successful generation and if there were changes to be applied, update the
5236 # config to drop the {cloudinit} entry.
5237 PVE
::QemuConfig-
>write_config($vmid, $conf);
5242 sub vmconfig_update_net
{
5243 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5245 my $newnet = parse_net
($value);
5247 if ($conf->{$opt}) {
5248 my $oldnet = parse_net
($conf->{$opt});
5250 if (safe_string_ne
($oldnet->{model
}, $newnet->{model
}) ||
5251 safe_string_ne
($oldnet->{macaddr
}, $newnet->{macaddr
}) ||
5252 safe_num_ne
($oldnet->{queues
}, $newnet->{queues
}) ||
5253 safe_num_ne
($oldnet->{mtu
}, $newnet->{mtu
}) ||
5254 !($newnet->{bridge
} && $oldnet->{bridge
})
5255 ) { # bridge/nat mode change
5257 # for non online change, we try to hot-unplug
5258 die "skip\n" if !$hotplug;
5259 vm_deviceunplug
($vmid, $conf, $opt);
5262 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($oldnet->{bridge
}, $oldnet->{macaddr
}, $conf->{name
});
5267 die "internal error" if $opt !~ m/net(\d+)/;
5268 my $iface = "tap${vmid}i$1";
5270 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
}) ||
5271 safe_num_ne
($oldnet->{tag
}, $newnet->{tag
}) ||
5272 safe_string_ne
($oldnet->{trunks
}, $newnet->{trunks
}) ||
5273 safe_num_ne
($oldnet->{firewall
}, $newnet->{firewall
})
5275 PVE
::Network
::tap_unplug
($iface);
5277 #set link_down in guest if bridge or vlan change to notify guest (dhcp renew for example)
5278 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
}) ||
5279 safe_num_ne
($oldnet->{tag
}, $newnet->{tag
})
5281 qemu_set_link_status
($vmid, $opt, 0);
5284 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
})) {
5286 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($oldnet->{bridge
}, $oldnet->{macaddr
}, $conf->{name
});
5287 PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($newnet->{bridge
}, $conf->{name
}, $newnet->{macaddr
}, $vmid, undef, 1);
5292 PVE
::Network
::SDN
::Zones
::tap_plug
($iface, $newnet->{bridge
}, $newnet->{tag
}, $newnet->{firewall
}, $newnet->{trunks
}, $newnet->{rate
});
5294 PVE
::Network
::tap_plug
($iface, $newnet->{bridge
}, $newnet->{tag
}, $newnet->{firewall
}, $newnet->{trunks
}, $newnet->{rate
});
5297 #set link_up in guest if bridge or vlan change to notify guest (dhcp renew for example)
5298 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
}) ||
5299 safe_num_ne
($oldnet->{tag
}, $newnet->{tag
})
5301 qemu_set_link_status
($vmid, $opt, 1);
5304 } elsif (safe_num_ne
($oldnet->{rate
}, $newnet->{rate
})) {
5305 # Rate can be applied on its own but any change above needs to
5306 # include the rate in tap_plug since OVS resets everything.
5307 PVE
::Network
::tap_rate_limit
($iface, $newnet->{rate
});
5310 if (safe_string_ne
($oldnet->{link_down
}, $newnet->{link_down
})) {
5311 qemu_set_link_status
($vmid, $opt, !$newnet->{link_down
});
5320 PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($newnet->{bridge
}, $conf->{name
}, $newnet->{macaddr
}, $vmid, undef, 1);
5321 PVE
::Network
::SDN
::Vnets
::add_dhcp_mapping
($newnet->{bridge
}, $newnet->{macaddr
}, $vmid, $conf->{name
});
5323 vm_deviceplug
($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
5329 sub vmconfig_update_agent
{
5330 my ($conf, $opt, $value) = @_;
5332 die "skip\n" if !$conf->{$opt};
5334 my $hotplug_options = { fstrim_cloned_disks
=> 1 };
5336 my $old_agent = parse_guest_agent
($conf);
5337 my $agent = parse_guest_agent
({$opt => $value});
5339 for my $option (keys %$agent) { # added/changed options
5340 next if defined($hotplug_options->{$option});
5341 die "skip\n" if safe_string_ne
($agent->{$option}, $old_agent->{$option});
5344 for my $option (keys %$old_agent) { # removed options
5345 next if defined($hotplug_options->{$option});
5346 die "skip\n" if safe_string_ne
($old_agent->{$option}, $agent->{$option});
5349 return; # either no actual change (e.g., format string reordered) or just hotpluggable changes
5352 sub vmconfig_update_disk
{
5353 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5355 my $drive = parse_drive
($opt, $value);
5357 if ($conf->{$opt} && (my $old_drive = parse_drive
($opt, $conf->{$opt}))) {
5358 my $media = $drive->{media
} || 'disk';
5359 my $oldmedia = $old_drive->{media
} || 'disk';
5360 die "unable to change media type\n" if $media ne $oldmedia;
5362 if (!drive_is_cdrom
($old_drive)) {
5364 if ($drive->{file
} ne $old_drive->{file
}) {
5366 die "skip\n" if !$hotplug;
5368 # unplug and register as unused
5369 vm_deviceunplug
($vmid, $conf, $opt);
5370 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $old_drive)
5373 # update existing disk
5375 # skip non hotpluggable value
5376 if (safe_string_ne
($drive->{aio
}, $old_drive->{aio
}) ||
5377 safe_string_ne
($drive->{discard
}, $old_drive->{discard
}) ||
5378 safe_string_ne
($drive->{iothread
}, $old_drive->{iothread
}) ||
5379 safe_string_ne
($drive->{queues
}, $old_drive->{queues
}) ||
5380 safe_string_ne
($drive->{product
}, $old_drive->{product
}) ||
5381 safe_string_ne
($drive->{cache
}, $old_drive->{cache
}) ||
5382 safe_string_ne
($drive->{ssd
}, $old_drive->{ssd
}) ||
5383 safe_string_ne
($drive->{vendor
}, $old_drive->{vendor
}) ||
5384 safe_string_ne
($drive->{ro
}, $old_drive->{ro
})) {
5389 if (safe_num_ne
($drive->{mbps
}, $old_drive->{mbps
}) ||
5390 safe_num_ne
($drive->{mbps_rd
}, $old_drive->{mbps_rd
}) ||
5391 safe_num_ne
($drive->{mbps_wr
}, $old_drive->{mbps_wr
}) ||
5392 safe_num_ne
($drive->{iops
}, $old_drive->{iops
}) ||
5393 safe_num_ne
($drive->{iops_rd
}, $old_drive->{iops_rd
}) ||
5394 safe_num_ne
($drive->{iops_wr
}, $old_drive->{iops_wr
}) ||
5395 safe_num_ne
($drive->{mbps_max
}, $old_drive->{mbps_max
}) ||
5396 safe_num_ne
($drive->{mbps_rd_max
}, $old_drive->{mbps_rd_max
}) ||
5397 safe_num_ne
($drive->{mbps_wr_max
}, $old_drive->{mbps_wr_max
}) ||
5398 safe_num_ne
($drive->{iops_max
}, $old_drive->{iops_max
}) ||
5399 safe_num_ne
($drive->{iops_rd_max
}, $old_drive->{iops_rd_max
}) ||
5400 safe_num_ne
($drive->{iops_wr_max
}, $old_drive->{iops_wr_max
}) ||
5401 safe_num_ne
($drive->{bps_max_length
}, $old_drive->{bps_max_length
}) ||
5402 safe_num_ne
($drive->{bps_rd_max_length
}, $old_drive->{bps_rd_max_length
}) ||
5403 safe_num_ne
($drive->{bps_wr_max_length
}, $old_drive->{bps_wr_max_length
}) ||
5404 safe_num_ne
($drive->{iops_max_length
}, $old_drive->{iops_max_length
}) ||
5405 safe_num_ne
($drive->{iops_rd_max_length
}, $old_drive->{iops_rd_max_length
}) ||
5406 safe_num_ne
($drive->{iops_wr_max_length
}, $old_drive->{iops_wr_max_length
})) {
5408 qemu_block_set_io_throttle
(
5410 ($drive->{mbps
} || 0)*1024*1024,
5411 ($drive->{mbps_rd
} || 0)*1024*1024,
5412 ($drive->{mbps_wr
} || 0)*1024*1024,
5413 $drive->{iops
} || 0,
5414 $drive->{iops_rd
} || 0,
5415 $drive->{iops_wr
} || 0,
5416 ($drive->{mbps_max
} || 0)*1024*1024,
5417 ($drive->{mbps_rd_max
} || 0)*1024*1024,
5418 ($drive->{mbps_wr_max
} || 0)*1024*1024,
5419 $drive->{iops_max
} || 0,
5420 $drive->{iops_rd_max
} || 0,
5421 $drive->{iops_wr_max
} || 0,
5422 $drive->{bps_max_length
} || 1,
5423 $drive->{bps_rd_max_length
} || 1,
5424 $drive->{bps_wr_max_length
} || 1,
5425 $drive->{iops_max_length
} || 1,
5426 $drive->{iops_rd_max_length
} || 1,
5427 $drive->{iops_wr_max_length
} || 1,
5437 if ($drive->{file
} eq 'none') {
5438 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$opt");
5439 if (drive_is_cloudinit
($old_drive)) {
5440 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $old_drive);
5443 my $path = get_iso_path
($storecfg, $vmid, $drive->{file
});
5445 # force eject if locked
5446 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$opt");
5449 mon_cmd
($vmid, "blockdev-change-medium",
5450 id
=> "$opt", filename
=> "$path");
5458 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
5460 PVE
::Storage
::activate_volumes
($storecfg, [$drive->{file
}]) if $drive->{file
} !~ m
|^/dev/.+|;
5461 vm_deviceplug
($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
5464 sub vmconfig_update_cloudinit_drive
{
5465 my ($storecfg, $conf, $vmid) = @_;
5467 my $cloudinit_ds = undef;
5468 my $cloudinit_drive = undef;
5470 PVE
::QemuConfig-
>foreach_volume($conf, sub {
5471 my ($ds, $drive) = @_;
5472 if (PVE
::QemuServer
::drive_is_cloudinit
($drive)) {
5473 $cloudinit_ds = $ds;
5474 $cloudinit_drive = $drive;
5478 return if !$cloudinit_drive;
5480 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5481 PVE
::QemuConfig-
>write_config($vmid, $conf);
5484 my $running = PVE
::QemuServer
::check_running
($vmid);
5487 my $path = PVE
::Storage
::path
($storecfg, $cloudinit_drive->{file
});
5489 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$cloudinit_ds");
5490 mon_cmd
($vmid, "blockdev-change-medium", id
=> "$cloudinit_ds", filename
=> "$path");
5495 # called in locked context by incoming migration
5496 sub vm_migrate_get_nbd_disks
{
5497 my ($storecfg, $conf, $replicated_volumes) = @_;
5499 my $local_volumes = {};
5500 PVE
::QemuConfig-
>foreach_volume($conf, sub {
5501 my ($ds, $drive) = @_;
5503 return if drive_is_cdrom
($drive);
5504 return if $ds eq 'tpmstate0';
5506 my $volid = $drive->{file
};
5510 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
5512 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
5513 return if $scfg->{shared
};
5515 my $format = qemu_img_format
($scfg, $volname);
5517 # replicated disks re-use existing state via bitmap
5518 my $use_existing = $replicated_volumes->{$volid} ?
1 : 0;
5519 $local_volumes->{$ds} = [$volid, $storeid, $drive, $use_existing, $format];
5521 return $local_volumes;
5524 # called in locked context by incoming migration
5525 sub vm_migrate_alloc_nbd_disks
{
5526 my ($storecfg, $vmid, $source_volumes, $storagemap) = @_;
5529 foreach my $opt (sort keys %$source_volumes) {
5530 my ($volid, $storeid, $drive, $use_existing, $format) = @{$source_volumes->{$opt}};
5532 if ($use_existing) {
5533 $nbd->{$opt}->{drivestr
} = print_drive
($drive);
5534 $nbd->{$opt}->{volid
} = $volid;
5535 $nbd->{$opt}->{replicated
} = 1;
5539 $storeid = PVE
::JSONSchema
::map_id
($storagemap, $storeid);
5541 # order of precedence, filtered by whether storage supports it:
5542 # 1. explicit requested format
5543 # 2. default format of storage
5544 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
5545 $format = $defFormat if !$format || !grep { $format eq $_ } $validFormats->@*;
5547 my $size = $drive->{size
} / 1024;
5548 my $newvolid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $format, undef, $size);
5549 my $newdrive = $drive;
5550 $newdrive->{format
} = $format;
5551 $newdrive->{file
} = $newvolid;
5552 my $drivestr = print_drive
($newdrive);
5553 $nbd->{$opt}->{drivestr
} = $drivestr;
5554 $nbd->{$opt}->{volid
} = $newvolid;
5560 # see vm_start_nolock for parameters, additionally:
5562 # storagemap = parsed storage map for allocating NBD disks
5564 my ($storecfg, $vmid, $params, $migrate_opts) = @_;
5566 return PVE
::QemuConfig-
>lock_config($vmid, sub {
5567 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migrate_opts->{migratedfrom
});
5569 die "you can't start a vm if it's a template\n"
5570 if !$params->{skiptemplate
} && PVE
::QemuConfig-
>is_template($conf);
5572 my $has_suspended_lock = PVE
::QemuConfig-
>has_lock($conf, 'suspended');
5573 my $has_backup_lock = PVE
::QemuConfig-
>has_lock($conf, 'backup');
5575 my $running = check_running
($vmid, undef, $migrate_opts->{migratedfrom
});
5577 if ($has_backup_lock && $running) {
5578 # a backup is currently running, attempt to start the guest in the
5579 # existing QEMU instance
5580 return vm_resume
($vmid);
5583 PVE
::QemuConfig-
>check_lock($conf)
5584 if !($params->{skiplock
} || $has_suspended_lock);
5586 $params->{resume
} = $has_suspended_lock || defined($conf->{vmstate
});
5588 die "VM $vmid already running\n" if $running;
5590 if (my $storagemap = $migrate_opts->{storagemap
}) {
5591 my $replicated = $migrate_opts->{replicated_volumes
};
5592 my $disks = vm_migrate_get_nbd_disks
($storecfg, $conf, $replicated);
5593 $migrate_opts->{nbd
} = vm_migrate_alloc_nbd_disks
($storecfg, $vmid, $disks, $storagemap);
5595 foreach my $opt (keys %{$migrate_opts->{nbd
}}) {
5596 $conf->{$opt} = $migrate_opts->{nbd
}->{$opt}->{drivestr
};
5600 return vm_start_nolock
($storecfg, $vmid, $conf, $params, $migrate_opts);
5606 # statefile => 'tcp', 'unix' for migration or path/volid for RAM state
5607 # skiplock => 0/1, skip checking for config lock
5608 # skiptemplate => 0/1, skip checking whether VM is template
5609 # forcemachine => to force QEMU machine (rollback/migration)
5610 # forcecpu => a QEMU '-cpu' argument string to override get_cpu_options
5611 # timeout => in seconds
5612 # paused => start VM in paused state (backup)
5613 # resume => resume from hibernation
5614 # live-restore-backing => {
5616 # name => blockdev-name,
5617 # blockdev => "arg to the -blockdev command instantiating device named 'name'",
5622 # nbd => volumes for NBD exports (vm_migrate_alloc_nbd_disks)
5623 # migratedfrom => source node
5624 # spice_ticket => used for spice migration, passed via tunnel/stdin
5625 # network => CIDR of migration network
5626 # type => secure/insecure - tunnel over encrypted connection or plain-text
5627 # nbd_proto_version => int, 0 for TCP, 1 for UNIX
5628 # replicated_volumes => which volids should be re-used with bitmaps for nbd migration
5629 # offline_volumes => new volids of offline migrated disks like tpmstate and cloudinit, not yet
5630 # contained in config
5631 sub vm_start_nolock
{
5632 my ($storecfg, $vmid, $conf, $params, $migrate_opts) = @_;
5634 my $statefile = $params->{statefile
};
5635 my $resume = $params->{resume
};
5637 my $migratedfrom = $migrate_opts->{migratedfrom
};
5638 my $migration_type = $migrate_opts->{type
};
5642 # clean up leftover reboot request files
5643 eval { clear_reboot_request
($vmid); };
5646 if (!$statefile && scalar(keys %{$conf->{pending
}})) {
5647 vmconfig_apply_pending
($vmid, $conf, $storecfg);
5648 $conf = PVE
::QemuConfig-
>load_config($vmid); # update/reload
5651 # don't regenerate the ISO if the VM is started as part of a live migration
5652 # this way we can reuse the old ISO with the correct config
5653 if (!$migratedfrom) {
5654 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5655 # FIXME: apply_cloudinit_config updates $conf in this case, and it would only drop
5656 # $conf->{cloudinit}, so we could just not do this?
5657 # But we do it above, so for now let's be consistent.
5658 $conf = PVE
::QemuConfig-
>load_config($vmid); # update/reload
5662 # override offline migrated volumes, conf is out of date still
5663 if (my $offline_volumes = $migrate_opts->{offline_volumes
}) {
5664 for my $key (sort keys $offline_volumes->%*) {
5665 my $parsed = parse_drive
($key, $conf->{$key});
5666 $parsed->{file
} = $offline_volumes->{$key};
5667 $conf->{$key} = print_drive
($parsed);
5671 my $defaults = load_defaults
();
5673 # set environment variable useful inside network script
5674 # for remote migration the config is available on the target node!
5675 if (!$migrate_opts->{remote_node
}) {
5676 $ENV{PVE_MIGRATED_FROM
} = $migratedfrom;
5679 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-start', 1);
5681 my $forcemachine = $params->{forcemachine
};
5682 my $forcecpu = $params->{forcecpu
};
5684 # enforce machine and CPU type on suspended vm to ensure HW compatibility
5685 $forcemachine = $conf->{runningmachine
};
5686 $forcecpu = $conf->{runningcpu
};
5687 print "Resuming suspended VM\n";
5690 my ($cmd, $vollist, $spice_port, $pci_devices) = config_to_command
($storecfg, $vmid,
5691 $conf, $defaults, $forcemachine, $forcecpu, $params->{'live-restore-backing'});
5694 my $get_migration_ip = sub {
5695 my ($nodename) = @_;
5697 return $migration_ip if defined($migration_ip);
5699 my $cidr = $migrate_opts->{network
};
5701 if (!defined($cidr)) {
5702 my $dc_conf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5703 $cidr = $dc_conf->{migration
}->{network
};
5706 if (defined($cidr)) {
5707 my $ips = PVE
::Network
::get_local_ip_from_cidr
($cidr);
5709 die "could not get IP: no address configured on local " .
5710 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5712 die "could not get IP: multiple addresses configured on local " .
5713 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5715 $migration_ip = @$ips[0];
5718 $migration_ip = PVE
::Cluster
::remote_node_ip
($nodename, 1)
5719 if !defined($migration_ip);
5721 return $migration_ip;
5725 if ($statefile eq 'tcp') {
5726 my $migrate = $res->{migrate
} = { proto
=> 'tcp' };
5727 $migrate->{addr
} = "localhost";
5728 my $datacenterconf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5729 my $nodename = nodename
();
5731 if (!defined($migration_type)) {
5732 if (defined($datacenterconf->{migration
}->{type
})) {
5733 $migration_type = $datacenterconf->{migration
}->{type
};
5735 $migration_type = 'secure';
5739 if ($migration_type eq 'insecure') {
5740 $migrate->{addr
} = $get_migration_ip->($nodename);
5741 $migrate->{addr
} = "[$migrate->{addr}]" if Net
::IP
::ip_is_ipv6
($migrate->{addr
});
5744 # see #4501: port reservation should be done close to usage - tell QEMU where to listen
5746 push @$cmd, '-incoming', 'defer';
5749 } elsif ($statefile eq 'unix') {
5750 # should be default for secure migrations as a ssh TCP forward
5751 # tunnel is not deterministic reliable ready and fails regurarly
5752 # to set up in time, so use UNIX socket forwards
5753 my $migrate = $res->{migrate
} = { proto
=> 'unix' };
5754 $migrate->{addr
} = "/run/qemu-server/$vmid.migrate";
5755 unlink $migrate->{addr
};
5757 $migrate->{uri
} = "unix:$migrate->{addr}";
5758 push @$cmd, '-incoming', $migrate->{uri
};
5761 } elsif (-e
$statefile) {
5762 push @$cmd, '-loadstate', $statefile;
5764 my $statepath = PVE
::Storage
::path
($storecfg, $statefile);
5765 push @$vollist, $statefile;
5766 push @$cmd, '-loadstate', $statepath;
5768 } elsif ($params->{paused
}) {
5772 my $memory = get_current_memory
($conf->{memory
});
5773 my $start_timeout = $params->{timeout
} // config_aware_timeout
($conf, $memory, $resume);
5775 my $pci_reserve_list = [];
5776 for my $device (values $pci_devices->%*) {
5777 next if $device->{mdev
}; # we don't reserve for mdev devices
5778 push $pci_reserve_list->@*, map { $_->{id
} } $device->{ids
}->@*;
5781 # reserve all PCI IDs before actually doing anything with them
5782 PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_reserve_list, $vmid, $start_timeout);
5786 for my $id (sort keys %$pci_devices) {
5787 my $d = $pci_devices->{$id};
5788 my ($index) = ($id =~ m/^hostpci(\d+)$/);
5791 for my $dev ($d->{ids
}->@*) {
5792 my $info = eval { PVE
::QemuServer
::PCI
::prepare_pci_device
($vmid, $dev->{id
}, $index, $d->{mdev
}) };
5795 $chosen_mdev = $info;
5796 last if $chosen_mdev; # if successful, we're done
5802 next if !$d->{mdev
};
5803 die "could not create mediated device\n" if !defined($chosen_mdev);
5805 # nvidia grid needs the uuid of the mdev as qemu parameter
5806 if (!defined($uuid) && $chosen_mdev->{vendor
} =~ m/^(0x)?10de$/) {
5807 if (defined($conf->{smbios1
})) {
5808 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
5809 $uuid = $smbios_conf->{uuid
} if defined($smbios_conf->{uuid
});
5811 $uuid = PVE
::QemuServer
::PCI
::generate_mdev_uuid
($vmid, $index) if !defined($uuid);
5814 push @$cmd, '-uuid', $uuid if defined($uuid);
5817 eval { cleanup_pci_devices
($vmid, $conf) };
5822 PVE
::Storage
::activate_volumes
($storecfg, $vollist);
5825 my %silence_std_outs = (outfunc
=> sub {}, errfunc
=> sub {});
5826 eval { run_command
(['/bin/systemctl', 'reset-failed', "$vmid.scope"], %silence_std_outs) };
5827 eval { run_command
(['/bin/systemctl', 'stop', "$vmid.scope"], %silence_std_outs) };
5828 # Issues with the above 'stop' not being fully completed are extremely rare, a very low
5829 # timeout should be more than enough here...
5830 PVE
::Systemd
::wait_for_unit_removed
("$vmid.scope", 20);
5832 my $cpuunits = PVE
::CGroup
::clamp_cpu_shares
($conf->{cpuunits
});
5835 timeout
=> $statefile ?
undef : $start_timeout,
5840 # when migrating, prefix QEMU output so other side can pick up any
5841 # errors that might occur and show the user
5842 if ($migratedfrom) {
5843 $run_params{quiet
} = 1;
5844 $run_params{logfunc
} = sub { print "QEMU: $_[0]\n" };
5847 my %systemd_properties = (
5848 Slice
=> 'qemu.slice',
5849 KillMode
=> 'process',
5851 TimeoutStopUSec
=> ULONG_MAX
, # infinity
5854 if (PVE
::CGroup
::cgroup_mode
() == 2) {
5855 $systemd_properties{CPUWeight
} = $cpuunits;
5857 $systemd_properties{CPUShares
} = $cpuunits;
5860 if (my $cpulimit = $conf->{cpulimit
}) {
5861 $systemd_properties{CPUQuota
} = int($cpulimit * 100);
5863 $systemd_properties{timeout
} = 10 if $statefile; # setting up the scope shoul be quick
5865 my $run_qemu = sub {
5866 PVE
::Tools
::run_fork
sub {
5867 PVE
::Systemd
::enter_systemd_scope
($vmid, "Proxmox VE VM $vmid", %systemd_properties);
5870 if ((my $tpm = $conf->{tpmstate0
}) && !PVE
::QemuConfig-
>is_template($conf)) {
5871 # start the TPM emulator so QEMU can connect on start
5872 $tpmpid = start_swtpm
($storecfg, $vmid, $tpm, $migratedfrom);
5875 my $exitcode = run_command
($cmd, %run_params);
5878 warn "stopping swtpm instance (pid $tpmpid) due to QEMU startup error\n";
5879 kill 'TERM', $tpmpid;
5881 die "QEMU exited with code $exitcode\n";
5886 if ($conf->{hugepages
}) {
5889 my $hotplug_features =
5890 parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
5891 my $hugepages_topology =
5892 PVE
::QemuServer
::Memory
::hugepages_topology
($conf, $hotplug_features->{memory
});
5894 my $hugepages_host_topology = PVE
::QemuServer
::Memory
::hugepages_host_topology
();
5896 PVE
::QemuServer
::Memory
::hugepages_mount
();
5897 PVE
::QemuServer
::Memory
::hugepages_allocate
($hugepages_topology, $hugepages_host_topology);
5899 eval { $run_qemu->() };
5901 PVE
::QemuServer
::Memory
::hugepages_reset
($hugepages_host_topology)
5902 if !$conf->{keephugepages
};
5906 PVE
::QemuServer
::Memory
::hugepages_pre_deallocate
($hugepages_topology)
5907 if !$conf->{keephugepages
};
5909 eval { PVE
::QemuServer
::Memory
::hugepages_update_locked
($code); };
5912 eval { $run_qemu->() };
5916 # deactivate volumes if start fails
5917 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
5919 eval { cleanup_pci_devices
($vmid, $conf) };
5922 die "start failed: $err";
5925 # re-reserve all PCI IDs now that we can know the actual VM PID
5926 my $pid = PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
5927 eval { PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_reserve_list, $vmid, undef, $pid) };
5930 if (defined(my $migrate = $res->{migrate
})) {
5931 if ($migrate->{proto
} eq 'tcp') {
5932 my $nodename = nodename
();
5933 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
5934 $migrate->{port
} = PVE
::Tools
::next_migrate_port
($pfamily);
5935 $migrate->{uri
} = "tcp:$migrate->{addr}:$migrate->{port}";
5936 mon_cmd
($vmid, "migrate-incoming", uri
=> $migrate->{uri
});
5938 print "migration listens on $migrate->{uri}\n";
5939 } elsif ($statefile) {
5940 eval { mon_cmd
($vmid, "cont"); };
5944 #start nbd server for storage migration
5945 if (my $nbd = $migrate_opts->{nbd
}) {
5946 my $nbd_protocol_version = $migrate_opts->{nbd_proto_version
} // 0;
5948 my $migrate_storage_uri;
5949 # nbd_protocol_version > 0 for unix socket support
5950 if ($nbd_protocol_version > 0 && ($migration_type eq 'secure' || $migration_type eq 'websocket')) {
5951 my $socket_path = "/run/qemu-server/$vmid\_nbd.migrate";
5952 mon_cmd
($vmid, "nbd-server-start", addr
=> { type
=> 'unix', data
=> { path
=> $socket_path } } );
5953 $migrate_storage_uri = "nbd:unix:$socket_path";
5954 $res->{migrate
}->{unix_sockets
} = [$socket_path];
5956 my $nodename = nodename
();
5957 my $localip = $get_migration_ip->($nodename);
5958 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
5959 my $storage_migrate_port = PVE
::Tools
::next_migrate_port
($pfamily);
5961 mon_cmd
($vmid, "nbd-server-start", addr
=> {
5964 host
=> "${localip}",
5965 port
=> "${storage_migrate_port}",
5968 $localip = "[$localip]" if Net
::IP
::ip_is_ipv6
($localip);
5969 $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}";
5972 my $block_info = mon_cmd
($vmid, "query-block");
5973 $block_info = { map { $_->{device
} => $_ } $block_info->@* };
5975 foreach my $opt (sort keys %$nbd) {
5976 my $drivestr = $nbd->{$opt}->{drivestr
};
5977 my $volid = $nbd->{$opt}->{volid
};
5979 my $block_node = $block_info->{"drive-$opt"}->{inserted
}->{'node-name'};
5985 'node-name' => $block_node,
5986 writable
=> JSON
::true
,
5988 name
=> "drive-$opt", # NBD export name
5991 my $nbd_uri = "$migrate_storage_uri:exportname=drive-$opt";
5992 print "storage migration listens on $nbd_uri volume:$drivestr\n";
5993 print "re-using replicated volume: $opt - $volid\n"
5994 if $nbd->{$opt}->{replicated
};
5996 $res->{drives
}->{$opt} = $nbd->{$opt};
5997 $res->{drives
}->{$opt}->{nbd_uri
} = $nbd_uri;
6001 if ($migratedfrom) {
6003 set_migration_caps
($vmid);
6008 print "spice listens on port $spice_port\n";
6009 $res->{spice_port
} = $spice_port;
6010 if ($migrate_opts->{spice_ticket
}) {
6011 mon_cmd
($vmid, "set_password", protocol
=> 'spice', password
=>
6012 $migrate_opts->{spice_ticket
});
6013 mon_cmd
($vmid, "expire_password", protocol
=> 'spice', time => "+30");
6018 mon_cmd
($vmid, "balloon", value
=> $conf->{balloon
}*1024*1024)
6019 if !$statefile && $conf->{balloon
};
6021 foreach my $opt (keys %$conf) {
6022 next if $opt !~ m/^net\d+$/;
6023 my $nicconf = parse_net
($conf->{$opt});
6024 qemu_set_link_status
($vmid, $opt, 0) if $nicconf->{link_down
};
6026 add_nets_bridge_fdb
($conf, $vmid);
6029 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
6034 path
=> "machine/peripheral/balloon0",
6035 property
=> "guest-stats-polling-interval",
6039 log_warn
("could not set polling interval for ballooning - $@") if $@;
6043 print "Resumed VM, removing state\n";
6044 if (my $vmstate = $conf->{vmstate
}) {
6045 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
6046 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
6048 delete $conf->@{qw(lock vmstate runningmachine runningcpu)};
6049 PVE
::QemuConfig-
>write_config($vmid, $conf);
6052 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'post-start');
6054 my ($current_machine, $is_deprecated) =
6055 PVE
::QemuServer
::Machine
::get_current_qemu_machine
($vmid);
6056 if ($is_deprecated) {
6058 "current machine version '$current_machine' is deprecated - see the documentation and ".
6059 "change to a newer one",
6066 sub vm_commandline
{
6067 my ($storecfg, $vmid, $snapname) = @_;
6069 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6071 my ($forcemachine, $forcecpu);
6073 my $snapshot = $conf->{snapshots
}->{$snapname};
6074 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
6076 # check for machine or CPU overrides in snapshot
6077 $forcemachine = $snapshot->{runningmachine
};
6078 $forcecpu = $snapshot->{runningcpu
};
6080 $snapshot->{digest
} = $conf->{digest
}; # keep file digest for API
6085 my $defaults = load_defaults
();
6087 my $cmd = config_to_command
($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu);
6089 return PVE
::Tools
::cmd2string
($cmd);
6093 my ($vmid, $skiplock) = @_;
6095 PVE
::QemuConfig-
>lock_config($vmid, sub {
6097 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6099 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
6101 mon_cmd
($vmid, "system_reset");
6105 sub get_vm_volumes
{
6109 foreach_volid
($conf, sub {
6110 my ($volid, $attr) = @_;
6112 return if $volid =~ m
|^/|;
6114 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
6117 push @$vollist, $volid;
6123 sub cleanup_pci_devices
{
6124 my ($vmid, $conf) = @_;
6126 foreach my $key (keys %$conf) {
6127 next if $key !~ m/^hostpci(\d+)$/;
6128 my $hostpciindex = $1;
6129 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $hostpciindex);
6130 my $d = parse_hostpci
($conf->{$key});
6132 # NOTE: avoid PVE::SysFSTools::pci_cleanup_mdev_device as it requires PCI ID and we
6133 # don't want to break ABI just for this two liner
6134 my $dev_sysfs_dir = "/sys/bus/mdev/devices/$uuid";
6136 # some nvidia vgpu driver versions want to clean the mdevs up themselves, and error
6137 # out when we do it first. so wait for up to 10 seconds and then try it manually
6138 if ($d->{ids
}->[0]->[0]->{vendor
} =~ m/^(0x)?10de$/ && -e
$dev_sysfs_dir) {
6140 while (-e
$dev_sysfs_dir && $count < 10) {
6144 print "waited $count seconds for mediated device driver finishing clean up\n";
6147 if (-e
$dev_sysfs_dir) {
6148 print "actively clean up mediated device with UUID $uuid\n";
6149 PVE
::SysFSTools
::file_write
("$dev_sysfs_dir/remove", "1");
6153 PVE
::QemuServer
::PCI
::remove_pci_reservation
($vmid);
6156 sub vm_stop_cleanup
{
6157 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
6162 my $vollist = get_vm_volumes
($conf);
6163 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
6165 if (my $tpmdrive = $conf->{tpmstate0
}) {
6166 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
6167 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
6169 PVE
::Storage
::unmap_volume
($storecfg, $tpm->{file
});
6174 foreach my $ext (qw(mon qmp pid vnc qga)) {
6175 unlink "/var/run/qemu-server/${vmid}.$ext";
6178 if ($conf->{ivshmem
}) {
6179 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
6180 # just delete it for now, VMs which have this already open do not
6181 # are affected, but new VMs will get a separated one. If this
6182 # becomes an issue we either add some sort of ref-counting or just
6183 # add a "don't delete on stop" flag to the ivshmem format.
6184 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name
} // $vmid);
6187 cleanup_pci_devices
($vmid, $conf);
6189 vmconfig_apply_pending
($vmid, $conf, $storecfg) if $apply_pending_changes;
6191 warn $@ if $@; # avoid errors - just warn
6194 # call only in locked context
6196 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
6198 my $pid = check_running
($vmid, $nocheck);
6203 $conf = PVE
::QemuConfig-
>load_config($vmid);
6204 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
6205 if (!defined($timeout) && $shutdown && $conf->{startup
}) {
6206 my $opts = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
6207 $timeout = $opts->{down
} if $opts->{down
};
6209 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-stop');
6214 if (defined($conf) && get_qga_key
($conf, 'enabled')) {
6215 mon_cmd
($vmid, "guest-shutdown", timeout
=> $timeout);
6217 mon_cmd
($vmid, "system_powerdown");
6220 mon_cmd
($vmid, "quit");
6226 $timeout = 60 if !defined($timeout);
6229 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
6234 if ($count >= $timeout) {
6236 warn "VM still running - terminating now with SIGTERM\n";
6239 die "VM quit/powerdown failed - got timeout\n";
6242 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6246 if (!check_running
($vmid, $nocheck)) {
6247 warn "Unexpected: VM shutdown command failed, but VM not running anymore..\n";
6251 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
6254 die "VM quit/powerdown failed\n";
6262 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
6267 if ($count >= $timeout) {
6268 warn "VM still running - terminating now with SIGKILL\n";
6273 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6276 # Note: use $nocheck to skip tests if VM configuration file exists.
6277 # We need that when migration VMs to other nodes (files already moved)
6278 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
6280 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
6282 $force = 1 if !defined($force) && !$shutdown;
6285 my $pid = check_running
($vmid, $nocheck, $migratedfrom);
6286 kill 15, $pid if $pid;
6287 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migratedfrom);
6288 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 0);
6292 PVE
::QemuConfig-
>lock_config($vmid, sub {
6293 _do_vm_stop
($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
6298 my ($vmid, $timeout) = @_;
6300 PVE
::QemuConfig-
>lock_config($vmid, sub {
6303 # only reboot if running, as qmeventd starts it again on a stop event
6304 return if !check_running
($vmid);
6306 create_reboot_request
($vmid);
6308 my $storecfg = PVE
::Storage
::config
();
6309 _do_vm_stop
($storecfg, $vmid, undef, undef, $timeout, 1);
6313 # avoid that the next normal shutdown will be confused for a reboot
6314 clear_reboot_request
($vmid);
6320 # note: if using the statestorage parameter, the caller has to check privileges
6322 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
6329 PVE
::QemuConfig-
>lock_config($vmid, sub {
6331 $conf = PVE
::QemuConfig-
>load_config($vmid);
6333 my $is_backing_up = PVE
::QemuConfig-
>has_lock($conf, 'backup');
6334 PVE
::QemuConfig-
>check_lock($conf)
6335 if !($skiplock || $is_backing_up);
6337 die "cannot suspend to disk during backup\n"
6338 if $is_backing_up && $includestate;
6340 if ($includestate) {
6341 $conf->{lock} = 'suspending';
6342 my $date = strftime
("%Y-%m-%d", localtime(time()));
6343 $storecfg = PVE
::Storage
::config
();
6344 if (!$statestorage) {
6345 $statestorage = find_vmstate_storage
($conf, $storecfg);
6346 # check permissions for the storage
6347 my $rpcenv = PVE
::RPCEnvironment
::get
();
6348 if ($rpcenv->{type
} ne 'cli') {
6349 my $authuser = $rpcenv->get_user();
6350 $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
6355 $vmstate = PVE
::QemuConfig-
>__snapshot_save_vmstate(
6356 $vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
6357 $path = PVE
::Storage
::path
($storecfg, $vmstate);
6358 PVE
::QemuConfig-
>write_config($vmid, $conf);
6360 mon_cmd
($vmid, "stop");
6364 if ($includestate) {
6366 PVE
::Storage
::activate_volumes
($storecfg, [$vmstate]);
6369 set_migration_caps
($vmid, 1);
6370 mon_cmd
($vmid, "savevm-start", statefile
=> $path);
6372 my $state = mon_cmd
($vmid, "query-savevm");
6373 if (!$state->{status
}) {
6374 die "savevm not active\n";
6375 } elsif ($state->{status
} eq 'active') {
6378 } elsif ($state->{status
} eq 'completed') {
6379 print "State saved, quitting\n";
6381 } elsif ($state->{status
} eq 'failed' && $state->{error
}) {
6382 die "query-savevm failed with error '$state->{error}'\n"
6384 die "query-savevm returned status '$state->{status}'\n";
6390 PVE
::QemuConfig-
>lock_config($vmid, sub {
6391 $conf = PVE
::QemuConfig-
>load_config($vmid);
6393 # cleanup, but leave suspending lock, to indicate something went wrong
6395 mon_cmd
($vmid, "savevm-end");
6396 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
6397 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
6398 delete $conf->@{qw(vmstate runningmachine runningcpu)};
6399 PVE
::QemuConfig-
>write_config($vmid, $conf);
6405 die "lock changed unexpectedly\n"
6406 if !PVE
::QemuConfig-
>has_lock($conf, 'suspending');
6408 mon_cmd
($vmid, "quit");
6409 $conf->{lock} = 'suspended';
6410 PVE
::QemuConfig-
>write_config($vmid, $conf);
6415 # $nocheck is set when called as part of a migration - in this context the
6416 # location of the config file (source or target node) is not deterministic,
6417 # since migration cannot wait for pmxcfs to process the rename
6419 my ($vmid, $skiplock, $nocheck) = @_;
6421 PVE
::QemuConfig-
>lock_config($vmid, sub {
6422 my $res = mon_cmd
($vmid, 'query-status');
6423 my $resume_cmd = 'cont';
6427 $conf = eval { PVE
::QemuConfig-
>load_config($vmid) }; # try on target node
6429 my $vmlist = PVE
::Cluster
::get_vmlist
();
6430 if (exists($vmlist->{ids
}->{$vmid})) {
6431 my $node = $vmlist->{ids
}->{$vmid}->{node
};
6432 $conf = eval { PVE
::QemuConfig-
>load_config($vmid, $node) }; # try on source node
6435 PVE
::Cluster
::cfs_update
(); # vmlist was wrong, invalidate cache
6436 $conf = PVE
::QemuConfig-
>load_config($vmid); # last try on target node again
6440 $conf = PVE
::QemuConfig-
>load_config($vmid);
6443 if ($res->{status
}) {
6444 return if $res->{status
} eq 'running'; # job done, go home
6445 $resume_cmd = 'system_wakeup' if $res->{status
} eq 'suspended';
6446 $reset = 1 if $res->{status
} eq 'shutdown';
6450 PVE
::QemuConfig-
>check_lock($conf)
6451 if !($skiplock || PVE
::QemuConfig-
>has_lock($conf, 'backup'));
6455 # required if a VM shuts down during a backup and we get a resume
6456 # request before the backup finishes for example
6457 mon_cmd
($vmid, "system_reset");
6460 add_nets_bridge_fdb
($conf, $vmid) if $resume_cmd eq 'cont';
6462 mon_cmd
($vmid, $resume_cmd);
6467 my ($vmid, $skiplock, $key) = @_;
6469 PVE
::QemuConfig-
>lock_config($vmid, sub {
6471 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6473 # there is no qmp command, so we use the human monitor command
6474 my $res = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "sendkey $key");
6475 die $res if $res ne '';
6479 sub check_bridge_access
{
6480 my ($rpcenv, $authuser, $conf) = @_;
6482 return 1 if $authuser eq 'root@pam';
6484 for my $opt (sort keys $conf->%*) {
6485 next if $opt !~ m/^net\d+$/;
6486 my $net = parse_net
($conf->{$opt});
6487 my ($bridge, $tag, $trunks) = $net->@{'bridge', 'tag', 'trunks'};
6488 PVE
::GuestHelpers
::check_vnet_access
($rpcenv, $authuser, $bridge, $tag, $trunks);
6493 sub check_mapping_access
{
6494 my ($rpcenv, $user, $conf) = @_;
6496 for my $opt (keys $conf->%*) {
6497 if ($opt =~ m/^usb\d+$/) {
6498 my $device = PVE
::JSONSchema
::parse_property_string
('pve-qm-usb', $conf->{$opt});
6499 if (my $host = $device->{host
}) {
6500 die "only root can set '$opt' config for real devices\n"
6501 if $host !~ m/^spice$/i && $user ne 'root@pam';
6502 } elsif ($device->{mapping
}) {
6503 $rpcenv->check_full($user, "/mapping/usb/$device->{mapping}", ['Mapping.Use']);
6505 die "either 'host' or 'mapping' must be set.\n";
6507 } elsif ($opt =~ m/^hostpci\d+$/) {
6508 my $device = PVE
::JSONSchema
::parse_property_string
('pve-qm-hostpci', $conf->{$opt});
6509 if ($device->{host
}) {
6510 die "only root can set '$opt' config for non-mapped devices\n" if $user ne 'root@pam';
6511 } elsif ($device->{mapping
}) {
6512 $rpcenv->check_full($user, "/mapping/pci/$device->{mapping}", ['Mapping.Use']);
6514 die "either 'host' or 'mapping' must be set.\n";
6520 sub check_restore_permissions
{
6521 my ($rpcenv, $user, $conf) = @_;
6523 check_bridge_access
($rpcenv, $user, $conf);
6524 check_mapping_access
($rpcenv, $user, $conf);
6526 # vzdump restore implementaion
6528 sub tar_archive_read_firstfile
{
6529 my $archive = shift;
6531 die "ERROR: file '$archive' does not exist\n" if ! -f
$archive;
6533 # try to detect archive type first
6534 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
6535 die "unable to open file '$archive'\n";
6536 my $firstfile = <$fh>;
6540 die "ERROR: archive contaions no data\n" if !$firstfile;
6546 sub tar_restore_cleanup
{
6547 my ($storecfg, $statfile) = @_;
6549 print STDERR
"starting cleanup\n";
6551 if (my $fd = IO
::File-
>new($statfile, "r")) {
6552 while (defined(my $line = <$fd>)) {
6553 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6556 if ($volid =~ m
|^/|) {
6557 unlink $volid || die 'unlink failed\n';
6559 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6561 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6563 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6565 print STDERR
"unable to parse line in statfile - $line";
6572 sub restore_file_archive
{
6573 my ($archive, $vmid, $user, $opts) = @_;
6575 return restore_vma_archive
($archive, $vmid, $user, $opts)
6578 my $info = PVE
::Storage
::archive_info
($archive);
6579 my $format = $opts->{format
} // $info->{format
};
6580 my $comp = $info->{compression
};
6582 # try to detect archive format
6583 if ($format eq 'tar') {
6584 return restore_tar_archive
($archive, $vmid, $user, $opts);
6586 return restore_vma_archive
($archive, $vmid, $user, $opts, $comp);
6590 # hepler to remove disks that will not be used after restore
6591 my $restore_cleanup_oldconf = sub {
6592 my ($storecfg, $vmid, $oldconf, $virtdev_hash) = @_;
6594 my $kept_disks = {};
6596 PVE
::QemuConfig-
>foreach_volume($oldconf, sub {
6597 my ($ds, $drive) = @_;
6599 return if drive_is_cdrom
($drive, 1);
6601 my $volid = $drive->{file
};
6602 return if !$volid || $volid =~ m
|^/|;
6604 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
6605 return if !$path || !$owner || ($owner != $vmid);
6607 # Note: only delete disk we want to restore
6608 # other volumes will become unused
6609 if ($virtdev_hash->{$ds}) {
6610 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
6615 $kept_disks->{$volid} = 1;
6619 # after the restore we have no snapshots anymore
6620 for my $snapname (keys $oldconf->{snapshots
}->%*) {
6621 my $snap = $oldconf->{snapshots
}->{$snapname};
6622 if ($snap->{vmstate
}) {
6623 eval { PVE
::Storage
::vdisk_free
($storecfg, $snap->{vmstate
}); };
6629 for my $volid (keys $kept_disks->%*) {
6630 eval { PVE
::Storage
::volume_snapshot_delete
($storecfg, $volid, $snapname); };
6636 # Helper to parse vzdump backup device hints
6638 # $rpcenv: Environment, used to ckeck storage permissions
6639 # $user: User ID, to check storage permissions
6640 # $storecfg: Storage configuration
6641 # $fh: the file handle for reading the configuration
6642 # $devinfo: should contain device sizes for all backu-up'ed devices
6643 # $options: backup options (pool, default storage)
6645 # Return: $virtdev_hash, updates $devinfo (add devname, virtdev, format, storeid)
6646 my $parse_backup_hints = sub {
6647 my ($rpcenv, $user, $storecfg, $fh, $devinfo, $options) = @_;
6649 my $check_storage = sub { # assert if an image can be allocate
6650 my ($storeid, $scfg) = @_;
6651 die "Content type 'images' is not available on storage '$storeid'\n"
6652 if !$scfg->{content
}->{images
};
6653 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace'])
6654 if $user ne 'root@pam';
6657 my $virtdev_hash = {};
6658 while (defined(my $line = <$fh>)) {
6659 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
6660 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
6661 die "archive does not contain data for drive '$virtdev'\n"
6662 if !$devinfo->{$devname};
6664 if (defined($options->{storage
})) {
6665 $storeid = $options->{storage
} || 'local';
6666 } elsif (!$storeid) {
6669 $format = 'raw' if !$format;
6670 $devinfo->{$devname}->{devname
} = $devname;
6671 $devinfo->{$devname}->{virtdev
} = $virtdev;
6672 $devinfo->{$devname}->{format
} = $format;
6673 $devinfo->{$devname}->{storeid
} = $storeid;
6675 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6676 $check_storage->($storeid, $scfg); # permission and content type check
6678 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
6679 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
6681 my $drive = parse_drive
($virtdev, $2);
6683 if (drive_is_cloudinit
($drive)) {
6684 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
6685 $storeid = $options->{storage
} if defined ($options->{storage
});
6686 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6687 my $format = qemu_img_format
($scfg, $volname); # has 'raw' fallback
6689 $check_storage->($storeid, $scfg); # permission and content type check
6691 $virtdev_hash->{$virtdev} = {
6693 storeid
=> $storeid,
6694 size
=> PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
,
6701 return $virtdev_hash;
6704 # Helper to allocate and activate all volumes required for a restore
6706 # $storecfg: Storage configuration
6707 # $virtdev_hash: as returned by parse_backup_hints()
6709 # Returns: { $virtdev => $volid }
6710 my $restore_allocate_devices = sub {
6711 my ($storecfg, $virtdev_hash, $vmid) = @_;
6714 foreach my $virtdev (sort keys %$virtdev_hash) {
6715 my $d = $virtdev_hash->{$virtdev};
6716 my $alloc_size = int(($d->{size
} + 1024 - 1)/1024);
6717 my $storeid = $d->{storeid
};
6718 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6720 # test if requested format is supported
6721 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
6722 my $supported = grep { $_ eq $d->{format
} } @$validFormats;
6723 $d->{format
} = $defFormat if !$supported;
6726 if ($d->{is_cloudinit
}) {
6727 $name = "vm-$vmid-cloudinit";
6728 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6729 if ($scfg->{path
}) {
6730 $name .= ".$d->{format}";
6734 my $volid = PVE
::Storage
::vdisk_alloc
(
6735 $storecfg, $storeid, $vmid, $d->{format
}, $name, $alloc_size);
6737 print STDERR
"new volume ID is '$volid'\n";
6738 $d->{volid
} = $volid;
6740 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
6742 $map->{$virtdev} = $volid;
6748 sub restore_update_config_line
{
6749 my ($cookie, $map, $line, $unique) = @_;
6751 return '' if $line =~ m/^\#qmdump\#/;
6752 return '' if $line =~ m/^\#vzdump\#/;
6753 return '' if $line =~ m/^lock:/;
6754 return '' if $line =~ m/^unused\d+:/;
6755 return '' if $line =~ m/^parent:/;
6759 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
6760 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
6761 # try to convert old 1.X settings
6762 my ($id, $ind, $ethcfg) = ($1, $2, $3);
6763 foreach my $devconfig (PVE
::Tools
::split_list
($ethcfg)) {
6764 my ($model, $macaddr) = split(/\=/, $devconfig);
6765 $macaddr = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if !$macaddr || $unique;
6768 bridge
=> "vmbr$ind",
6769 macaddr
=> $macaddr,
6771 my $netstr = print_net
($net);
6773 $res .= "net$cookie->{netcount}: $netstr\n";
6774 $cookie->{netcount
}++;
6776 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
6777 my ($id, $netstr) = ($1, $2);
6778 my $net = parse_net
($netstr);
6779 $net->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if $net->{macaddr
};
6780 $netstr = print_net
($net);
6781 $res .= "$id: $netstr\n";
6782 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk|tpmstate)\d+):\s*(\S+)\s*$/) {
6785 my $di = parse_drive
($virtdev, $value);
6786 if (defined($di->{backup
}) && !$di->{backup
}) {
6788 } elsif ($map->{$virtdev}) {
6789 delete $di->{format
}; # format can change on restore
6790 $di->{file
} = $map->{$virtdev};
6791 $value = print_drive
($di);
6792 $res .= "$virtdev: $value\n";
6796 } elsif (($line =~ m/^vmgenid: (.*)/)) {
6798 if ($vmgenid ne '0') {
6799 # always generate a new vmgenid if there was a valid one setup
6800 $vmgenid = generate_uuid
();
6802 $res .= "vmgenid: $vmgenid\n";
6803 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
6804 my ($uuid, $uuid_str);
6805 UUID
::generate
($uuid);
6806 UUID
::unparse
($uuid, $uuid_str);
6807 my $smbios1 = parse_smbios1
($2);
6808 $smbios1->{uuid
} = $uuid_str;
6809 $res .= $1.print_smbios1
($smbios1)."\n";
6817 my $restore_deactivate_volumes = sub {
6818 my ($storecfg, $virtdev_hash) = @_;
6821 for my $dev (values $virtdev_hash->%*) {
6822 push $vollist->@*, $dev->{volid
} if $dev->{volid
};
6825 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
6826 print STDERR
$@ if $@;
6829 my $restore_destroy_volumes = sub {
6830 my ($storecfg, $virtdev_hash) = @_;
6832 for my $dev (values $virtdev_hash->%*) {
6833 my $volid = $dev->{volid
} or next;
6835 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6836 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6838 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6842 sub restore_merge_config
{
6843 my ($filename, $backup_conf_raw, $override_conf) = @_;
6845 my $backup_conf = parse_vm_config
($filename, $backup_conf_raw);
6846 for my $key (keys $override_conf->%*) {
6847 $backup_conf->{$key} = $override_conf->{$key};
6850 return $backup_conf;
6854 my ($cfg, $vmid) = @_;
6856 my $info = PVE
::Storage
::vdisk_list
($cfg, undef, $vmid, undef, 'images');
6858 my $volid_hash = {};
6859 foreach my $storeid (keys %$info) {
6860 foreach my $item (@{$info->{$storeid}}) {
6861 next if !($item->{volid
} && $item->{size
});
6862 $item->{path
} = PVE
::Storage
::path
($cfg, $item->{volid
});
6863 $volid_hash->{$item->{volid
}} = $item;
6870 sub update_disk_config
{
6871 my ($vmid, $conf, $volid_hash) = @_;
6874 my $prefix = "VM $vmid";
6876 # used and unused disks
6877 my $referenced = {};
6879 # Note: it is allowed to define multiple storages with same path (alias), so
6880 # we need to check both 'volid' and real 'path' (two different volid can point
6881 # to the same path).
6883 my $referencedpath = {};
6886 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6887 my ($opt, $drive) = @_;
6889 my $volid = $drive->{file
};
6891 my $volume = $volid_hash->{$volid};
6893 # mark volid as "in-use" for next step
6894 $referenced->{$volid} = 1;
6895 if ($volume && (my $path = $volume->{path
})) {
6896 $referencedpath->{$path} = 1;
6899 return if drive_is_cdrom
($drive);
6902 my ($updated, $msg) = PVE
::QemuServer
::Drive
::update_disksize
($drive, $volume->{size
});
6903 if (defined($updated)) {
6905 $conf->{$opt} = print_drive
($updated);
6906 print "$prefix ($opt): $msg\n";
6910 # remove 'unusedX' entry if volume is used
6911 PVE
::QemuConfig-
>foreach_unused_volume($conf, sub {
6912 my ($opt, $drive) = @_;
6914 my $volid = $drive->{file
};
6918 $path = $volid_hash->{$volid}->{path
} if $volid_hash->{$volid};
6919 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6920 print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
6922 delete $conf->{$opt};
6925 $referenced->{$volid} = 1;
6926 $referencedpath->{$path} = 1 if $path;
6929 foreach my $volid (sort keys %$volid_hash) {
6930 next if $volid =~ m/vm-$vmid-state-/;
6931 next if $referenced->{$volid};
6932 my $path = $volid_hash->{$volid}->{path
};
6933 next if !$path; # just to be sure
6934 next if $referencedpath->{$path};
6936 my $key = PVE
::QemuConfig-
>add_unused_volume($conf, $volid);
6937 print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
6938 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6945 my ($vmid, $nolock, $dryrun) = @_;
6947 my $cfg = PVE
::Storage
::config
();
6949 print "rescan volumes...\n";
6950 my $volid_hash = scan_volids
($cfg, $vmid);
6952 my $updatefn = sub {
6955 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6957 PVE
::QemuConfig-
>check_lock($conf);
6960 foreach my $volid (keys %$volid_hash) {
6961 my $info = $volid_hash->{$volid};
6962 $vm_volids->{$volid} = $info if $info->{vmid
} && $info->{vmid
} == $vmid;
6965 my $changes = update_disk_config
($vmid, $conf, $vm_volids);
6967 PVE
::QemuConfig-
>write_config($vmid, $conf) if $changes && !$dryrun;
6970 if (defined($vmid)) {
6974 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6977 my $vmlist = config_list
();
6978 foreach my $vmid (keys %$vmlist) {
6982 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6988 sub restore_proxmox_backup_archive
{
6989 my ($archive, $vmid, $user, $options) = @_;
6991 my $storecfg = PVE
::Storage
::config
();
6993 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($archive);
6994 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6996 my $fingerprint = $scfg->{fingerprint
};
6997 my $keyfile = PVE
::Storage
::PBSPlugin
::pbs_encryption_key_file_name
($storecfg, $storeid);
6999 my $repo = PVE
::PBSClient
::get_repository
($scfg);
7000 my $namespace = $scfg->{namespace
};
7002 # This is only used for `pbs-restore` and the QEMU PBS driver (live-restore)
7003 my $password = PVE
::Storage
::PBSPlugin
::pbs_get_password
($scfg, $storeid);
7004 local $ENV{PBS_PASSWORD
} = $password;
7005 local $ENV{PBS_FINGERPRINT
} = $fingerprint if defined($fingerprint);
7007 my ($vtype, $pbs_backup_name, undef, undef, undef, undef, $format) =
7008 PVE
::Storage
::parse_volname
($storecfg, $archive);
7010 die "got unexpected vtype '$vtype'\n" if $vtype ne 'backup';
7012 die "got unexpected backup format '$format'\n" if $format ne 'pbs-vm';
7014 my $tmpdir = "/var/tmp/vzdumptmp$$";
7018 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7019 # disable interrupts (always do cleanups)
7023 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
7025 # Note: $oldconf is undef if VM does not exists
7026 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
7027 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
7028 my $new_conf_raw = '';
7030 my $rpcenv = PVE
::RPCEnvironment
::get
();
7031 my $devinfo = {}; # info about drives included in backup
7032 my $virtdev_hash = {}; # info about allocated drives
7040 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7042 my $cfgfn = "$tmpdir/qemu-server.conf";
7043 my $firewall_config_fn = "$tmpdir/fw.conf";
7044 my $index_fn = "$tmpdir/index.json";
7046 my $cmd = "restore";
7048 my $param = [$pbs_backup_name, "index.json", $index_fn];
7049 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7050 my $index = PVE
::Tools
::file_get_contents
($index_fn);
7051 $index = decode_json
($index);
7053 foreach my $info (@{$index->{files
}}) {
7054 if ($info->{filename
} =~ m/^(drive-\S+).img.fidx$/) {
7056 if ($info->{size
} =~ m/^(\d+)$/) { # untaint size
7057 $devinfo->{$devname}->{size
} = $1;
7059 die "unable to parse file size in 'index.json' - got '$info->{size}'\n";
7064 my $is_qemu_server_backup = scalar(
7065 grep { $_->{filename
} eq 'qemu-server.conf.blob' } @{$index->{files
}}
7067 if (!$is_qemu_server_backup) {
7068 die "backup does not look like a qemu-server backup (missing 'qemu-server.conf' file)\n";
7070 my $has_firewall_config = scalar(grep { $_->{filename
} eq 'fw.conf.blob' } @{$index->{files
}});
7072 $param = [$pbs_backup_name, "qemu-server.conf", $cfgfn];
7073 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7075 if ($has_firewall_config) {
7076 $param = [$pbs_backup_name, "fw.conf", $firewall_config_fn];
7077 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7079 my $pve_firewall_dir = '/etc/pve/firewall';
7080 mkdir $pve_firewall_dir; # make sure the dir exists
7081 PVE
::Tools
::file_copy
($firewall_config_fn, "${pve_firewall_dir}/$vmid.fw");
7084 my $fh = IO
::File-
>new($cfgfn, "r") ||
7085 die "unable to read qemu-server.conf - $!\n";
7087 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $storecfg, $fh, $devinfo, $options);
7089 # fixme: rate limit?
7091 # create empty/temp config
7092 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\nlock: create");
7094 $restore_cleanup_oldconf->($storecfg, $vmid, $oldconf, $virtdev_hash) if $oldconf;
7097 my $map = $restore_allocate_devices->($storecfg, $virtdev_hash, $vmid);
7099 foreach my $virtdev (sort keys %$virtdev_hash) {
7100 my $d = $virtdev_hash->{$virtdev};
7101 next if $d->{is_cloudinit
}; # no need to restore cloudinit
7103 # this fails if storage is unavailable
7104 my $volid = $d->{volid
};
7105 my $path = PVE
::Storage
::path
($storecfg, $volid);
7107 # for live-restore we only want to preload the efidisk and TPM state
7108 next if $options->{live
} && $virtdev ne 'efidisk0' && $virtdev ne 'tpmstate0';
7111 if (defined(my $ns = $scfg->{namespace
})) {
7112 @ns_arg = ('--ns', $ns);
7115 my $pbs_restore_cmd = [
7116 '/usr/bin/pbs-restore',
7117 '--repository', $repo,
7120 "$d->{devname}.img.fidx",
7125 push @$pbs_restore_cmd, '--format', $d->{format
} if $d->{format
};
7126 push @$pbs_restore_cmd, '--keyfile', $keyfile if -e
$keyfile;
7128 if (PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $volid)) {
7129 push @$pbs_restore_cmd, '--skip-zero';
7132 my $dbg_cmdstring = PVE
::Tools
::cmd2string
($pbs_restore_cmd);
7133 print "restore proxmox backup image: $dbg_cmdstring\n";
7134 run_command
($pbs_restore_cmd);
7137 $fh->seek(0, 0) || die "seek failed - $!\n";
7139 my $cookie = { netcount
=> 0 };
7140 while (defined(my $line = <$fh>)) {
7141 $new_conf_raw .= restore_update_config_line
(
7153 if ($err || !$options->{live
}) {
7154 $restore_deactivate_volumes->($storecfg, $virtdev_hash);
7160 $restore_destroy_volumes->($storecfg, $virtdev_hash);
7164 if ($options->{live
}) {
7165 # keep lock during live-restore
7166 $new_conf_raw .= "\nlock: create";
7169 my $new_conf = restore_merge_config
($conffile, $new_conf_raw, $options->{override_conf
});
7170 check_restore_permissions
($rpcenv, $user, $new_conf);
7171 PVE
::QemuConfig-
>write_config($vmid, $new_conf);
7173 eval { rescan
($vmid, 1); };
7176 PVE
::AccessControl
::add_vm_to_pool
($vmid, $options->{pool
}) if $options->{pool
};
7178 if ($options->{live
}) {
7184 local $SIG{PIPE
} = sub { die "got signal ($!) - abort\n"; };
7186 my $conf = PVE
::QemuConfig-
>load_config($vmid);
7187 die "cannot do live-restore for template\n" if PVE
::QemuConfig-
>is_template($conf);
7189 # these special drives are already restored before start
7190 delete $devinfo->{'drive-efidisk0'};
7191 delete $devinfo->{'drive-tpmstate0-backup'};
7195 keyfile
=> $keyfile,
7196 snapshot
=> $pbs_backup_name,
7197 namespace
=> $namespace,
7199 pbs_live_restore
($vmid, $conf, $storecfg, $devinfo, $pbs_opts);
7201 PVE
::QemuConfig-
>remove_lock($vmid, "create");
7205 sub pbs_live_restore
{
7206 my ($vmid, $conf, $storecfg, $restored_disks, $opts) = @_;
7208 print "starting VM for live-restore\n";
7209 print "repository: '$opts->{repo}', snapshot: '$opts->{snapshot}'\n";
7211 my $live_restore_backing = {};
7212 for my $ds (keys %$restored_disks) {
7213 $ds =~ m/^drive-(.*)$/;
7217 repository
=> $opts->{repo
},
7218 snapshot
=> $opts->{snapshot
},
7219 archive
=> "$ds.img.fidx",
7221 $pbs_conf->{keyfile
} = $opts->{keyfile
} if -e
$opts->{keyfile
};
7222 $pbs_conf->{namespace
} = $opts->{namespace
} if defined($opts->{namespace
});
7224 my $drive = parse_drive
($confname, $conf->{$confname});
7225 print "restoring '$ds' to '$drive->{file}'\n";
7227 my $pbs_name = "drive-${confname}-pbs";
7228 $live_restore_backing->{$confname} = {
7230 blockdev
=> print_pbs_blockdev
($pbs_conf, $pbs_name),
7234 my $drives_streamed = 0;
7236 # make sure HA doesn't interrupt our restore by stopping the VM
7237 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid)) {
7238 run_command
(['ha-manager', 'set', "vm:$vmid", '--state', 'started']);
7241 # start VM with backing chain pointing to PBS backup, environment vars for PBS driver
7242 # in QEMU (PBS_PASSWORD and PBS_FINGERPRINT) are already set by our caller
7243 vm_start_nolock
($storecfg, $vmid, $conf, {paused
=> 1, 'live-restore-backing' => $live_restore_backing}, {});
7245 my $qmeventd_fd = register_qmeventd_handle
($vmid);
7247 # begin streaming, i.e. data copy from PBS to target disk for every vol,
7248 # this will effectively collapse the backing image chain consisting of
7249 # [target <- alloc-track -> PBS snapshot] to just [target] (alloc-track
7250 # removes itself once all backing images vanish with 'auto-remove=on')
7252 for my $ds (sort keys %$restored_disks) {
7253 my $job_id = "restore-$ds";
7254 mon_cmd
($vmid, 'block-stream',
7255 'job-id' => $job_id,
7258 $jobs->{$job_id} = {};
7261 mon_cmd
($vmid, 'cont');
7262 qemu_drive_mirror_monitor
($vmid, undef, $jobs, 'auto', 0, 'stream');
7264 print "restore-drive jobs finished successfully, removing all tracking block devices"
7265 ." to disconnect from Proxmox Backup Server\n";
7267 for my $ds (sort keys %$restored_disks) {
7268 mon_cmd
($vmid, 'blockdev-del', 'node-name' => "$ds-pbs");
7271 close($qmeventd_fd);
7277 warn "An error occurred during live-restore: $err\n";
7278 _do_vm_stop
($storecfg, $vmid, 1, 1, 10, 0, 1);
7279 die "live-restore failed\n";
7283 # Inspired by pbs live-restore, this restores with the disks being available as files.
7284 # Theoretically this can also be used to quick-start a full-clone vm if the
7285 # disks are all available as files.
7287 # The mapping should provide a path by config entry, such as
7288 # `{ scsi0 => { format => <qcow2|raw|...>, path => "/path/to/file", sata1 => ... } }`
7290 # This is used when doing a `create` call with the `--live-import` parameter,
7291 # where the disks get an `import-from=` property. The non-live part is
7292 # therefore already handled in the `$create_disks()` call happening in the
7294 sub live_import_from_files
{
7295 my ($mapping, $vmid, $conf, $restore_options) = @_;
7297 my $live_restore_backing = {};
7298 for my $dev (keys %$mapping) {
7299 die "disk not support for live-restoring: '$dev'\n"
7300 if !is_valid_drivename
($dev) || $dev =~ /^(?:efidisk|tpmstate)/;
7302 die "mapping contains disk '$dev' which does not exist in the config\n"
7303 if !exists($conf->{$dev});
7305 my $info = $mapping->{$dev};
7306 my ($format, $path) = $info->@{qw(format path)};
7307 die "missing path for '$dev' mapping\n" if !$path;
7308 die "missing format for '$dev' mapping\n" if !$format;
7309 die "invalid format '$format' for '$dev' mapping\n"
7310 if !grep { $format eq $_ } qw(raw qcow2 vmdk);
7312 $live_restore_backing->{$dev} = {
7313 name
=> "drive-$dev-restore",
7314 blockdev
=> "driver=$format,node-name=drive-$dev-restore"
7316 . ",file.driver=file,file.filename=$path"
7320 my $storecfg = PVE
::Storage
::config
();
7323 # make sure HA doesn't interrupt our restore by stopping the VM
7324 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid)) {
7325 run_command
(['ha-manager', 'set', "vm:$vmid", '--state', 'started']);
7328 vm_start_nolock
($storecfg, $vmid, $conf, {paused
=> 1, 'live-restore-backing' => $live_restore_backing}, {});
7330 # prevent shutdowns from qmeventd when the VM powers off from the inside
7331 my $qmeventd_fd = register_qmeventd_handle
($vmid);
7333 # begin streaming, i.e. data copy from PBS to target disk for every vol,
7334 # this will effectively collapse the backing image chain consisting of
7335 # [target <- alloc-track -> PBS snapshot] to just [target] (alloc-track
7336 # removes itself once all backing images vanish with 'auto-remove=on')
7338 for my $ds (sort keys %$live_restore_backing) {
7339 my $job_id = "restore-$ds";
7340 mon_cmd
($vmid, 'block-stream',
7341 'job-id' => $job_id,
7342 device
=> "drive-$ds",
7344 $jobs->{$job_id} = {};
7347 mon_cmd
($vmid, 'cont');
7348 qemu_drive_mirror_monitor
($vmid, undef, $jobs, 'auto', 0, 'stream');
7350 print "restore-drive jobs finished successfully, removing all tracking block devices\n";
7352 for my $ds (sort keys %$live_restore_backing) {
7353 mon_cmd
($vmid, 'blockdev-del', 'node-name' => "drive-$ds-restore");
7356 close($qmeventd_fd);
7362 warn "An error occurred during live-restore: $err\n";
7363 _do_vm_stop
($storecfg, $vmid, 1, 1, 10, 0, 1);
7364 die "live-restore failed\n";
7367 PVE
::QemuConfig-
>remove_lock($vmid, "import");
7370 sub restore_vma_archive
{
7371 my ($archive, $vmid, $user, $opts, $comp) = @_;
7373 my $readfrom = $archive;
7375 my $cfg = PVE
::Storage
::config
();
7377 my $bwlimit = $opts->{bwlimit
};
7379 my $dbg_cmdstring = '';
7380 my $add_pipe = sub {
7382 push @$commands, $cmd;
7383 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
7384 $dbg_cmdstring .= PVE
::Tools
::cmd2string
($cmd);
7389 if ($archive eq '-') {
7392 # If we use a backup from a PVE defined storage we also consider that
7393 # storage's rate limit:
7394 my (undef, $volid) = PVE
::Storage
::path_to_volume_id
($cfg, $archive);
7395 if (defined($volid)) {
7396 my ($sid, undef) = PVE
::Storage
::parse_volume_id
($volid);
7397 my $readlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [$sid], $bwlimit);
7399 print STDERR
"applying read rate limit: $readlimit\n";
7400 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
7401 $add_pipe->($cstream);
7407 my $info = PVE
::Storage
::decompressor_info
('vma', $comp);
7408 my $cmd = $info->{decompressor
};
7409 push @$cmd, $readfrom;
7413 my $tmpdir = "/var/tmp/vzdumptmp$$";
7416 # disable interrupts (always do cleanups)
7420 local $SIG{HUP
} = sub { warn "got interrupt - ignored\n"; };
7422 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
7423 POSIX
::mkfifo
($mapfifo, 0600);
7425 my $openfifo = sub { open($fifofh, '>', $mapfifo) or die $! };
7427 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
7429 my $devinfo = {}; # info about drives included in backup
7430 my $virtdev_hash = {}; # info about allocated drives
7432 my $rpcenv = PVE
::RPCEnvironment
::get
();
7434 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7436 # Note: $oldconf is undef if VM does not exist
7437 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
7438 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
7439 my $new_conf_raw = '';
7443 my $print_devmap = sub {
7444 my $cfgfn = "$tmpdir/qemu-server.conf";
7446 # we can read the config - that is already extracted
7447 my $fh = IO
::File-
>new($cfgfn, "r") ||
7448 die "unable to read qemu-server.conf - $!\n";
7450 my $fwcfgfn = "$tmpdir/qemu-server.fw";
7452 my $pve_firewall_dir = '/etc/pve/firewall';
7453 mkdir $pve_firewall_dir; # make sure the dir exists
7454 PVE
::Tools
::file_copy
($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
7457 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $cfg, $fh, $devinfo, $opts);
7459 foreach my $info (values %{$virtdev_hash}) {
7460 my $storeid = $info->{storeid
};
7461 next if defined($storage_limits{$storeid});
7463 my $limit = PVE
::Storage
::get_bandwidth_limit
('restore', [$storeid], $bwlimit) // 0;
7464 print STDERR
"rate limit for storage $storeid: $limit KiB/s\n" if $limit;
7465 $storage_limits{$storeid} = $limit * 1024;
7468 foreach my $devname (keys %$devinfo) {
7469 die "found no device mapping information for device '$devname'\n"
7470 if !$devinfo->{$devname}->{virtdev
};
7473 # create empty/temp config
7475 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\n");
7476 $restore_cleanup_oldconf->($cfg, $vmid, $oldconf, $virtdev_hash);
7480 my $map = $restore_allocate_devices->($cfg, $virtdev_hash, $vmid);
7482 # print restore information to $fifofh
7483 foreach my $virtdev (sort keys %$virtdev_hash) {
7484 my $d = $virtdev_hash->{$virtdev};
7485 next if $d->{is_cloudinit
}; # no need to restore cloudinit
7487 my $storeid = $d->{storeid
};
7488 my $volid = $d->{volid
};
7491 if (my $limit = $storage_limits{$storeid}) {
7492 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
7495 my $write_zeros = 1;
7496 if (PVE
::Storage
::volume_has_feature
($cfg, 'sparseinit', $volid)) {
7500 my $path = PVE
::Storage
::path
($cfg, $volid);
7502 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
7504 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
7507 $fh->seek(0, 0) || die "seek failed - $!\n";
7509 my $cookie = { netcount
=> 0 };
7510 while (defined(my $line = <$fh>)) {
7511 $new_conf_raw .= restore_update_config_line
(
7530 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7531 local $SIG{ALRM
} = sub { die "got timeout\n"; };
7533 $oldtimeout = alarm(5); # for reading the VMA header - might hang with a corrupted one
7540 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
7541 my ($dev_id, $size, $devname) = ($1, $2, $3);
7542 $devinfo->{$devname} = { size
=> $size, dev_id
=> $dev_id };
7543 } elsif ($line =~ m/^CTIME: /) {
7544 # we correctly received the vma config, so we can disable
7545 # the timeout now for disk allocation
7546 alarm($oldtimeout || 0);
7547 $oldtimeout = undef;
7549 print $fifofh "done\n";
7555 print "restore vma archive: $dbg_cmdstring\n";
7556 run_command
($commands, input
=> $input, outfunc
=> $parser, afterfork
=> $openfifo);
7560 alarm($oldtimeout) if $oldtimeout;
7562 $restore_deactivate_volumes->($cfg, $virtdev_hash);
7564 close($fifofh) if $fifofh;
7569 $restore_destroy_volumes->($cfg, $virtdev_hash);
7573 my $new_conf = restore_merge_config
($conffile, $new_conf_raw, $opts->{override_conf
});
7574 check_restore_permissions
($rpcenv, $user, $new_conf);
7575 PVE
::QemuConfig-
>write_config($vmid, $new_conf);
7577 eval { rescan
($vmid, 1); };
7580 PVE
::AccessControl
::add_vm_to_pool
($vmid, $opts->{pool
}) if $opts->{pool
};
7583 sub restore_tar_archive
{
7584 my ($archive, $vmid, $user, $opts) = @_;
7586 if (scalar(keys $opts->{override_conf
}->%*) > 0) {
7587 my $keystring = join(' ', keys $opts->{override_conf
}->%*);
7588 die "cannot pass along options ($keystring) when restoring from tar archive\n";
7591 if ($archive ne '-') {
7592 my $firstfile = tar_archive_read_firstfile
($archive);
7593 die "ERROR: file '$archive' does not look like a QemuServer vzdump backup\n"
7594 if $firstfile ne 'qemu-server.conf';
7597 my $storecfg = PVE
::Storage
::config
();
7599 # avoid zombie disks when restoring over an existing VM -> cleanup first
7600 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
7601 # skiplock=1 because qmrestore has set the 'create' lock itself already
7602 my $vmcfgfn = PVE
::QemuConfig-
>config_file($vmid);
7603 destroy_vm
($storecfg, $vmid, 1, { lock => 'restore' }) if -f
$vmcfgfn;
7605 my $tocmd = "/usr/lib/qemu-server/qmextract";
7607 $tocmd .= " --storage " . PVE
::Tools
::shellquote
($opts->{storage
}) if $opts->{storage
};
7608 $tocmd .= " --pool " . PVE
::Tools
::shellquote
($opts->{pool
}) if $opts->{pool
};
7609 $tocmd .= ' --prealloc' if $opts->{prealloc
};
7610 $tocmd .= ' --info' if $opts->{info
};
7612 # tar option "xf" does not autodetect compression when read from STDIN,
7613 # so we pipe to zcat
7614 my $cmd = "zcat -f|tar xf " . PVE
::Tools
::shellquote
($archive) . " " .
7615 PVE
::Tools
::shellquote
("--to-command=$tocmd");
7617 my $tmpdir = "/var/tmp/vzdumptmp$$";
7620 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
7621 local $ENV{VZDUMP_VMID
} = $vmid;
7622 local $ENV{VZDUMP_USER
} = $user;
7624 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7625 my $new_conf_raw = '';
7627 # disable interrupts (always do cleanups)
7631 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
7639 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7641 if ($archive eq '-') {
7642 print "extracting archive from STDIN\n";
7643 run_command
($cmd, input
=> "<&STDIN");
7645 print "extracting archive '$archive'\n";
7649 return if $opts->{info
};
7653 my $statfile = "$tmpdir/qmrestore.stat";
7654 if (my $fd = IO
::File-
>new($statfile, "r")) {
7655 while (defined (my $line = <$fd>)) {
7656 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
7657 $map->{$1} = $2 if $1;
7659 print STDERR
"unable to parse line in statfile - $line\n";
7665 my $confsrc = "$tmpdir/qemu-server.conf";
7667 my $srcfd = IO
::File-
>new($confsrc, "r") || die "unable to open file '$confsrc'\n";
7669 my $cookie = { netcount
=> 0 };
7670 while (defined (my $line = <$srcfd>)) {
7671 $new_conf_raw .= restore_update_config_line
(
7682 tar_restore_cleanup
($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info
};
7688 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
7690 PVE
::Cluster
::cfs_update
(); # make sure we read new file
7692 eval { rescan
($vmid, 1); };
7696 sub foreach_storage_used_by_vm
{
7697 my ($conf, $func) = @_;
7701 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7702 my ($ds, $drive) = @_;
7703 return if drive_is_cdrom
($drive);
7705 my $volid = $drive->{file
};
7707 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
7708 $sidhash->{$sid} = $sid if $sid;
7711 foreach my $sid (sort keys %$sidhash) {
7716 my $qemu_snap_storage = {
7719 sub do_snapshots_with_qemu
{
7720 my ($storecfg, $volid, $deviceid) = @_;
7722 return if $deviceid =~ m/tpmstate0/;
7724 my $storage_name = PVE
::Storage
::parse_volume_id
($volid);
7725 my $scfg = $storecfg->{ids
}->{$storage_name};
7726 die "could not find storage '$storage_name'\n" if !defined($scfg);
7728 if ($qemu_snap_storage->{$scfg->{type
}} && !$scfg->{krbd
}){
7732 if ($volid =~ m/\.(qcow2|qed)$/){
7739 sub qga_check_running
{
7740 my ($vmid, $nowarn) = @_;
7742 eval { mon_cmd
($vmid, "guest-ping", timeout
=> 3); };
7744 warn "QEMU Guest Agent is not running - $@" if !$nowarn;
7750 sub template_create
{
7751 my ($vmid, $conf, $disk) = @_;
7753 my $storecfg = PVE
::Storage
::config
();
7755 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7756 my ($ds, $drive) = @_;
7758 return if drive_is_cdrom
($drive);
7759 return if $disk && $ds ne $disk;
7761 my $volid = $drive->{file
};
7762 return if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
7764 my $voliddst = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
7765 $drive->{file
} = $voliddst;
7766 $conf->{$ds} = print_drive
($drive);
7767 PVE
::QemuConfig-
>write_config($vmid, $conf);
7771 sub convert_iscsi_path
{
7774 if ($path =~ m
|^iscsi
://([^/]+)/([^/]+)/(.+)$|) {
7779 my $initiator_name = get_initiator_name
();
7781 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
7782 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
7785 die "cannot convert iscsi path '$path', unkown format\n";
7788 sub qemu_img_convert
{
7789 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized, $bwlimit) = @_;
7791 my $storecfg = PVE
::Storage
::config
();
7792 my ($src_storeid, $src_volname) = PVE
::Storage
::parse_volume_id
($src_volid, 1);
7793 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid, 1);
7795 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
7799 my $src_is_iscsi = 0;
7803 PVE
::Storage
::activate_volumes
($storecfg, [$src_volid], $snapname);
7804 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
7805 $src_format = qemu_img_format
($src_scfg, $src_volname);
7806 $src_path = PVE
::Storage
::path
($storecfg, $src_volid, $snapname);
7807 $src_is_iscsi = ($src_path =~ m
|^iscsi
://|);
7808 $cachemode = 'none' if $src_scfg->{type
} eq 'zfspool';
7809 } elsif (-f
$src_volid || -b
$src_volid) {
7810 $src_path = $src_volid;
7811 if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7816 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
7818 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7819 my $dst_format = qemu_img_format
($dst_scfg, $dst_volname);
7820 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7821 my $dst_is_iscsi = ($dst_path =~ m
|^iscsi
://|);
7824 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
7825 push @$cmd, '-l', "snapshot.name=$snapname"
7826 if $snapname && $src_format && $src_format eq "qcow2";
7827 push @$cmd, '-t', 'none' if $dst_scfg->{type
} eq 'zfspool';
7828 push @$cmd, '-T', $cachemode if defined($cachemode);
7829 push @$cmd, '-r', "${bwlimit}K" if defined($bwlimit);
7831 if ($src_is_iscsi) {
7832 push @$cmd, '--image-opts';
7833 $src_path = convert_iscsi_path
($src_path);
7834 } elsif ($src_format) {
7835 push @$cmd, '-f', $src_format;
7838 if ($dst_is_iscsi) {
7839 push @$cmd, '--target-image-opts';
7840 $dst_path = convert_iscsi_path
($dst_path);
7842 push @$cmd, '-O', $dst_format;
7845 push @$cmd, $src_path;
7847 if (!$dst_is_iscsi && $is_zero_initialized) {
7848 push @$cmd, "zeroinit:$dst_path";
7850 push @$cmd, $dst_path;
7855 if($line =~ m/\((\S+)\/100\
%\)/){
7857 my $transferred = int($size * $percent / 100);
7858 my $total_h = render_bytes
($size, 1);
7859 my $transferred_h = render_bytes
($transferred, 1);
7861 print "transferred $transferred_h of $total_h ($percent%)\n";
7866 eval { run_command
($cmd, timeout
=> undef, outfunc
=> $parser); };
7868 die "copy failed: $err" if $err;
7871 sub qemu_img_format
{
7872 my ($scfg, $volname) = @_;
7874 # FIXME: this entire function is kind of weird given that `parse_volname`
7875 # also already gives us a format?
7876 my $is_path_storage = $scfg->{path
} || $scfg->{type
} eq 'esxi';
7878 if ($is_path_storage && $volname =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7885 sub qemu_drive_mirror
{
7886 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
7888 $jobs = {} if !$jobs;
7892 $jobs->{"drive-$drive"} = {};
7894 if ($dst_volid =~ /^nbd:/) {
7895 $qemu_target = $dst_volid;
7898 my $storecfg = PVE
::Storage
::config
();
7899 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid);
7901 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7903 $format = qemu_img_format
($dst_scfg, $dst_volname);
7905 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7907 $qemu_target = $is_zero_initialized ?
"zeroinit:$dst_path" : $dst_path;
7910 my $opts = { timeout
=> 10, device
=> "drive-$drive", mode
=> "existing", sync
=> "full", target
=> $qemu_target };
7911 $opts->{format
} = $format if $format;
7913 if (defined($src_bitmap)) {
7914 $opts->{sync
} = 'incremental';
7915 $opts->{bitmap
} = $src_bitmap;
7916 print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
7919 if (defined($bwlimit)) {
7920 $opts->{speed
} = $bwlimit * 1024;
7921 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
7923 print "drive mirror is starting for drive-$drive\n";
7926 # if a job already runs for this device we get an error, catch it for cleanup
7927 eval { mon_cmd
($vmid, "drive-mirror", %$opts); };
7929 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7931 die "mirroring error: $err\n";
7934 qemu_drive_mirror_monitor
($vmid, $vmiddst, $jobs, $completion, $qga);
7937 # $completion can be either
7938 # 'complete': wait until all jobs are ready, block-job-complete them (default)
7939 # 'cancel': wait until all jobs are ready, block-job-cancel them
7940 # 'skip': wait until all jobs are ready, return with block jobs in ready state
7941 # 'auto': wait until all jobs disappear, only use for jobs which complete automatically
7942 sub qemu_drive_mirror_monitor
{
7943 my ($vmid, $vmiddst, $jobs, $completion, $qga, $op) = @_;
7945 $completion //= 'complete';
7949 my $err_complete = 0;
7951 my $starttime = time ();
7953 die "block job ('$op') timed out\n" if $err_complete > 300;
7955 my $stats = mon_cmd
($vmid, "query-block-jobs");
7958 my $running_jobs = {};
7959 for my $stat (@$stats) {
7960 next if $stat->{type
} ne $op;
7961 $running_jobs->{$stat->{device
}} = $stat;
7964 my $readycounter = 0;
7966 for my $job_id (sort keys %$jobs) {
7967 my $job = $running_jobs->{$job_id};
7969 my $vanished = !defined($job);
7970 my $complete = defined($jobs->{$job_id}->{complete
}) && $vanished;
7971 if($complete || ($vanished && $completion eq 'auto')) {
7972 print "$job_id: $op-job finished\n";
7973 delete $jobs->{$job_id};
7977 die "$job_id: '$op' has been cancelled\n" if !defined($job);
7979 my $busy = $job->{busy
};
7980 my $ready = $job->{ready
};
7981 if (my $total = $job->{len
}) {
7982 my $transferred = $job->{offset
} || 0;
7983 my $remaining = $total - $transferred;
7984 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
7986 my $duration = $ctime - $starttime;
7987 my $total_h = render_bytes
($total, 1);
7988 my $transferred_h = render_bytes
($transferred, 1);
7990 my $status = sprintf(
7991 "transferred $transferred_h of $total_h ($percent%%) in %s",
7992 render_duration
($duration),
7997 $status .= ", still busy"; # shouldn't even happen? but mirror is weird
7999 $status .= ", ready";
8002 print "$job_id: $status\n" if !$jobs->{$job_id}->{ready
};
8003 $jobs->{$job_id}->{ready
} = $ready;
8006 $readycounter++ if $job->{ready
};
8009 last if scalar(keys %$jobs) == 0;
8011 if ($readycounter == scalar(keys %$jobs)) {
8012 print "all '$op' jobs are ready\n";
8014 # do the complete later (or has already been done)
8015 last if $completion eq 'skip' || $completion eq 'auto';
8017 if ($vmiddst && $vmiddst != $vmid) {
8018 my $agent_running = $qga && qga_check_running
($vmid);
8019 if ($agent_running) {
8020 print "freeze filesystem\n";
8021 eval { mon_cmd
($vmid, "guest-fsfreeze-freeze"); };
8024 print "suspend vm\n";
8025 eval { PVE
::QemuServer
::vm_suspend
($vmid, 1); };
8029 # if we clone a disk for a new target vm, we don't switch the disk
8030 PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs);
8032 if ($agent_running) {
8033 print "unfreeze filesystem\n";
8034 eval { mon_cmd
($vmid, "guest-fsfreeze-thaw"); };
8037 print "resume vm\n";
8038 eval { PVE
::QemuServer
::vm_resume
($vmid, 1, 1); };
8045 for my $job_id (sort keys %$jobs) {
8046 # try to switch the disk if source and destination are on the same guest
8047 print "$job_id: Completing block job_id...\n";
8050 if ($completion eq 'complete') {
8051 $op = 'block-job-complete';
8052 } elsif ($completion eq 'cancel') {
8053 $op = 'block-job-cancel';
8055 die "invalid completion value: $completion\n";
8057 eval { mon_cmd
($vmid, $op, device
=> $job_id) };
8058 if ($@ =~ m/cannot be completed/) {
8059 print "$job_id: block job cannot be completed, trying again.\n";
8062 print "$job_id: Completed successfully.\n";
8063 $jobs->{$job_id}->{complete
} = 1;
8074 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
8075 die "block job ($op) error: $err";
8079 sub qemu_blockjobs_cancel
{
8080 my ($vmid, $jobs) = @_;
8082 foreach my $job (keys %$jobs) {
8083 print "$job: Cancelling block job\n";
8084 eval { mon_cmd
($vmid, "block-job-cancel", device
=> $job); };
8085 $jobs->{$job}->{cancel
} = 1;
8089 my $stats = mon_cmd
($vmid, "query-block-jobs");
8091 my $running_jobs = {};
8092 foreach my $stat (@$stats) {
8093 $running_jobs->{$stat->{device
}} = $stat;
8096 foreach my $job (keys %$jobs) {
8098 if (defined($jobs->{$job}->{cancel
}) && !defined($running_jobs->{$job})) {
8099 print "$job: Done.\n";
8100 delete $jobs->{$job};
8104 last if scalar(keys %$jobs) == 0;
8110 # Check for bug #4525: drive-mirror will open the target drive with the same aio setting as the
8111 # source, but some storages have problems with io_uring, sometimes even leading to crashes.
8112 my sub clone_disk_check_io_uring
{
8113 my ($src_drive, $storecfg, $src_storeid, $dst_storeid, $use_drive_mirror) = @_;
8115 return if !$use_drive_mirror;
8117 # Don't complain when not changing storage.
8118 # Assume if it works for the source, it'll work for the target too.
8119 return if $src_storeid eq $dst_storeid;
8121 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
8122 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
8124 my $cache_direct = drive_uses_cache_direct
($src_drive);
8126 my $src_uses_io_uring;
8127 if ($src_drive->{aio
}) {
8128 $src_uses_io_uring = $src_drive->{aio
} eq 'io_uring';
8130 $src_uses_io_uring = storage_allows_io_uring_default
($src_scfg, $cache_direct);
8133 die "target storage is known to cause issues with aio=io_uring (used by current drive)\n"
8134 if $src_uses_io_uring && !storage_allows_io_uring_default
($dst_scfg, $cache_direct);
8138 my ($storecfg, $source, $dest, $full, $newvollist, $jobs, $completion, $qga, $bwlimit) = @_;
8140 my ($vmid, $running) = $source->@{qw(vmid running)};
8141 my ($src_drivename, $drive, $snapname) = $source->@{qw(drivename drive snapname)};
8143 my ($newvmid, $dst_drivename, $efisize) = $dest->@{qw(vmid drivename efisize)};
8144 my ($storage, $format) = $dest->@{qw(storage format)};
8146 my $use_drive_mirror = $full && $running && $src_drivename && !$snapname;
8148 if ($src_drivename && $dst_drivename && $src_drivename ne $dst_drivename) {
8149 die "cloning from/to EFI disk requires EFI disk\n"
8150 if $src_drivename eq 'efidisk0' || $dst_drivename eq 'efidisk0';
8151 die "cloning from/to TPM state requires TPM state\n"
8152 if $src_drivename eq 'tpmstate0' || $dst_drivename eq 'tpmstate0';
8154 # This would lead to two device nodes in QEMU pointing to the same backing image!
8155 die "cannot change drive name when cloning disk from/to the same VM\n"
8156 if $use_drive_mirror && $vmid == $newvmid;
8159 die "cannot move TPM state while VM is running\n"
8160 if $use_drive_mirror && $src_drivename eq 'tpmstate0';
8164 print "create " . ($full ?
'full' : 'linked') . " clone of drive ";
8165 print "$src_drivename " if $src_drivename;
8166 print "($drive->{file})\n";
8169 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $drive->{file
}, $newvmid, $snapname);
8170 push @$newvollist, $newvolid;
8172 my ($src_storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
8173 my $storeid = $storage || $src_storeid;
8175 my $dst_format = resolve_dst_disk_format
($storecfg, $storeid, $volname, $format);
8179 if (drive_is_cloudinit
($drive)) {
8180 $name = "vm-$newvmid-cloudinit";
8181 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8182 if ($scfg->{path
}) {
8183 $name .= ".$dst_format";
8186 $size = PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
;
8187 } elsif ($dst_drivename eq 'efidisk0') {
8188 $size = $efisize or die "internal error - need to specify EFI disk size\n";
8189 } elsif ($dst_drivename eq 'tpmstate0') {
8190 $dst_format = 'raw';
8191 $size = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
8193 clone_disk_check_io_uring
($drive, $storecfg, $src_storeid, $storeid, $use_drive_mirror);
8195 $size = PVE
::Storage
::volume_size_info
($storecfg, $drive->{file
}, 10);
8197 $newvolid = PVE
::Storage
::vdisk_alloc
(
8198 $storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024)
8200 push @$newvollist, $newvolid;
8202 PVE
::Storage
::activate_volumes
($storecfg, [$newvolid]);
8204 if (drive_is_cloudinit
($drive)) {
8205 # when cloning multiple disks (e.g. during clone_vm) it might be the last disk
8206 # if this is the case, we have to complete any block-jobs still there from
8207 # previous drive-mirrors
8208 if (($completion eq 'complete') && (scalar(keys %$jobs) > 0)) {
8209 qemu_drive_mirror_monitor
($vmid, $newvmid, $jobs, $completion, $qga);
8214 my $sparseinit = PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $newvolid);
8215 if ($use_drive_mirror) {
8216 qemu_drive_mirror
($vmid, $src_drivename, $newvolid, $newvmid, $sparseinit, $jobs,
8217 $completion, $qga, $bwlimit);
8219 if ($dst_drivename eq 'efidisk0') {
8220 # the relevant data on the efidisk may be smaller than the source
8221 # e.g. on RBD/ZFS, so we use dd to copy only the amount
8222 # that is given by the OVMF_VARS.fd
8223 my $src_path = PVE
::Storage
::path
($storecfg, $drive->{file
}, $snapname);
8224 my $dst_path = PVE
::Storage
::path
($storecfg, $newvolid);
8226 my $src_format = (PVE
::Storage
::parse_volname
($storecfg, $drive->{file
}))[6];
8228 # better for Ceph if block size is not too small, see bug #3324
8231 my $cmd = ['qemu-img', 'dd', '-n', '-O', $dst_format];
8233 if ($src_format eq 'qcow2' && $snapname) {
8234 die "cannot clone qcow2 EFI disk snapshot - requires QEMU >= 6.2\n"
8235 if !min_version
(kvm_user_version
(), 6, 2);
8236 push $cmd->@*, '-l', $snapname;
8238 push $cmd->@*, "bs=$bs", "osize=$size", "if=$src_path", "of=$dst_path";
8241 qemu_img_convert
($drive->{file
}, $newvolid, $size, $snapname, $sparseinit, $bwlimit);
8247 my $size = eval { PVE
::Storage
::volume_size_info
($storecfg, $newvolid, 10) };
8249 my $disk = dclone
($drive);
8250 delete $disk->{format
};
8251 $disk->{file
} = $newvolid;
8252 $disk->{size
} = $size if defined($size);
8257 sub get_running_qemu_version
{
8259 my $res = mon_cmd
($vmid, "query-version");
8260 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
8263 sub qemu_use_old_bios_files
{
8264 my ($machine_type) = @_;
8266 return if !$machine_type;
8268 my $use_old_bios_files = undef;
8270 if ($machine_type =~ m/^(\S+)\.pxe$/) {
8272 $use_old_bios_files = 1;
8274 my $version = extract_version
($machine_type, kvm_user_version
());
8275 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
8276 # load new efi bios files on migration. So this hack is required to allow
8277 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
8278 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
8279 $use_old_bios_files = !min_version
($version, 2, 4);
8282 return ($use_old_bios_files, $machine_type);
8285 sub get_efivars_size
{
8286 my ($conf, $efidisk) = @_;
8288 my $arch = get_vm_arch
($conf);
8289 $efidisk //= $conf->{efidisk0
} ? parse_drive
('efidisk0', $conf->{efidisk0
}) : undef;
8290 my $smm = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
8291 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
8292 return -s
$ovmf_vars;
8295 sub update_efidisk_size
{
8298 return if !defined($conf->{efidisk0
});
8300 my $disk = PVE
::QemuServer
::parse_drive
('efidisk0', $conf->{efidisk0
});
8301 $disk->{size
} = get_efivars_size
($conf);
8302 $conf->{efidisk0
} = print_drive
($disk);
8307 sub update_tpmstate_size
{
8310 my $disk = PVE
::QemuServer
::parse_drive
('tpmstate0', $conf->{tpmstate0
});
8311 $disk->{size
} = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
8312 $conf->{tpmstate0
} = print_drive
($disk);
8315 sub create_efidisk
($$$$$$$) {
8316 my ($storecfg, $storeid, $vmid, $fmt, $arch, $efidisk, $smm) = @_;
8318 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
8320 my $vars_size_b = -s
$ovmf_vars;
8321 my $vars_size = PVE
::Tools
::convert_size
($vars_size_b, 'b' => 'kb');
8322 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
8323 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
8325 qemu_img_convert
($ovmf_vars, $volid, $vars_size_b, undef, 0);
8326 my $size = PVE
::Storage
::volume_size_info
($storecfg, $volid, 3);
8328 return ($volid, $size/1024);
8331 sub vm_iothreads_list
{
8334 my $res = mon_cmd
($vmid, 'query-iothreads');
8337 foreach my $iothread (@$res) {
8338 $iothreads->{ $iothread->{id
} } = $iothread->{"thread-id"};
8345 my ($conf, $drive) = @_;
8349 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)) {
8351 } elsif ($conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single')) {
8357 my $controller = int($drive->{index} / $maxdev);
8358 my $controller_prefix = ($conf->{scsihw
} && $conf->{scsihw
} eq 'virtio-scsi-single')
8362 return ($maxdev, $controller, $controller_prefix);
8365 sub resolve_dst_disk_format
{
8366 my ($storecfg, $storeid, $src_volname, $format) = @_;
8367 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
8370 # if no target format is specified, use the source disk format as hint
8372 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8373 $format = qemu_img_format
($scfg, $src_volname);
8379 # test if requested format is supported - else use default
8380 my $supported = grep { $_ eq $format } @$validFormats;
8381 $format = $defFormat if !$supported;
8385 # NOTE: if this logic changes, please update docs & possibly gui logic
8386 sub find_vmstate_storage
{
8387 my ($conf, $storecfg) = @_;
8389 # first, return storage from conf if set
8390 return $conf->{vmstatestorage
} if $conf->{vmstatestorage
};
8392 my ($target, $shared, $local);
8394 foreach_storage_used_by_vm
($conf, sub {
8396 my $scfg = PVE
::Storage
::storage_config
($storecfg, $sid);
8397 my $dst = $scfg->{shared
} ? \
$shared : \
$local;
8398 $$dst = $sid if !$$dst || $scfg->{path
}; # prefer file based storage
8401 # second, use shared storage where VM has at least one disk
8402 # third, use local storage where VM has at least one disk
8403 # fall back to local storage
8404 $target = $shared // $local // 'local';
8410 my ($uuid, $uuid_str);
8411 UUID
::generate
($uuid);
8412 UUID
::unparse
($uuid, $uuid_str);
8416 sub generate_smbios1_uuid
{
8417 return "uuid=".generate_uuid
();
8423 mon_cmd
($vmid, 'nbd-server-stop', timeout
=> 25);
8426 sub create_reboot_request
{
8428 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
8429 or die "failed to create reboot trigger file: $!\n";
8433 sub clear_reboot_request
{
8435 my $path = "/run/qemu-server/$vmid.reboot";
8438 $res = unlink($path);
8439 die "could not remove reboot request for $vmid: $!"
8440 if !$res && $! != POSIX
::ENOENT
;
8445 sub bootorder_from_legacy
{
8446 my ($conf, $bootcfg) = @_;
8448 my $boot = $bootcfg->{legacy
} || $boot_fmt->{legacy
}->{default};
8449 my $bootindex_hash = {};
8451 foreach my $o (split(//, $boot)) {
8452 $bootindex_hash->{$o} = $i*100;
8458 PVE
::QemuConfig-
>foreach_volume($conf, sub {
8459 my ($ds, $drive) = @_;
8461 if (drive_is_cdrom
($drive, 1)) {
8462 if ($bootindex_hash->{d
}) {
8463 $bootorder->{$ds} = $bootindex_hash->{d
};
8464 $bootindex_hash->{d
} += 1;
8466 } elsif ($bootindex_hash->{c
}) {
8467 $bootorder->{$ds} = $bootindex_hash->{c
}
8468 if $conf->{bootdisk
} && $conf->{bootdisk
} eq $ds;
8469 $bootindex_hash->{c
} += 1;
8473 if ($bootindex_hash->{n
}) {
8474 for (my $i = 0; $i < $MAX_NETS; $i++) {
8475 my $netname = "net$i";
8476 next if !$conf->{$netname};
8477 $bootorder->{$netname} = $bootindex_hash->{n
};
8478 $bootindex_hash->{n
} += 1;
8485 # Generate default device list for 'boot: order=' property. Matches legacy
8486 # default boot order, but with explicit device names. This is important, since
8487 # the fallback for when neither 'order' nor the old format is specified relies
8488 # on 'bootorder_from_legacy' above, and it would be confusing if this diverges.
8489 sub get_default_bootdevices
{
8495 my $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 0);
8496 push @ret, $first if $first;
8499 $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 1);
8500 push @ret, $first if $first;
8503 for (my $i = 0; $i < $MAX_NETS; $i++) {
8504 my $netname = "net$i";
8505 next if !$conf->{$netname};
8506 push @ret, $netname;
8513 sub device_bootorder
{
8516 return bootorder_from_legacy
($conf) if !defined($conf->{boot
});
8518 my $boot = parse_property_string
($boot_fmt, $conf->{boot
});
8521 if (!defined($boot) || $boot->{legacy
}) {
8522 $bootorder = bootorder_from_legacy
($conf, $boot);
8523 } elsif ($boot->{order
}) {
8524 my $i = 100; # start at 100 to allow user to insert devices before us with -args
8525 for my $dev (PVE
::Tools
::split_list
($boot->{order
})) {
8526 $bootorder->{$dev} = $i++;
8533 sub register_qmeventd_handle
{
8537 my $peer = "/var/run/qmeventd.sock";
8542 $fh = IO
::Socket
::UNIX-
>new(Peer
=> $peer, Blocking
=> 0, Timeout
=> 1);
8544 if ($! != EINTR
&& $! != EAGAIN
) {
8545 die "unable to connect to qmeventd socket (vmid: $vmid) - $!\n";
8548 die "unable to connect to qmeventd socket (vmid: $vmid) - timeout "
8549 . "after $count retries\n";
8554 # send handshake to mark VM as backing up
8555 print $fh to_json
({vzdump
=> {vmid
=> "$vmid"}});
8557 # return handle to be closed later when inhibit is no longer required
8561 # bash completion helper
8563 sub complete_backup_archives
{
8564 my ($cmdname, $pname, $cvalue) = @_;
8566 my $cfg = PVE
::Storage
::config
();
8570 if ($cvalue =~ m/^([^:]+):/) {
8574 my $data = PVE
::Storage
::template_list
($cfg, $storeid, 'backup');
8577 foreach my $id (keys %$data) {
8578 foreach my $item (@{$data->{$id}}) {
8579 next if $item->{format
} !~ m/^vma\.(${\PVE::Storage::Plugin::COMPRESSOR_RE})$/;
8580 push @$res, $item->{volid
} if defined($item->{volid
});
8587 my $complete_vmid_full = sub {
8590 my $idlist = vmstatus
();
8594 foreach my $id (keys %$idlist) {
8595 my $d = $idlist->{$id};
8596 if (defined($running)) {
8597 next if $d->{template
};
8598 next if $running && $d->{status
} ne 'running';
8599 next if !$running && $d->{status
} eq 'running';
8608 return &$complete_vmid_full();
8611 sub complete_vmid_stopped
{
8612 return &$complete_vmid_full(0);
8615 sub complete_vmid_running
{
8616 return &$complete_vmid_full(1);
8619 sub complete_storage
{
8621 my $cfg = PVE
::Storage
::config
();
8622 my $ids = $cfg->{ids
};
8625 foreach my $sid (keys %$ids) {
8626 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, undef, 1);
8627 next if !$ids->{$sid}->{content
}->{images
};
8634 sub complete_migration_storage
{
8635 my ($cmd, $param, $current_value, $all_args) = @_;
8637 my $targetnode = @$all_args[1];
8639 my $cfg = PVE
::Storage
::config
();
8640 my $ids = $cfg->{ids
};
8643 foreach my $sid (keys %$ids) {
8644 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, $targetnode, 1);
8645 next if !$ids->{$sid}->{content
}->{images
};
8653 my ($vmid, $include_suspended) = @_;
8654 my $qmpstatus = eval {
8655 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid);
8656 mon_cmd
($vmid, "query-status");
8659 return $qmpstatus && (
8660 $qmpstatus->{status
} eq "paused" ||
8661 $qmpstatus->{status
} eq "prelaunch" ||
8662 ($include_suspended && $qmpstatus->{status
} eq "suspended")
8666 sub check_volume_storage_type
{
8667 my ($storecfg, $vol) = @_;
8669 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($vol);
8670 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8671 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $vol);
8673 die "storage '$storeid' does not support content-type '$vtype'\n"
8674 if !$scfg->{content
}->{$vtype};
8679 sub add_nets_bridge_fdb
{
8680 my ($conf, $vmid) = @_;
8682 for my $opt (keys %$conf) {
8683 next if $opt !~ m/^net(\d+)$/;
8684 my $iface = "tap${vmid}i$1";
8685 # NOTE: expect setups with learning off to *not* use auto-random-generation of MAC on start
8686 my $net = parse_net
($conf->{$opt}, 1) or next;
8688 my $mac = $net->{macaddr
};
8690 log_warn
("MAC learning disabled, but vNIC '$iface' has no static MAC to add to forwarding DB!")
8691 if !file_read_firstline
("/sys/class/net/$iface/brport/learning");
8695 my $bridge = $net->{bridge
};
8697 log_warn
("Interface '$iface' not attached to any bridge.");
8701 PVE
::Network
::SDN
::Zones
::add_bridge_fdb
($iface, $mac, $bridge);
8702 } elsif (-d
"/sys/class/net/$bridge/bridge") { # avoid fdb management with OVS for now
8703 PVE
::Network
::add_bridge_fdb
($iface, $mac);
8708 sub del_nets_bridge_fdb
{
8709 my ($conf, $vmid) = @_;
8711 for my $opt (keys %$conf) {
8712 next if $opt !~ m/^net(\d+)$/;
8713 my $iface = "tap${vmid}i$1";
8715 my $net = parse_net
($conf->{$opt}) or next;
8716 my $mac = $net->{macaddr
} or next;
8718 my $bridge = $net->{bridge
};
8720 PVE
::Network
::SDN
::Zones
::del_bridge_fdb
($iface, $mac, $bridge);
8721 } elsif (-d
"/sys/class/net/$bridge/bridge") { # avoid fdb management with OVS for now
8722 PVE
::Network
::del_bridge_fdb
($iface, $mac);
8727 sub create_ifaces_ipams_ips
{
8728 my ($conf, $vmid) = @_;
8730 return if !$have_sdn;
8732 foreach my $opt (keys %$conf) {
8733 if ($opt =~ m/^net(\d+)$/) {
8734 my $value = $conf->{$opt};
8735 my $net = PVE
::QemuServer
::parse_net
($value);
8736 eval { PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($net->{bridge
}, $conf->{name
}, $net->{macaddr
}, $vmid, undef, 1) };
8742 sub delete_ifaces_ipams_ips
{
8743 my ($conf, $vmid) = @_;
8745 return if !$have_sdn;
8747 foreach my $opt (keys %$conf) {
8748 if ($opt =~ m/^net(\d+)$/) {
8749 my $net = PVE
::QemuServer
::parse_net
($conf->{$opt});
8750 eval { PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($net->{bridge
}, $net->{macaddr
}, $conf->{name
}) };