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);
37 use PVE
::Mapping
::PCI
;
38 use PVE
::Mapping
::USB
;
40 use PVE
::JSONSchema
qw(get_standard_option parse_property_string);
43 use PVE
::RESTEnvironment
qw(log_warn);
44 use PVE
::RPCEnvironment
;
48 use PVE
::Tools
qw(run_command file_read_firstline file_get_contents dir_glob_foreach get_host_arch $IPV6RE);
52 use PVE
::QemuServer
::Helpers
qw(config_aware_timeout min_version windows_version);
53 use PVE
::QemuServer
::Cloudinit
;
54 use PVE
::QemuServer
::CGroup
;
55 use PVE
::QemuServer
::CPUConfig
qw(print_cpu_device get_cpu_options);
56 use PVE
::QemuServer
::Drive
qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom drive_is_read_only parse_drive print_drive path_is_scsi);
57 use PVE
::QemuServer
::Machine
;
58 use PVE
::QemuServer
::Memory
qw(get_current_memory);
59 use PVE
::QemuServer
::Monitor
qw(mon_cmd);
60 use PVE
::QemuServer
::PCI
qw(print_pci_addr print_pcie_addr print_pcie_root_port parse_hostpci);
61 use PVE
::QemuServer
::QMPHelpers
qw(qemu_deviceadd qemu_devicedel qemu_objectadd qemu_objectdel);
62 use PVE
::QemuServer
::USB
;
66 require PVE
::Network
::SDN
::Zones
;
67 require PVE
::Network
::SDN
::Vnets
;
71 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
75 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
76 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
79 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
80 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
83 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
84 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
87 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
88 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
90 # FIXME: These are legacy 2MB-sized images that modern OVMF doesn't supports to build
91 # anymore. how can we deperacate this sanely without breaking existing instances, or using
92 # older backups and snapshot?
94 "$EDK2_FW_BASE/OVMF_CODE.fd",
95 "$EDK2_FW_BASE/OVMF_VARS.fd",
100 "$EDK2_FW_BASE/AAVMF_CODE.fd",
101 "$EDK2_FW_BASE/AAVMF_VARS.fd",
106 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
108 # Note about locking: we use flock on the config file protect against concurent actions.
109 # Aditionaly, we have a 'lock' setting in the config file. This can be set to 'migrate',
110 # 'backup', 'snapshot' or 'rollback'. Most actions are not allowed when such lock is set.
111 # But you can ignore this kind of lock with the --skiplock flag.
119 PVE
::JSONSchema
::register_standard_option
('pve-qm-stateuri', {
120 description
=> "Some command save/restore state from this location.",
126 PVE
::JSONSchema
::register_standard_option
('pve-qemu-machine', {
127 description
=> "Specifies the QEMU machine type.",
129 pattern
=> '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
134 # FIXME: remove in favor of just using the INotify one, it's cached there exactly the same way
137 $nodename_cache //= PVE
::INotify
::nodename
();
138 return $nodename_cache;
145 enum
=> [qw(i6300esb ib700)],
146 description
=> "Watchdog type to emulate.",
147 default => 'i6300esb',
152 enum
=> [qw(reset shutdown poweroff pause debug none)],
153 description
=> "The action to perform if after activation the guest fails to poll the watchdog in time.",
157 PVE
::JSONSchema
::register_format
('pve-qm-watchdog', $watchdog_fmt);
161 description
=> "Enable/disable communication with a QEMU Guest Agent (QGA) running in the VM.",
166 fstrim_cloned_disks
=> {
167 description
=> "Run fstrim after moving a disk or migrating the VM.",
172 'freeze-fs-on-backup' => {
173 description
=> "Freeze/thaw guest filesystems on backup for consistency.",
179 description
=> "Select the agent type",
183 enum
=> [qw(virtio isa)],
189 description
=> "Select the VGA type.",
194 enum
=> [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio virtio-gl vmware)],
197 description
=> "Sets the VGA memory (in MiB). Has no effect with serial display.",
204 description
=> 'Enable a specific clipboard. If not set, depending on the display type the'
205 .' SPICE one will be added. Migration with VNC clipboard is not yet supported!',
216 description
=> "The size of the file in MB.",
220 pattern
=> '[a-zA-Z0-9\-]+',
222 format_description
=> 'string',
223 description
=> "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
230 enum
=> [qw(ich9-intel-hda intel-hda AC97)],
231 description
=> "Configure an audio device."
235 enum
=> ['spice', 'none'],
238 description
=> "Driver backend for the audio device."
242 my $spice_enhancements_fmt = {
247 description
=> "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
251 enum
=> ['off', 'all', 'filter'],
254 description
=> "Enable video streaming. Uses compression for detected video streams."
261 enum
=> ['/dev/urandom', '/dev/random', '/dev/hwrng'],
263 description
=> "The file on the host to gather entropy from. In most cases '/dev/urandom'"
264 ." should be preferred over '/dev/random' to avoid entropy-starvation issues on the"
265 ." host. Using urandom does *not* decrease security in any meaningful way, as it's"
266 ." still seeded from real entropy, and the bytes provided will most likely be mixed"
267 ." with real entropy on the guest as well. '/dev/hwrng' can be used to pass through"
268 ." a hardware RNG from the host.",
272 description
=> "Maximum bytes of entropy allowed to get injected into the guest every"
273 ." 'period' milliseconds. Prefer a lower value when using '/dev/random' as source. Use"
274 ." `0` to disable limiting (potentially dangerous!).",
277 # default is 1 KiB/s, provides enough entropy to the guest to avoid boot-starvation issues
278 # (e.g. systemd etc...) while allowing no chance of overwhelming the host, provided we're
279 # reading from /dev/urandom
284 description
=> "Every 'period' milliseconds the entropy-injection quota is reset, allowing"
285 ." the guest to retrieve another 'max_bytes' of entropy.",
291 my $meta_info_fmt = {
294 description
=> "The guest creation timestamp as UNIX epoch time",
300 description
=> "The QEMU (machine) version from the time this VM was created.",
301 pattern
=> '\d+(\.\d+)+',
310 description
=> "Specifies whether a VM will be started during system bootup.",
316 description
=> "Automatic restart after crash (currently ignored).",
321 type
=> 'string', format
=> 'pve-hotplug-features',
322 description
=> "Selectively enable hotplug features. This is a comma separated list of"
323 ." hotplug features: 'network', 'disk', 'cpu', 'memory', 'usb' and 'cloudinit'. Use '0' to disable"
324 ." hotplug completely. Using '1' as value is an alias for the default `network,disk,usb`."
325 ." USB hotplugging is possible for guests with machine version >= 7.1 and ostype l26 or"
327 default => 'network,disk,usb',
332 description
=> "Allow reboot. If set to '0' the VM exit on reboot.",
338 description
=> "Lock/unlock the VM.",
339 enum
=> [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
344 description
=> "Limit of CPU usage.",
345 verbose_description
=> "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has"
346 ." total of '2' CPU time. Value '0' indicates no CPU limit.",
354 description
=> "CPU weight for a VM, will be clamped to [1, 10000] in cgroup v2.",
355 verbose_description
=> "CPU weight for a VM. Argument is used in the kernel fair scheduler."
356 ." The larger the number is, the more CPU time this VM gets. Number is relative to"
357 ." weights of all the other running VMs.",
360 default => 'cgroup v1: 1024, cgroup v2: 100',
365 description
=> "Memory properties.",
366 format
=> $PVE::QemuServer
::Memory
::memory_fmt
371 description
=> "Amount of target RAM for the VM in MiB. Using zero disables the ballon driver.",
377 description
=> "Amount of memory shares for auto-ballooning. The larger the number is, the"
378 ." more memory this VM gets. Number is relative to weights of all other running VMs."
379 ." Using zero disables auto-ballooning. Auto-ballooning is done by pvestatd.",
387 description
=> "Keyboard layout for VNC server. This option is generally not required and"
388 ." is often better handled from within the guest OS.",
389 enum
=> PVE
::Tools
::kvmkeymaplist
(),
394 type
=> 'string', format
=> 'dns-name',
395 description
=> "Set a name for the VM. Only used on the configuration web interface.",
400 description
=> "SCSI controller model",
401 enum
=> [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
407 description
=> "Description for the VM. Shown in the web-interface VM's summary."
408 ." This is saved as comment inside the configuration file.",
409 maxLength
=> 1024 * 8,
414 enum
=> [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 win11 l24 l26 solaris)],
415 description
=> "Specify guest operating system.",
416 verbose_description
=> <<EODESC,
417 Specify guest operating system. This is used to enable special
418 optimization/features for specific operating systems:
421 other;; unspecified OS
422 wxp;; Microsoft Windows XP
423 w2k;; Microsoft Windows 2000
424 w2k3;; Microsoft Windows 2003
425 w2k8;; Microsoft Windows 2008
426 wvista;; Microsoft Windows Vista
427 win7;; Microsoft Windows 7
428 win8;; Microsoft Windows 8/2012/2012r2
429 win10;; Microsoft Windows 10/2016/2019
430 win11;; Microsoft Windows 11/2022
431 l24;; Linux 2.4 Kernel
432 l26;; Linux 2.6 - 6.X Kernel
433 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
438 type
=> 'string', format
=> 'pve-qm-boot',
439 description
=> "Specify guest boot order. Use the 'order=' sub-property as usage with no"
440 ." key or 'legacy=' is deprecated.",
444 type
=> 'string', format
=> 'pve-qm-bootdisk',
445 description
=> "Enable booting from specified disk. Deprecated: Use 'boot: order=foo;bar' instead.",
446 pattern
=> '(ide|sata|scsi|virtio)\d+',
451 description
=> "The number of CPUs. Please use option -sockets instead.",
458 description
=> "The number of CPU sockets.",
465 description
=> "The number of cores per socket.",
472 description
=> "Enable/disable NUMA.",
478 description
=> "Enable/disable hugepages memory.",
479 enum
=> [qw(any 2 1024)],
485 description
=> "Use together with hugepages. If enabled, hugepages will not not be deleted"
486 ." after VM shutdown and can be used for subsequent starts.",
491 description
=> "Number of hotplugged vcpus.",
498 description
=> "Enable/disable ACPI.",
503 description
=> "Enable/disable communication with the QEMU Guest Agent and its properties.",
505 format
=> $agent_fmt,
510 description
=> "Enable/disable KVM hardware virtualization.",
516 description
=> "Enable/disable time drift fix.",
522 description
=> "Set the real time clock (RTC) to local time. This is enabled by default if"
523 ." the `ostype` indicates a Microsoft Windows OS.",
528 description
=> "Freeze CPU at startup (use 'c' monitor command to start execution).",
532 type
=> 'string', format
=> $vga_fmt,
533 description
=> "Configure the VGA hardware.",
534 verbose_description
=> "Configure the VGA Hardware. If you want to use high resolution"
535 ." modes (>= 1280x1024x16) you may need to increase the vga memory option. Since QEMU"
536 ." 2.9 the default VGA display type is 'std' for all OS types besides some Windows"
537 ." versions (XP and older) which use 'cirrus'. The 'qxl' option enables the SPICE"
538 ." display server. For win* OS you can select how many independent displays you want,"
539 ." Linux guests can add displays them self.\nYou can also run without any graphic card,"
540 ." using a serial device as terminal.",
544 type
=> 'string', format
=> 'pve-qm-watchdog',
545 description
=> "Create a virtual hardware watchdog device.",
546 verbose_description
=> "Create a virtual hardware watchdog device. Once enabled (by a guest"
547 ." action), the watchdog must be periodically polled by an agent inside the guest or"
548 ." else the watchdog will reset the guest (or execute the respective action specified)",
553 typetext
=> "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
554 description
=> "Set the initial date of the real time clock. Valid format for date are:"
555 ."'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
556 pattern
=> '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
559 startup
=> get_standard_option
('pve-startup-order'),
563 description
=> "Enable/disable Template.",
569 description
=> "Arbitrary arguments passed to kvm.",
570 verbose_description
=> <<EODESCR,
571 Arbitrary arguments passed to kvm, for example:
573 args: -no-reboot -smbios 'type=0,vendor=FOO'
575 NOTE: this option is for experts only.
582 description
=> "Enable/disable the USB tablet device.",
583 verbose_description
=> "Enable/disable the USB tablet device. This device is usually needed"
584 ." to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with"
585 ." normal VNC clients. If you're running lots of console-only guests on one host, you"
586 ." may consider disabling this to save some context switches. This is turned off by"
587 ." default if you use spice (`qm set <vmid> --vga qxl`).",
592 description
=> "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
596 migrate_downtime
=> {
599 description
=> "Set maximum tolerated downtime (in seconds) for migrations.",
605 type
=> 'string', format
=> 'pve-qm-ide',
606 typetext
=> '<volume>',
607 description
=> "This is an alias for option -ide2",
611 description
=> "Emulated CPU type.",
613 format
=> 'pve-vm-cpu-conf',
615 parent
=> get_standard_option
('pve-snapshot-name', {
617 description
=> "Parent snapshot name. This is used internally, and should not be modified.",
621 description
=> "Timestamp for snapshots.",
627 type
=> 'string', format
=> 'pve-volume-id',
628 description
=> "Reference to a volume which stores the VM state. This is used internally"
631 vmstatestorage
=> get_standard_option
('pve-storage-id', {
632 description
=> "Default storage for VM state volumes/files.",
635 runningmachine
=> get_standard_option
('pve-qemu-machine', {
636 description
=> "Specifies the QEMU machine type of the running vm. This is used internally"
640 description
=> "Specifies the QEMU '-cpu' parameter of the running vm. This is used"
641 ." internally for snapshots.",
644 pattern
=> $PVE::QemuServer
::CPUConfig
::qemu_cmdline_cpu_re
,
645 format_description
=> 'QEMU -cpu parameter'
647 machine
=> get_standard_option
('pve-qemu-machine'),
649 description
=> "Virtual processor architecture. Defaults to the host.",
652 enum
=> [qw(x86_64 aarch64)],
655 description
=> "Specify SMBIOS type 1 fields.",
656 type
=> 'string', format
=> 'pve-qm-smbios1',
663 description
=> "Sets the protection flag of the VM. This will disable the remove VM and"
664 ." remove disk operations.",
670 enum
=> [ qw(seabios ovmf) ],
671 description
=> "Select BIOS implementation.",
672 default => 'seabios',
676 pattern
=> '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
677 format_description
=> 'UUID',
678 description
=> "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0'"
679 ." to disable explicitly.",
680 verbose_description
=> "The VM generation ID (vmgenid) device exposes a 128-bit integer"
681 ." value identifier to the guest OS. This allows to notify the guest operating system"
682 ." when the virtual machine is executed with a different configuration (e.g. snapshot"
683 ." execution or creation from a template). The guest operating system notices the"
684 ." change, and is then able to react as appropriate by marking its copies of"
685 ." distributed databases as dirty, re-initializing its random number generator, etc.\n"
686 ."Note that auto-creation only works when done through API/CLI create or update methods"
687 .", but not when manually editing the config file.",
688 default => "1 (autogenerated)",
693 format
=> 'pve-volume-id',
695 description
=> "Script that will be executed during various steps in the vms lifetime.",
699 format
=> $ivshmem_fmt,
700 description
=> "Inter-VM shared memory. Useful for direct communication between VMs, or to"
706 format
=> $audio_fmt,
707 description
=> "Configure a audio device, useful in combination with QXL/Spice.",
710 spice_enhancements
=> {
712 format
=> $spice_enhancements_fmt,
713 description
=> "Configure additional enhancements for SPICE.",
717 type
=> 'string', format
=> 'pve-tag-list',
718 description
=> 'Tags of the VM. This is only meta information.',
724 description
=> "Configure a VirtIO-based Random Number Generator.",
729 format
=> $meta_info_fmt,
730 description
=> "Some (read-only) meta-information about this guest.",
734 type
=> 'string', format
=> 'pve-cpuset',
735 description
=> "List of host cores used to execute guest processes, for example: 0,5,8-11",
744 description
=> 'Specify a custom file containing all meta data passed to the VM via"
745 ." cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
746 format
=> 'pve-volume-id',
747 format_description
=> 'volume',
752 description
=> 'To pass a custom file containing all network data to the VM via cloud-init.',
753 format
=> 'pve-volume-id',
754 format_description
=> 'volume',
759 description
=> 'To pass a custom file containing all user data to the VM via cloud-init.',
760 format
=> 'pve-volume-id',
761 format_description
=> 'volume',
766 description
=> 'To pass a custom file containing all vendor data to the VM via cloud-init.',
767 format
=> 'pve-volume-id',
768 format_description
=> 'volume',
771 PVE
::JSONSchema
::register_format
('pve-qm-cicustom', $cicustom_fmt);
773 # any new option might need to be added to $cloudinitoptions in PVE::API2::Qemu
774 my $confdesc_cloudinit = {
778 description
=> 'Specifies the cloud-init configuration format. The default depends on the'
779 .' configured operating system type (`ostype`. We use the `nocloud` format for Linux,'
780 .' and `configdrive2` for windows.',
781 enum
=> ['configdrive2', 'nocloud', 'opennebula'],
786 description
=> "cloud-init: User name to change ssh keys and password for instead of the"
787 ." image's configured default user.",
792 description
=> 'cloud-init: Password to assign the user. Using this is generally not'
793 .' recommended. Use ssh keys instead. Also note that older cloud-init versions do not'
794 .' support hashed passwords.',
799 description
=> 'cloud-init: do an automatic package upgrade after the first boot.',
805 description
=> 'cloud-init: Specify custom files to replace the automatically generated'
807 format
=> 'pve-qm-cicustom',
812 description
=> 'cloud-init: Sets DNS search domains for a container. Create will'
813 .' automatically use the setting from the host if neither searchdomain nor nameserver'
818 type
=> 'string', format
=> 'address-list',
819 description
=> 'cloud-init: Sets DNS server IP address for a container. Create will'
820 .' automatically use the setting from the host if neither searchdomain nor nameserver'
826 format
=> 'urlencoded',
827 description
=> "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
831 # what about other qemu settings ?
833 #machine => 'string',
846 ##soundhw => 'string',
848 while (my ($k, $v) = each %$confdesc) {
849 PVE
::JSONSchema
::register_standard_option
("pve-qm-$k", $v);
853 my $MAX_SERIAL_PORTS = 4;
854 my $MAX_PARALLEL_PORTS = 3;
856 for (my $i = 0; $i < $PVE::QemuServer
::Memory
::MAX_NUMA
; $i++) {
857 $confdesc->{"numa$i"} = $PVE::QemuServer
::Memory
::numadesc
;
860 my $nic_model_list = [
876 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
878 my $net_fmt_bridge_descr = <<__EOD__;
879 Bridge to attach the network device to. The Proxmox VE standard bridge
882 If you do not specify a bridge, we create a kvm user (NATed) network
883 device, which provides DHCP and DNS services. The following addresses
890 The DHCP server assign addresses to the guest starting from 10.0.2.15.
894 macaddr
=> get_standard_option
('mac-addr', {
895 description
=> "MAC address. That address must be unique withing your network. This is"
896 ." automatically generated if not specified.",
900 description
=> "Network Card Model. The 'virtio' model provides the best performance with"
901 ." very low CPU overhead. If your guest does not support this driver, it is usually"
902 ." best to use 'e1000'.",
903 enum
=> $nic_model_list,
906 (map { $_ => { keyAlias
=> 'model', alias
=> 'macaddr' }} @$nic_model_list),
907 bridge
=> get_standard_option
('pve-bridge-id', {
908 description
=> $net_fmt_bridge_descr,
913 minimum
=> 0, maximum
=> 64,
914 description
=> 'Number of packet queues to be used on the device.',
920 description
=> "Rate limit in mbps (megabytes per second) as floating point number.",
925 minimum
=> 1, maximum
=> 4094,
926 description
=> 'VLAN tag to apply to packets on this interface.',
931 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
932 description
=> 'VLAN trunks to pass through this interface.',
933 format_description
=> 'vlanid[;vlanid...]',
938 description
=> 'Whether this interface should be protected by the firewall.',
943 description
=> 'Whether this interface should be disconnected (like pulling the plug).',
948 minimum
=> 1, maximum
=> 65520,
949 description
=> "Force MTU, for VirtIO only. Set to '1' to use the bridge MTU",
956 type
=> 'string', format
=> $net_fmt,
957 description
=> "Specify network devices.",
960 PVE
::JSONSchema
::register_standard_option
("pve-qm-net", $netdesc);
965 format
=> 'pve-ipv4-config',
966 format_description
=> 'IPv4Format/CIDR',
967 description
=> 'IPv4 address in CIDR format.',
974 format_description
=> 'GatewayIPv4',
975 description
=> 'Default gateway for IPv4 traffic.',
981 format
=> 'pve-ipv6-config',
982 format_description
=> 'IPv6Format/CIDR',
983 description
=> 'IPv6 address in CIDR format.',
990 format_description
=> 'GatewayIPv6',
991 description
=> 'Default gateway for IPv6 traffic.',
996 PVE
::JSONSchema
::register_format
('pve-qm-ipconfig', $ipconfig_fmt);
999 type
=> 'string', format
=> 'pve-qm-ipconfig',
1000 description
=> <<'EODESCR',
1001 cloud-init: Specify IP addresses and gateways for the corresponding interface.
1003 IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
1005 The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit
1006 gateway should be provided.
1007 For IPv6 the special string 'auto' can be used to use stateless autoconfiguration. This requires
1008 cloud-init 19.4 or newer.
1010 If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using
1014 PVE
::JSONSchema
::register_standard_option
("pve-qm-ipconfig", $netdesc);
1016 for (my $i = 0; $i < $MAX_NETS; $i++) {
1017 $confdesc->{"net$i"} = $netdesc;
1018 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
1021 foreach my $key (keys %$confdesc_cloudinit) {
1022 $confdesc->{$key} = $confdesc_cloudinit->{$key};
1025 PVE
::JSONSchema
::register_format
('pve-cpuset', \
&pve_verify_cpuset
);
1026 sub pve_verify_cpuset
{
1027 my ($set_text, $noerr) = @_;
1029 my ($count, $members) = eval { PVE
::CpuSet
::parse_cpuset
($set_text) };
1033 die "unable to parse cpuset option\n";
1036 return PVE
::CpuSet-
>new($members)->short_string();
1039 PVE
::JSONSchema
::register_format
('pve-volume-id-or-qm-path', \
&verify_volume_id_or_qm_path
);
1040 sub verify_volume_id_or_qm_path
{
1041 my ($volid, $noerr) = @_;
1043 return $volid if $volid eq 'none' || $volid eq 'cdrom';
1045 return verify_volume_id_or_absolute_path
($volid, $noerr);
1048 PVE
::JSONSchema
::register_format
('pve-volume-id-or-absolute-path', \
&verify_volume_id_or_absolute_path
);
1049 sub verify_volume_id_or_absolute_path
{
1050 my ($volid, $noerr) = @_;
1052 return $volid if $volid =~ m
|^/|;
1054 $volid = eval { PVE
::JSONSchema
::check_format
('pve-volume-id', $volid, '') };
1065 pattern
=> '(/dev/.+|socket)',
1066 description
=> "Create a serial device inside the VM (n is 0 to 3)",
1067 verbose_description
=> <<EODESCR,
1068 Create a serial device inside the VM (n is 0 to 3), and pass through a
1069 host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1070 host side (use 'qm terminal' to open a terminal connection).
1072 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines -
1073 use with special care.
1075 CAUTION: Experimental! User reported problems with this option.
1082 pattern
=> '/dev/parport\d+|/dev/usb/lp\d+',
1083 description
=> "Map host parallel devices (n is 0 to 2).",
1084 verbose_description
=> <<EODESCR,
1085 Map host parallel devices (n is 0 to 2).
1087 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1088 machines - use with special care.
1090 CAUTION: Experimental! User reported problems with this option.
1094 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1095 $confdesc->{"parallel$i"} = $paralleldesc;
1098 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1099 $confdesc->{"serial$i"} = $serialdesc;
1102 for (my $i = 0; $i < $PVE::QemuServer
::PCI
::MAX_HOSTPCI_DEVICES
; $i++) {
1103 $confdesc->{"hostpci$i"} = $PVE::QemuServer
::PCI
::hostpcidesc
;
1106 for my $key (keys %{$PVE::QemuServer
::Drive
::drivedesc_hash
}) {
1107 $confdesc->{$key} = $PVE::QemuServer
::Drive
::drivedesc_hash-
>{$key};
1110 for (my $i = 0; $i < $PVE::QemuServer
::USB
::MAX_USB_DEVICES
; $i++) {
1111 $confdesc->{"usb$i"} = $PVE::QemuServer
::USB
::usbdesc
;
1119 description
=> "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)."
1120 . " Deprecated, use 'order=' instead.",
1121 pattern
=> '[acdn]{1,4}',
1122 format_description
=> "[acdn]{1,4}",
1124 # note: this is also the fallback if boot: is not given at all
1130 format
=> 'pve-qm-bootdev-list',
1131 format_description
=> "device[;device...]",
1132 description
=> <<EODESC,
1133 The guest will attempt to boot from devices in the order they appear here.
1135 Disks, optical drives and passed-through storage USB devices will be directly
1136 booted from, NICs will load PXE, and PCIe devices will either behave like disks
1137 (e.g. NVMe) or load an option ROM (e.g. RAID controller, hardware NIC).
1139 Note that only devices in this list will be marked as bootable and thus loaded
1140 by the guest firmware (BIOS/UEFI). If you require multiple disks for booting
1141 (e.g. software-raid), you need to specify all of them here.
1143 Overrides the deprecated 'legacy=[acdn]*' value when given.
1147 PVE
::JSONSchema
::register_format
('pve-qm-boot', $boot_fmt);
1149 PVE
::JSONSchema
::register_format
('pve-qm-bootdev', \
&verify_bootdev
);
1150 sub verify_bootdev
{
1151 my ($dev, $noerr) = @_;
1153 my $special = $dev =~ m/^efidisk/ || $dev =~ m/^tpmstate/;
1154 return $dev if PVE
::QemuServer
::Drive
::is_valid_drivename
($dev) && !$special;
1158 return 0 if $dev !~ m/^$base\d+$/;
1159 return 0 if !$confdesc->{$dev};
1163 return $dev if $check->("net");
1164 return $dev if $check->("usb");
1165 return $dev if $check->("hostpci");
1168 die "invalid boot device '$dev'\n";
1171 sub print_bootorder
{
1173 return "" if !@$devs;
1174 my $data = { order
=> join(';', @$devs) };
1175 return PVE
::JSONSchema
::print_property_string
($data, $boot_fmt);
1178 my $kvm_api_version = 0;
1181 return $kvm_api_version if $kvm_api_version;
1183 open my $fh, '<', '/dev/kvm' or return;
1185 # 0xae00 => KVM_GET_API_VERSION
1186 $kvm_api_version = ioctl($fh, 0xae00, 0);
1189 return $kvm_api_version;
1192 my $kvm_user_version = {};
1195 sub kvm_user_version
{
1198 $binary //= get_command_for_arch
(get_host_arch
()); # get the native arch by default
1199 my $st = stat($binary);
1201 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1202 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1203 $cachedmtime == $st->mtime;
1205 $kvm_user_version->{$binary} = 'unknown';
1206 $kvm_mtime->{$binary} = $st->mtime;
1210 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1211 $kvm_user_version->{$binary} = $2;
1215 eval { run_command
([$binary, '--version'], outfunc
=> $code); };
1218 return $kvm_user_version->{$binary};
1221 my sub extract_version
{
1222 my ($machine_type, $version) = @_;
1223 $version = kvm_user_version
() if !defined($version);
1224 return PVE
::QemuServer
::Machine
::extract_version
($machine_type, $version)
1227 sub kernel_has_vhost_net
{
1228 return -c
'/dev/vhost-net';
1233 return defined($confdesc->{$key});
1237 sub get_cdrom_path
{
1239 return $cdrom_path if defined($cdrom_path);
1241 $cdrom_path = first
{ -l
$_ } map { "/dev/cdrom$_" } ('', '1', '2');
1243 if (!defined($cdrom_path)) {
1244 log_warn
("no physical CD-ROM available, ignoring");
1252 my ($storecfg, $vmid, $cdrom) = @_;
1254 if ($cdrom eq 'cdrom') {
1255 return get_cdrom_path
();
1256 } elsif ($cdrom eq 'none') {
1258 } elsif ($cdrom =~ m
|^/|) {
1261 return PVE
::Storage
::path
($storecfg, $cdrom);
1265 # try to convert old style file names to volume IDs
1266 sub filename_to_volume_id
{
1267 my ($vmid, $file, $media) = @_;
1269 if (!($file eq 'none' || $file eq 'cdrom' ||
1270 $file =~ m
|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1272 return if $file =~ m
|/|;
1274 if ($media && $media eq 'cdrom') {
1275 $file = "local:iso/$file";
1277 $file = "local:$vmid/$file";
1284 sub verify_media_type
{
1285 my ($opt, $vtype, $media) = @_;
1290 if ($media eq 'disk') {
1292 } elsif ($media eq 'cdrom') {
1295 die "internal error";
1298 return if ($vtype eq $etype);
1300 raise_param_exc
({ $opt => "unexpected media type ($vtype != $etype)" });
1303 sub cleanup_drive_path
{
1304 my ($opt, $storecfg, $drive) = @_;
1306 # try to convert filesystem paths to volume IDs
1308 if (($drive->{file
} !~ m/^(cdrom|none)$/) &&
1309 ($drive->{file
} !~ m
|^/dev/.+|) &&
1310 ($drive->{file
} !~ m/^([^:]+):(.+)$/) &&
1311 ($drive->{file
} !~ m/^\d+$/)) {
1312 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($storecfg, $drive->{file
});
1313 raise_param_exc
({ $opt => "unable to associate path '$drive->{file}' to any storage"})
1315 $drive->{media
} = 'cdrom' if !$drive->{media
} && $vtype eq 'iso';
1316 verify_media_type
($opt, $vtype, $drive->{media
});
1317 $drive->{file
} = $volid;
1320 $drive->{media
} = 'cdrom' if !$drive->{media
} && $drive->{file
} =~ m/^(cdrom|none)$/;
1323 sub parse_hotplug_features
{
1328 return $res if $data eq '0';
1330 $data = $confdesc->{hotplug
}->{default} if $data eq '1';
1332 foreach my $feature (PVE
::Tools
::split_list
($data)) {
1333 if ($feature =~ m/^(network|disk|cpu|memory|usb|cloudinit)$/) {
1336 die "invalid hotplug feature '$feature'\n";
1342 PVE
::JSONSchema
::register_format
('pve-hotplug-features', \
&pve_verify_hotplug_features
);
1343 sub pve_verify_hotplug_features
{
1344 my ($value, $noerr) = @_;
1346 return $value if parse_hotplug_features
($value);
1350 die "unable to parse hotplug option\n";
1353 sub assert_clipboard_config
{
1356 my $clipboard_regex = qr/^(std|cirrus|vmware|virtio|qxl)/;
1360 && $vga->{'clipboard'} eq 'vnc'
1362 && $vga->{type
} !~ $clipboard_regex
1364 die "vga type $vga->{type} is not compatible with VNC clipboard\n";
1368 sub print_tabletdevice_full
{
1369 my ($conf, $arch) = @_;
1371 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1373 # we use uhci for old VMs because tablet driver was buggy in older qemu
1375 if ($q35 || $arch eq 'aarch64') {
1381 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1384 sub print_keyboarddevice_full
{
1385 my ($conf, $arch) = @_;
1387 return if $arch ne 'aarch64';
1389 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1392 my sub get_drive_id
{
1394 return "$drive->{interface}$drive->{index}";
1397 sub print_drivedevice_full
{
1398 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1403 my $drive_id = get_drive_id
($drive);
1404 if ($drive->{interface
} eq 'virtio') {
1405 my $pciaddr = print_pci_addr
("$drive_id", $bridges, $arch, $machine_type);
1406 $device = "virtio-blk-pci,drive=drive-$drive_id,id=${drive_id}${pciaddr}";
1407 $device .= ",iothread=iothread-$drive_id" if $drive->{iothread
};
1408 } elsif ($drive->{interface
} eq 'scsi') {
1410 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
1411 my $unit = $drive->{index} % $maxdev;
1412 my $devicetype = 'hd';
1414 if (drive_is_cdrom
($drive)) {
1417 if ($drive->{file
} =~ m
|^/|) {
1418 $path = $drive->{file
};
1419 if (my $info = path_is_scsi
($path)) {
1420 if ($info->{type
} == 0 && $drive->{scsiblock
}) {
1421 $devicetype = 'block';
1422 } elsif ($info->{type
} == 1) { # tape
1423 $devicetype = 'generic';
1427 $path = PVE
::Storage
::path
($storecfg, $drive->{file
});
1430 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1431 my $version = extract_version
($machine_type, kvm_user_version
());
1432 if ($path =~ m/^iscsi\:\/\
// &&
1433 !min_version
($version, 4, 1)) {
1434 $devicetype = 'generic';
1438 if (!$conf->{scsihw
} || $conf->{scsihw
} =~ m/^lsi/ || $conf->{scsihw
} eq 'pvscsi') {
1439 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
1441 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
1442 .",lun=$drive->{index}";
1444 $device .= ",drive=drive-$drive_id,id=$drive_id";
1446 if ($drive->{ssd
} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1447 $device .= ",rotation_rate=1";
1449 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1451 } elsif ($drive->{interface
} eq 'ide' || $drive->{interface
} eq 'sata') {
1452 my $maxdev = ($drive->{interface
} eq 'sata') ?
$PVE::QemuServer
::Drive
::MAX_SATA_DISKS
: 2;
1453 my $controller = int($drive->{index} / $maxdev);
1454 my $unit = $drive->{index} % $maxdev;
1456 # machine type q35 only supports unit=0 for IDE rather than 2 units. This wasn't handled
1457 # correctly before, so e.g. index=2 was mapped to controller=1,unit=0 rather than
1458 # controller=2,unit=0. Note that odd indices never worked, as they would be mapped to
1459 # unit=1, so to keep backwards compat for migration, it suffices to keep even ones as they
1460 # were before. Move odd ones up by 2 where they don't clash.
1461 if (PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf) && $drive->{interface
} eq 'ide') {
1462 $controller += 2 * ($unit % 2);
1466 my $devicetype = ($drive->{media
} && $drive->{media
} eq 'cdrom') ?
"cd" : "hd";
1468 $device = "ide-$devicetype";
1469 if ($drive->{interface
} eq 'ide') {
1470 $device .= ",bus=ide.$controller,unit=$unit";
1472 $device .= ",bus=ahci$controller.$unit";
1474 $device .= ",drive=drive-$drive_id,id=$drive_id";
1476 if ($devicetype eq 'hd') {
1477 if (my $model = $drive->{model
}) {
1478 $model = URI
::Escape
::uri_unescape
($model);
1479 $device .= ",model=$model";
1481 if ($drive->{ssd
}) {
1482 $device .= ",rotation_rate=1";
1485 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1486 } elsif ($drive->{interface
} eq 'usb') {
1488 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1490 die "unsupported interface type";
1493 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex
};
1495 if (my $serial = $drive->{serial
}) {
1496 $serial = URI
::Escape
::uri_unescape
($serial);
1497 $device .= ",serial=$serial";
1504 sub get_initiator_name
{
1507 my $fh = IO
::File-
>new('/etc/iscsi/initiatorname.iscsi') || return;
1508 while (defined(my $line = <$fh>)) {
1509 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1518 my sub storage_allows_io_uring_default
{
1519 my ($scfg, $cache_direct) = @_;
1521 # io_uring with cache mode writeback or writethrough on krbd will hang...
1522 return if $scfg && $scfg->{type
} eq 'rbd' && $scfg->{krbd
} && !$cache_direct;
1524 # io_uring with cache mode writeback or writethrough on LVM will hang, without cache only
1525 # sometimes, just plain disable...
1526 return if $scfg && $scfg->{type
} eq 'lvm';
1528 # io_uring causes problems when used with CIFS since kernel 5.15
1529 # Some discussion: https://www.spinics.net/lists/linux-cifs/msg26734.html
1530 return if $scfg && $scfg->{type
} eq 'cifs';
1535 my sub drive_uses_cache_direct
{
1536 my ($drive, $scfg) = @_;
1538 my $cache_direct = 0;
1540 if (my $cache = $drive->{cache
}) {
1541 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1542 } elsif (!drive_is_cdrom
($drive) && !($scfg && $scfg->{type
} eq 'btrfs' && !$scfg->{nocow
})) {
1546 return $cache_direct;
1549 sub print_drive_commandline_full
{
1550 my ($storecfg, $vmid, $drive, $pbs_name, $io_uring) = @_;
1553 my $volid = $drive->{file
};
1554 my $format = $drive->{format
};
1555 my $drive_id = get_drive_id
($drive);
1557 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1558 my $scfg = $storeid ? PVE
::Storage
::storage_config
($storecfg, $storeid) : undef;
1560 if (drive_is_cdrom
($drive)) {
1561 $path = get_iso_path
($storecfg, $vmid, $volid);
1562 die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
1565 $path = PVE
::Storage
::path
($storecfg, $volid);
1566 $format //= qemu_img_format
($scfg, $volname);
1573 my $is_rbd = $path =~ m/^rbd:/;
1576 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1577 foreach my $o (@qemu_drive_options) {
1578 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1581 # snapshot only accepts on|off
1582 if (defined($drive->{snapshot
})) {
1583 my $v = $drive->{snapshot
} ?
'on' : 'off';
1584 $opts .= ",snapshot=$v";
1587 if (defined($drive->{ro
})) { # ro maps to QEMUs `readonly`, which accepts `on` or `off` only
1588 $opts .= ",readonly=" . ($drive->{ro
} ?
'on' : 'off');
1591 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1592 my ($dir, $qmpname) = @$type;
1593 if (my $v = $drive->{"mbps$dir"}) {
1594 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1596 if (my $v = $drive->{"mbps${dir}_max"}) {
1597 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1599 if (my $v = $drive->{"bps${dir}_max_length"}) {
1600 $opts .= ",throttling.bps$qmpname-max-length=$v";
1602 if (my $v = $drive->{"iops${dir}"}) {
1603 $opts .= ",throttling.iops$qmpname=$v";
1605 if (my $v = $drive->{"iops${dir}_max"}) {
1606 $opts .= ",throttling.iops$qmpname-max=$v";
1608 if (my $v = $drive->{"iops${dir}_max_length"}) {
1609 $opts .= ",throttling.iops$qmpname-max-length=$v";
1614 $format = "rbd" if $is_rbd;
1615 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1617 $opts .= ",format=alloc-track,file.driver=$format";
1619 $opts .= ",format=$format";
1622 my $cache_direct = drive_uses_cache_direct
($drive, $scfg);
1624 $opts .= ",cache=none" if !$drive->{cache
} && $cache_direct;
1626 if (!$drive->{aio
}) {
1627 if ($io_uring && storage_allows_io_uring_default
($scfg, $cache_direct)) {
1628 # io_uring supports all cache modes
1629 $opts .= ",aio=io_uring";
1631 # aio native works only with O_DIRECT
1633 $opts .= ",aio=native";
1635 $opts .= ",aio=threads";
1640 if (!drive_is_cdrom
($drive)) {
1642 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
1643 $detectzeroes = 'off';
1644 } elsif ($drive->{discard
}) {
1645 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
1647 # This used to be our default with discard not being specified:
1648 $detectzeroes = 'on';
1651 # note: 'detect-zeroes' works per blockdev and we want it to persist
1652 # after the alloc-track is removed, so put it on 'file' directly
1653 my $dz_param = $pbs_name ?
"file.detect-zeroes" : "detect-zeroes";
1654 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
1658 $opts .= ",backing=$pbs_name";
1659 $opts .= ",auto-remove=on";
1662 # my $file_param = $pbs_name ? "file.file.filename" : "file";
1663 my $file_param = "file";
1665 # non-rbd drivers require the underlying file to be a seperate block
1666 # node, so add a second .file indirection
1667 $file_param .= ".file" if !$is_rbd;
1668 $file_param .= ".filename";
1670 my $pathinfo = $path ?
"$file_param=$path," : '';
1672 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1675 sub print_pbs_blockdev
{
1676 my ($pbs_conf, $pbs_name) = @_;
1677 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1678 $blockdev .= ",repository=$pbs_conf->{repository}";
1679 $blockdev .= ",namespace=$pbs_conf->{namespace}" if $pbs_conf->{namespace
};
1680 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1681 $blockdev .= ",archive=$pbs_conf->{archive}";
1682 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile
};
1686 sub print_netdevice_full
{
1687 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version) = @_;
1689 my $device = $net->{model
};
1690 if ($net->{model
} eq 'virtio') {
1691 $device = 'virtio-net-pci';
1694 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
1695 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
1696 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
1697 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1698 # and out of each queue plus one config interrupt and control vector queue
1699 my $vectors = $net->{queues
} * 2 + 2;
1700 $tmpstr .= ",vectors=$vectors,mq=on";
1701 if (min_version
($machine_version, 7, 1)) {
1702 $tmpstr .= ",packed=on";
1706 if (min_version
($machine_version, 7, 1) && $net->{model
} eq 'virtio'){
1707 $tmpstr .= ",rx_queue_size=1024,tx_queue_size=256";
1710 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
1712 if (my $mtu = $net->{mtu
}) {
1713 if ($net->{model
} eq 'virtio' && $net->{bridge
}) {
1714 my $bridge_mtu = PVE
::Network
::read_bridge_mtu
($net->{bridge
});
1717 } elsif ($mtu < 576) {
1718 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1719 } elsif ($mtu > $bridge_mtu) {
1720 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1722 $tmpstr .= ",host_mtu=$mtu";
1724 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
1728 if ($use_old_bios_files) {
1730 if ($device eq 'virtio-net-pci') {
1731 $romfile = 'pxe-virtio.rom';
1732 } elsif ($device eq 'e1000') {
1733 $romfile = 'pxe-e1000.rom';
1734 } elsif ($device eq 'e1000e') {
1735 $romfile = 'pxe-e1000e.rom';
1736 } elsif ($device eq 'ne2k') {
1737 $romfile = 'pxe-ne2k_pci.rom';
1738 } elsif ($device eq 'pcnet') {
1739 $romfile = 'pxe-pcnet.rom';
1740 } elsif ($device eq 'rtl8139') {
1741 $romfile = 'pxe-rtl8139.rom';
1743 $tmpstr .= ",romfile=$romfile" if $romfile;
1749 sub print_netdev_full
{
1750 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
1753 if ($netid =~ m/^net(\d+)$/) {
1757 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
1759 my $ifname = "tap${vmid}i$i";
1761 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1762 die "interface name '$ifname' is too long (max 15 character)\n"
1763 if length($ifname) >= 16;
1765 my $vhostparam = '';
1766 if (is_native
($arch)) {
1767 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
1770 my $vmname = $conf->{name
} || "vm$vmid";
1773 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
1775 if ($net->{bridge
}) {
1776 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1777 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
1779 $netdev = "type=user,id=$netid,hostname=$vmname";
1782 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
1788 'cirrus' => 'cirrus-vga',
1790 'vmware' => 'vmware-svga',
1791 'virtio' => 'virtio-vga',
1792 'virtio-gl' => 'virtio-vga-gl',
1795 sub print_vga_device
{
1796 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
1798 my $type = $vga_map->{$vga->{type
}};
1799 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
1800 $type = 'virtio-gpu';
1802 my $vgamem_mb = $vga->{memory
};
1804 my $max_outputs = '';
1806 $type = $id ?
'qxl' : 'qxl-vga';
1808 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
1809 # set max outputs so linux can have up to 4 qxl displays with one device
1810 if (min_version
($machine_version, 4, 1)) {
1811 $max_outputs = ",max_outputs=4";
1816 die "no devicetype for $vga->{type}\n" if !$type;
1820 if ($vga->{type
} =~ /^virtio/) {
1821 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
1822 $memory = ",max_hostmem=$bytes";
1824 # from https://www.spice-space.org/multiple-monitors.html
1825 $memory = ",vgamem_mb=$vga->{memory}";
1826 my $ram = $vgamem_mb * 4;
1827 my $vram = $vgamem_mb * 2;
1828 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1830 $memory = ",vgamem_mb=$vga->{memory}";
1832 } elsif ($qxlnum && $id) {
1833 $memory = ",ram_size=67108864,vram_size=33554432";
1837 if ($type eq 'VGA' && windows_version
($conf->{ostype
})) {
1838 $edidoff=",edid=off" if (!defined($conf->{bios
}) || $conf->{bios
} ne 'ovmf');
1841 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1842 my $vgaid = "vga" . ($id // '');
1844 if ($q35 && $vgaid eq 'vga') {
1845 # the first display uses pcie.0 bus on q35 machines
1846 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
1848 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
1851 if ($vga->{type
} eq 'virtio-gl') {
1852 my $base = '/usr/lib/x86_64-linux-gnu/lib';
1853 die "missing libraries for '$vga->{type}' detected! Please install 'libgl1' and 'libegl1'\n"
1854 if !-e
"${base}EGL.so.1" || !-e
"${base}GL.so.1";
1856 die "no DRM render node detected (/dev/dri/renderD*), no GPU? - needed for '$vga->{type}' display\n"
1857 if !PVE
::Tools
::dir_glob_regex
('/dev/dri/', "renderD.*");
1860 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
1863 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1865 my ($data, $disable_mac_autogen) = @_;
1867 my $res = eval { parse_property_string
($net_fmt, $data) };
1872 if (!defined($res->{macaddr
}) && !$disable_mac_autogen) {
1873 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1874 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1879 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1880 sub parse_ipconfig
{
1883 my $res = eval { parse_property_string
($ipconfig_fmt, $data) };
1889 if ($res->{gw
} && !$res->{ip
}) {
1890 warn 'gateway specified without specifying an IP address';
1893 if ($res->{gw6
} && !$res->{ip6
}) {
1894 warn 'IPv6 gateway specified without specifying an IPv6 address';
1897 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
1898 warn 'gateway specified together with DHCP';
1901 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
1903 warn "IPv6 gateway specified together with $res->{ip6} address";
1907 if (!$res->{ip
} && !$res->{ip6
}) {
1908 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
1917 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
1920 sub add_random_macs
{
1921 my ($settings) = @_;
1923 foreach my $opt (keys %$settings) {
1924 next if $opt !~ m/^net(\d+)$/;
1925 my $net = parse_net
($settings->{$opt});
1927 $settings->{$opt} = print_net
($net);
1931 sub vm_is_volid_owner
{
1932 my ($storecfg, $vmid, $volid) = @_;
1934 if ($volid !~ m
|^/|) {
1936 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
1937 if ($owner && ($owner == $vmid)) {
1945 sub vmconfig_register_unused_drive
{
1946 my ($storecfg, $vmid, $conf, $drive) = @_;
1948 if (drive_is_cloudinit
($drive)) {
1949 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
1951 delete $conf->{cloudinit
};
1952 } elsif (!drive_is_cdrom
($drive)) {
1953 my $volid = $drive->{file
};
1954 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
1955 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
1960 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
1964 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
1965 format_description
=> 'UUID',
1966 description
=> "Set SMBIOS1 UUID.",
1971 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1972 format_description
=> 'Base64 encoded string',
1973 description
=> "Set SMBIOS1 version.",
1978 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1979 format_description
=> 'Base64 encoded string',
1980 description
=> "Set SMBIOS1 serial number.",
1985 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1986 format_description
=> 'Base64 encoded string',
1987 description
=> "Set SMBIOS1 manufacturer.",
1992 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1993 format_description
=> 'Base64 encoded string',
1994 description
=> "Set SMBIOS1 product ID.",
1999 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2000 format_description
=> 'Base64 encoded string',
2001 description
=> "Set SMBIOS1 SKU string.",
2006 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2007 format_description
=> 'Base64 encoded string',
2008 description
=> "Set SMBIOS1 family string.",
2013 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
2021 my $res = eval { parse_property_string
($smbios1_fmt, $data) };
2028 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2031 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2033 sub parse_watchdog
{
2038 my $res = eval { parse_property_string
($watchdog_fmt, $value) };
2043 sub parse_guest_agent
{
2046 return {} if !defined($conf->{agent
});
2048 my $res = eval { parse_property_string
($agent_fmt, $conf->{agent
}) };
2051 # if the agent is disabled ignore the other potentially set properties
2052 return {} if !$res->{enabled
};
2057 my ($conf, $key) = @_;
2058 return undef if !defined($conf->{agent
});
2060 my $agent = parse_guest_agent
($conf);
2061 return $agent->{$key};
2067 return {} if !$value;
2068 my $res = eval { parse_property_string
($vga_fmt, $value) };
2078 my $res = eval { parse_property_string
($rng_fmt, $value) };
2083 sub parse_meta_info
{
2088 my $res = eval { parse_property_string
($meta_info_fmt, $value) };
2093 sub new_meta_info_string
{
2094 my () = @_; # for now do not allow to override any value
2096 return PVE
::JSONSchema
::print_property_string
(
2098 'creation-qemu' => kvm_user_version
(),
2099 ctime
=> "". int(time()),
2105 sub qemu_created_version_fixups
{
2106 my ($conf, $forcemachine, $kvmver) = @_;
2108 my $meta = parse_meta_info
($conf->{meta
}) // {};
2109 my $forced_vers = PVE
::QemuServer
::Machine
::extract_version
($forcemachine);
2111 # check if we need to apply some handling for VMs that always use the latest machine version but
2112 # had a machine version transition happen that affected HW such that, e.g., an OS config change
2113 # would be required (we do not want to pin machine version for non-windows OS type)
2115 (!defined($conf->{machine
}) || $conf->{machine
} =~ m/^(?:pc|q35|virt)$/) # non-versioned machine
2116 && (!defined($meta->{'creation-qemu'}) || !min_version
($meta->{'creation-qemu'}, 6, 1)) # created before 6.1
2117 && (!$forced_vers || min_version
($forced_vers, 6, 1)) # handle snapshot-rollback/migrations
2118 && min_version
($kvmver, 6, 1) # only need to apply the change since 6.1
2120 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
2121 if ($q35 && $conf->{ostype
} && $conf->{ostype
} eq 'l26') {
2122 # this changed to default-on in Q 6.1 for q35 machines, it will mess with PCI slot view
2123 # and thus with the predictable interface naming of systemd
2124 return ['-global', 'ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off'];
2130 # add JSON properties for create and set function
2131 sub json_config_properties
{
2132 my ($prop, $with_disk_alloc) = @_;
2134 my $skip_json_config_opts = {
2138 runningmachine
=> 1,
2143 foreach my $opt (keys %$confdesc) {
2144 next if $skip_json_config_opts->{$opt};
2146 if ($with_disk_alloc && is_valid_drivename
($opt)) {
2147 $prop->{$opt} = $PVE::QemuServer
::Drive
::drivedesc_hash_with_alloc-
>{$opt};
2149 $prop->{$opt} = $confdesc->{$opt};
2156 # Properties that we can read from an OVF file
2157 sub json_ovf_properties
{
2160 for my $device (PVE
::QemuServer
::Drive
::valid_drive_names
()) {
2161 $prop->{$device} = {
2163 format
=> 'pve-volume-id-or-absolute-path',
2164 description
=> "Disk image that gets imported to $device",
2171 description
=> "The number of CPU cores.",
2176 description
=> "Amount of RAM for the VM in MB.",
2181 description
=> "Name of the VM.",
2188 # return copy of $confdesc_cloudinit to generate documentation
2189 sub cloudinit_config_properties
{
2191 return dclone
($confdesc_cloudinit);
2194 sub cloudinit_pending_properties
{
2196 map { $_ => 1 } keys $confdesc_cloudinit->%*,
2199 $p->{"net$_"} = 1 for 0..($MAX_NETS-1);
2204 my ($key, $value) = @_;
2206 die "unknown setting '$key'\n" if !$confdesc->{$key};
2208 my $type = $confdesc->{$key}->{type
};
2210 if (!defined($value)) {
2211 die "got undefined value\n";
2214 if ($value =~ m/[\n\r]/) {
2215 die "property contains a line feed\n";
2218 if ($type eq 'boolean') {
2219 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2220 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2221 die "type check ('boolean') failed - got '$value'\n";
2222 } elsif ($type eq 'integer') {
2223 return int($1) if $value =~ m/^(\d+)$/;
2224 die "type check ('integer') failed - got '$value'\n";
2225 } elsif ($type eq 'number') {
2226 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2227 die "type check ('number') failed - got '$value'\n";
2228 } elsif ($type eq 'string') {
2229 if (my $fmt = $confdesc->{$key}->{format
}) {
2230 PVE
::JSONSchema
::check_format
($fmt, $value);
2233 $value =~ s/^\"(.*)\"$/$1/;
2236 die "internal error"
2241 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
2243 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2245 if (!$skiplock && !PVE
::QemuConfig-
>has_lock($conf, 'suspended')) {
2246 PVE
::QemuConfig-
>check_lock($conf);
2249 if ($conf->{template
}) {
2250 # check if any base image is still used by a linked clone
2251 PVE
::QemuConfig-
>foreach_volume_full($conf, { include_unused
=> 1 }, sub {
2252 my ($ds, $drive) = @_;
2253 return if drive_is_cdrom
($drive);
2255 my $volid = $drive->{file
};
2256 return if !$volid || $volid =~ m
|^/|;
2258 die "base volume '$volid' is still in use by linked cloned\n"
2259 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2265 my $remove_owned_drive = sub {
2266 my ($ds, $drive) = @_;
2267 return if drive_is_cdrom
($drive, 1);
2269 my $volid = $drive->{file
};
2270 return if !$volid || $volid =~ m
|^/|;
2271 return if $volids->{$volid};
2273 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2274 return if !$path || !$owner || ($owner != $vmid);
2276 $volids->{$volid} = 1;
2277 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2278 warn "Could not remove disk '$volid', check manually: $@" if $@;
2281 # only remove disks owned by this VM (referenced in the config)
2282 my $include_opts = {
2283 include_unused
=> 1,
2284 extra_keys
=> ['vmstate'],
2286 PVE
::QemuConfig-
>foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2288 for my $snap (values %{$conf->{snapshots
}}) {
2289 next if !defined($snap->{vmstate
});
2290 my $drive = PVE
::QemuConfig-
>parse_volume('vmstate', $snap->{vmstate
}, 1);
2291 next if !defined($drive);
2292 $remove_owned_drive->('vmstate', $drive);
2295 PVE
::QemuConfig-
>foreach_volume_full($conf->{pending
}, $include_opts, $remove_owned_drive);
2297 if ($purge_unreferenced) { # also remove unreferenced disk
2298 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid, undef, 'images');
2299 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2300 my ($volid, $sid, $volname, $d) = @_;
2301 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2306 eval { delete_ifaces_ipams_ips
($conf, $vmid)};
2309 if (defined $replacement_conf) {
2310 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2312 PVE
::QemuConfig-
>destroy_config($vmid);
2316 sub parse_vm_config
{
2317 my ($filename, $raw, $strict) = @_;
2319 return if !defined($raw);
2322 digest
=> Digest
::SHA
::sha1_hex
($raw),
2328 my $handle_error = sub {
2338 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2339 || die "got strange filename '$filename'";
2345 my $finish_description = sub {
2346 if (defined($descr)) {
2348 $conf->{description
} = $descr;
2354 my @lines = split(/\n/, $raw);
2355 foreach my $line (@lines) {
2356 next if $line =~ m/^\s*$/;
2358 if ($line =~ m/^\[PENDING\]\s*$/i) {
2359 $section = 'pending';
2360 $finish_description->();
2361 $conf = $res->{$section} = {};
2363 } elsif ($line =~ m/^\[special:cloudinit\]\s*$/i) {
2364 $section = 'cloudinit';
2365 $finish_description->();
2366 $conf = $res->{$section} = {};
2369 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2371 $finish_description->();
2372 $conf = $res->{snapshots
}->{$section} = {};
2376 if ($line =~ m/^\#(.*)$/) {
2377 $descr = '' if !defined($descr);
2378 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2382 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2383 $descr = '' if !defined($descr);
2384 $descr .= PVE
::Tools
::decode_text
($2);
2385 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2386 $conf->{snapstate
} = $1;
2387 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2390 $conf->{$key} = $value;
2391 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2393 if ($section eq 'pending') {
2394 $conf->{delete} = $value; # we parse this later
2396 $handle_error->("vm $vmid - property 'delete' is only allowed in [PENDING]\n");
2398 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2401 if ($section eq 'cloudinit') {
2402 # ignore validation only used for informative purpose
2403 $conf->{$key} = $value;
2406 eval { $value = check_type
($key, $value); };
2408 $handle_error->("vm $vmid - unable to parse value of '$key' - $@");
2410 $key = 'ide2' if $key eq 'cdrom';
2411 my $fmt = $confdesc->{$key}->{format
};
2412 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2413 my $v = parse_drive
($key, $value);
2414 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2415 $v->{file
} = $volid;
2416 $value = print_drive
($v);
2418 $handle_error->("vm $vmid - unable to parse value of '$key'\n");
2423 $conf->{$key} = $value;
2426 $handle_error->("vm $vmid - unable to parse config: $line\n");
2430 $finish_description->();
2431 delete $res->{snapstate
}; # just to be sure
2436 sub write_vm_config
{
2437 my ($filename, $conf) = @_;
2439 delete $conf->{snapstate
}; # just to be sure
2441 if ($conf->{cdrom
}) {
2442 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2443 $conf->{ide2
} = $conf->{cdrom
};
2444 delete $conf->{cdrom
};
2447 # we do not use 'smp' any longer
2448 if ($conf->{sockets
}) {
2449 delete $conf->{smp
};
2450 } elsif ($conf->{smp
}) {
2451 $conf->{sockets
} = $conf->{smp
};
2452 delete $conf->{cores
};
2453 delete $conf->{smp
};
2456 my $used_volids = {};
2458 my $cleanup_config = sub {
2459 my ($cref, $pending, $snapname) = @_;
2461 foreach my $key (keys %$cref) {
2462 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2463 $key eq 'snapstate' || $key eq 'pending' || $key eq 'cloudinit';
2464 my $value = $cref->{$key};
2465 if ($key eq 'delete') {
2466 die "propertry 'delete' is only allowed in [PENDING]\n"
2468 # fixme: check syntax?
2471 eval { $value = check_type
($key, $value); };
2472 die "unable to parse value of '$key' - $@" if $@;
2474 $cref->{$key} = $value;
2476 if (!$snapname && is_valid_drivename
($key)) {
2477 my $drive = parse_drive
($key, $value);
2478 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2483 &$cleanup_config($conf);
2485 &$cleanup_config($conf->{pending
}, 1);
2487 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2488 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2489 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2492 # remove 'unusedX' settings if we re-add a volume
2493 foreach my $key (keys %$conf) {
2494 my $value = $conf->{$key};
2495 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2496 delete $conf->{$key};
2500 my $generate_raw_config = sub {
2501 my ($conf, $pending) = @_;
2505 # add description as comment to top of file
2506 if (defined(my $descr = $conf->{description
})) {
2508 foreach my $cl (split(/\n/, $descr)) {
2509 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2512 $raw .= "#\n" if $pending;
2516 foreach my $key (sort keys %$conf) {
2517 next if $key =~ /^(digest|description|pending|cloudinit|snapshots)$/;
2518 $raw .= "$key: $conf->{$key}\n";
2523 my $raw = &$generate_raw_config($conf);
2525 if (scalar(keys %{$conf->{pending
}})){
2526 $raw .= "\n[PENDING]\n";
2527 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2530 if (scalar(keys %{$conf->{cloudinit
}}) && PVE
::QemuConfig-
>has_cloudinit($conf)){
2531 $raw .= "\n[special:cloudinit]\n";
2532 $raw .= &$generate_raw_config($conf->{cloudinit
});
2535 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2536 $raw .= "\n[$snapname]\n";
2537 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2547 # we use static defaults from our JSON schema configuration
2548 foreach my $key (keys %$confdesc) {
2549 if (defined(my $default = $confdesc->{$key}->{default})) {
2550 $res->{$key} = $default;
2558 my $vmlist = PVE
::Cluster
::get_vmlist
();
2560 return $res if !$vmlist || !$vmlist->{ids
};
2561 my $ids = $vmlist->{ids
};
2562 my $nodename = nodename
();
2564 foreach my $vmid (keys %$ids) {
2565 my $d = $ids->{$vmid};
2566 next if !$d->{node
} || $d->{node
} ne $nodename;
2567 next if !$d->{type
} || $d->{type
} ne 'qemu';
2568 $res->{$vmid}->{exists} = 1;
2573 # test if VM uses local resources (to prevent migration)
2574 sub check_local_resources
{
2575 my ($conf, $noerr) = @_;
2578 my $mapped_res = [];
2580 my $nodelist = PVE
::Cluster
::get_nodelist
();
2581 my $pci_map = PVE
::Mapping
::PCI
::config
();
2582 my $usb_map = PVE
::Mapping
::USB
::config
();
2584 my $missing_mappings_by_node = { map { $_ => [] } @$nodelist };
2586 my $add_missing_mapping = sub {
2587 my ($type, $key, $id) = @_;
2588 for my $node (@$nodelist) {
2590 if ($type eq 'pci') {
2591 $entry = PVE
::Mapping
::PCI
::get_node_mapping
($pci_map, $id, $node);
2592 } elsif ($type eq 'usb') {
2593 $entry = PVE
::Mapping
::USB
::get_node_mapping
($usb_map, $id, $node);
2595 if (!scalar($entry->@*)) {
2596 push @{$missing_mappings_by_node->{$node}}, $key;
2601 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2602 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2604 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2606 foreach my $k (keys %$conf) {
2607 if ($k =~ m/^usb/) {
2608 my $entry = parse_property_string
('pve-qm-usb', $conf->{$k});
2609 next if $entry->{host
} =~ m/^spice$/i;
2610 if ($entry->{mapping
}) {
2611 $add_missing_mapping->('usb', $k, $entry->{mapping
});
2612 push @$mapped_res, $k;
2615 if ($k =~ m/^hostpci/) {
2616 my $entry = parse_property_string
('pve-qm-hostpci', $conf->{$k});
2617 if ($entry->{mapping
}) {
2618 $add_missing_mapping->('pci', $k, $entry->{mapping
});
2619 push @$mapped_res, $k;
2622 # sockets are safe: they will recreated be on the target side post-migrate
2623 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2624 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2627 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2629 return wantarray ?
(\
@loc_res, $mapped_res, $missing_mappings_by_node) : \
@loc_res;
2632 # check if used storages are available on all nodes (use by migrate)
2633 sub check_storage_availability
{
2634 my ($storecfg, $conf, $node) = @_;
2636 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2637 my ($ds, $drive) = @_;
2639 my $volid = $drive->{file
};
2642 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2645 # check if storage is available on both nodes
2646 my $scfg = PVE
::Storage
::storage_check_enabled
($storecfg, $sid);
2647 PVE
::Storage
::storage_check_enabled
($storecfg, $sid, $node);
2649 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $volid);
2651 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2652 if !$scfg->{content
}->{$vtype};
2656 # list nodes where all VM images are available (used by has_feature API)
2658 my ($conf, $storecfg) = @_;
2660 my $nodelist = PVE
::Cluster
::get_nodelist
();
2661 my $nodehash = { map { $_ => 1 } @$nodelist };
2662 my $nodename = nodename
();
2664 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2665 my ($ds, $drive) = @_;
2667 my $volid = $drive->{file
};
2670 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2672 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2673 if ($scfg->{disable
}) {
2675 } elsif (my $avail = $scfg->{nodes
}) {
2676 foreach my $node (keys %$nodehash) {
2677 delete $nodehash->{$node} if !$avail->{$node};
2679 } elsif (!$scfg->{shared
}) {
2680 foreach my $node (keys %$nodehash) {
2681 delete $nodehash->{$node} if $node ne $nodename
2690 sub check_local_storage_availability
{
2691 my ($conf, $storecfg) = @_;
2693 my $nodelist = PVE
::Cluster
::get_nodelist
();
2694 my $nodehash = { map { $_ => {} } @$nodelist };
2696 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2697 my ($ds, $drive) = @_;
2699 my $volid = $drive->{file
};
2702 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2704 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2706 if ($scfg->{disable
}) {
2707 foreach my $node (keys %$nodehash) {
2708 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2710 } elsif (my $avail = $scfg->{nodes
}) {
2711 foreach my $node (keys %$nodehash) {
2712 if (!$avail->{$node}) {
2713 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2720 foreach my $node (values %$nodehash) {
2721 if (my $unavail = $node->{unavailable_storages
}) {
2722 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2729 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2731 my ($vmid, $nocheck, $node) = @_;
2733 # $nocheck is set when called during a migration, in which case the config
2734 # file might still or already reside on the *other* node
2735 # - because rename has already happened, and current node is source
2736 # - because rename hasn't happened yet, and current node is target
2737 # - because rename has happened, current node is target, but hasn't yet
2739 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2740 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2745 my $vzlist = config_list
();
2747 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2749 while (defined(my $de = $fd->read)) {
2750 next if $de !~ m/^(\d+)\.pid$/;
2752 next if !defined($vzlist->{$vmid});
2753 if (my $pid = check_running
($vmid)) {
2754 $vzlist->{$vmid}->{pid
} = $pid;
2761 our $vmstatus_return_properties = {
2762 vmid
=> get_standard_option
('pve-vmid'),
2764 description
=> "QEMU process status.",
2766 enum
=> ['stopped', 'running'],
2769 description
=> "Maximum memory in bytes.",
2772 renderer
=> 'bytes',
2775 description
=> "Root disk size in bytes.",
2778 renderer
=> 'bytes',
2781 description
=> "VM name.",
2786 description
=> "VM run state from the 'query-status' QMP monitor command.",
2791 description
=> "PID of running qemu process.",
2796 description
=> "Uptime.",
2799 renderer
=> 'duration',
2802 description
=> "Maximum usable CPUs.",
2807 description
=> "The current config lock, if any.",
2812 description
=> "The current configured tags, if any",
2816 'running-machine' => {
2817 description
=> "The currently running machine type (if running).",
2822 description
=> "The currently running QEMU version (if running).",
2828 my $last_proc_pid_stat;
2830 # get VM status information
2831 # This must be fast and should not block ($full == false)
2832 # We only query KVM using QMP if $full == true (this can be slow)
2834 my ($opt_vmid, $full) = @_;
2838 my $storecfg = PVE
::Storage
::config
();
2840 my $list = vzlist
();
2841 my $defaults = load_defaults
();
2843 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
(1);
2845 my $cpucount = $cpuinfo->{cpus
} || 1;
2847 foreach my $vmid (keys %$list) {
2848 next if $opt_vmid && ($vmid ne $opt_vmid);
2850 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2852 my $d = { vmid
=> int($vmid) };
2853 $d->{pid
} = int($list->{$vmid}->{pid
}) if $list->{$vmid}->{pid
};
2855 # fixme: better status?
2856 $d->{status
} = $list->{$vmid}->{pid
} ?
'running' : 'stopped';
2858 my $size = PVE
::QemuServer
::Drive
::bootdisk_size
($storecfg, $conf);
2859 if (defined($size)) {
2860 $d->{disk
} = 0; # no info available
2861 $d->{maxdisk
} = $size;
2867 $d->{cpus
} = ($conf->{sockets
} || $defaults->{sockets
})
2868 * ($conf->{cores
} || $defaults->{cores
});
2869 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
2870 $d->{cpus
} = $conf->{vcpus
} if $conf->{vcpus
};
2872 $d->{name
} = $conf->{name
} || "VM $vmid";
2873 $d->{maxmem
} = get_current_memory
($conf->{memory
})*(1024*1024);
2875 if ($conf->{balloon
}) {
2876 $d->{balloon_min
} = $conf->{balloon
}*(1024*1024);
2877 $d->{shares
} = defined($conf->{shares
}) ?
$conf->{shares
}
2878 : $defaults->{shares
};
2889 $d->{diskwrite
} = 0;
2891 $d->{template
} = 1 if PVE
::QemuConfig-
>is_template($conf);
2893 $d->{serial
} = 1 if conf_has_serial
($conf);
2894 $d->{lock} = $conf->{lock} if $conf->{lock};
2895 $d->{tags
} = $conf->{tags
} if defined($conf->{tags
});
2900 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
2901 foreach my $dev (keys %$netdev) {
2902 next if $dev !~ m/^tap([1-9]\d*)i/;
2904 my $d = $res->{$vmid};
2907 $d->{netout
} += $netdev->{$dev}->{receive
};
2908 $d->{netin
} += $netdev->{$dev}->{transmit
};
2911 $d->{nics
}->{$dev}->{netout
} = int($netdev->{$dev}->{receive
});
2912 $d->{nics
}->{$dev}->{netin
} = int($netdev->{$dev}->{transmit
});
2917 my $ctime = gettimeofday
;
2919 foreach my $vmid (keys %$list) {
2921 my $d = $res->{$vmid};
2922 my $pid = $d->{pid
};
2925 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
2926 next if !$pstat; # not running
2928 my $used = $pstat->{utime} + $pstat->{stime
};
2930 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
2932 if ($pstat->{vsize
}) {
2933 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
2936 my $old = $last_proc_pid_stat->{$pid};
2938 $last_proc_pid_stat->{$pid} = {
2946 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
2948 if ($dtime > 1000) {
2949 my $dutime = $used - $old->{used
};
2951 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
2952 $last_proc_pid_stat->{$pid} = {
2958 $d->{cpu
} = $old->{cpu
};
2962 return $res if !$full;
2964 my $qmpclient = PVE
::QMPClient-
>new();
2966 my $ballooncb = sub {
2967 my ($vmid, $resp) = @_;
2969 my $info = $resp->{'return'};
2970 return if !$info->{max_mem
};
2972 my $d = $res->{$vmid};
2974 # use memory assigned to VM
2975 $d->{maxmem
} = $info->{max_mem
};
2976 $d->{balloon
} = $info->{actual
};
2978 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
2979 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
2980 $d->{freemem
} = $info->{free_mem
};
2983 $d->{ballooninfo
} = $info;
2986 my $blockstatscb = sub {
2987 my ($vmid, $resp) = @_;
2988 my $data = $resp->{'return'} || [];
2989 my $totalrdbytes = 0;
2990 my $totalwrbytes = 0;
2992 for my $blockstat (@$data) {
2993 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
2994 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
2996 $blockstat->{device
} =~ s/drive-//;
2997 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
2999 $res->{$vmid}->{diskread
} = $totalrdbytes;
3000 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
3003 my $machinecb = sub {
3004 my ($vmid, $resp) = @_;
3005 my $data = $resp->{'return'} || [];
3007 $res->{$vmid}->{'running-machine'} =
3008 PVE
::QemuServer
::Machine
::current_from_query_machines
($data);
3011 my $versioncb = sub {
3012 my ($vmid, $resp) = @_;
3013 my $data = $resp->{'return'} // {};
3014 my $version = 'unknown';
3016 if (my $v = $data->{qemu
}) {
3017 $version = $v->{major
} . "." . $v->{minor
} . "." . $v->{micro
};
3020 $res->{$vmid}->{'running-qemu'} = $version;
3023 my $statuscb = sub {
3024 my ($vmid, $resp) = @_;
3026 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
3027 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
3028 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
3029 # this fails if ballon driver is not loaded, so this must be
3030 # the last commnand (following command are aborted if this fails).
3031 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
3033 my $status = 'unknown';
3034 if (!defined($status = $resp->{'return'}->{status
})) {
3035 warn "unable to get VM status\n";
3039 $res->{$vmid}->{qmpstatus
} = $resp->{'return'}->{status
};
3042 foreach my $vmid (keys %$list) {
3043 next if $opt_vmid && ($vmid ne $opt_vmid);
3044 next if !$res->{$vmid}->{pid
}; # not running
3045 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
3048 $qmpclient->queue_execute(undef, 2);
3050 foreach my $vmid (keys %$list) {
3051 next if $opt_vmid && ($vmid ne $opt_vmid);
3052 next if !$res->{$vmid}->{pid
}; #not running
3054 # we can't use the $qmpclient since it might have already aborted on
3055 # 'query-balloon', but this might also fail for older versions...
3056 my $qemu_support = eval { mon_cmd
($vmid, "query-proxmox-support") };
3057 $res->{$vmid}->{'proxmox-support'} = $qemu_support // {};
3060 foreach my $vmid (keys %$list) {
3061 next if $opt_vmid && ($vmid ne $opt_vmid);
3062 $res->{$vmid}->{qmpstatus
} = $res->{$vmid}->{status
} if !$res->{$vmid}->{qmpstatus
};
3068 sub conf_has_serial
{
3071 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3072 if ($conf->{"serial$i"}) {
3080 sub conf_has_audio
{
3081 my ($conf, $id) = @_;
3084 my $audio = $conf->{"audio$id"};
3085 return if !defined($audio);
3087 my $audioproperties = parse_property_string
($audio_fmt, $audio);
3088 my $audiodriver = $audioproperties->{driver
} // 'spice';
3091 dev
=> $audioproperties->{device
},
3092 dev_id
=> "audiodev$id",
3093 backend
=> $audiodriver,
3094 backend_id
=> "$audiodriver-backend${id}",
3099 my ($audio, $audiopciaddr, $machine_version) = @_;
3103 my $id = $audio->{dev_id
};
3105 if (min_version
($machine_version, 4, 2)) {
3106 $audiodev = ",audiodev=$audio->{backend_id}";
3109 if ($audio->{dev
} eq 'AC97') {
3110 push @$devs, '-device', "AC97,id=${id}${audiopciaddr}$audiodev";
3111 } elsif ($audio->{dev
} =~ /intel\-hda$/) {
3112 push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
3113 push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0$audiodev";
3114 push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1$audiodev";
3116 die "unkown audio device '$audio->{dev}', implement me!";
3119 push @$devs, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
3127 socket => "/var/run/qemu-server/$vmid.swtpm",
3128 pid
=> "/var/run/qemu-server/$vmid.swtpm.pid",
3132 sub add_tpm_device
{
3133 my ($vmid, $devices, $conf) = @_;
3135 return if !$conf->{tpmstate0
};
3137 my $paths = get_tpm_paths
($vmid);
3139 push @$devices, "-chardev", "socket,id=tpmchar,path=$paths->{socket}";
3140 push @$devices, "-tpmdev", "emulator,id=tpmdev,chardev=tpmchar";
3141 push @$devices, "-device", "tpm-tis,tpmdev=tpmdev";
3145 my ($storecfg, $vmid, $tpmdrive, $migration) = @_;
3147 return if !$tpmdrive;
3150 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
3151 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
3153 $state = PVE
::Storage
::map_volume
($storecfg, $tpm->{file
});
3155 $state = $tpm->{file
};
3158 my $paths = get_tpm_paths
($vmid);
3160 # during migration, we will get state from remote
3163 # run swtpm_setup to create a new TPM state if it doesn't exist yet
3170 "--create-platform-cert",
3173 "/etc/swtpm_setup.conf", # do not use XDG configs
3175 "0", # force creation as root, error if not possible
3176 "--not-overwrite", # ignore existing state, do not modify
3179 push @$setup_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3180 # TPM 2.0 supports ECC crypto, use if possible
3181 push @$setup_cmd, "--ecc" if $tpm->{version
} eq 'v2.0';
3183 run_command
($setup_cmd, outfunc
=> sub {
3184 print "swtpm_setup: $1\n";
3188 # Used to distinguish different invocations in the log.
3189 my $log_prefix = "[id=" . int(time()) . "] ";
3191 my $emulator_cmd = [
3195 "backend-uri=file://$state,mode=0600",
3197 "type=unixio,path=$paths->{socket},mode=0600",
3199 "file=$paths->{pid}",
3200 "--terminate", # terminate on QEMU disconnect
3203 "file=/run/qemu-server/$vmid-swtpm.log,level=1,prefix=$log_prefix",
3205 push @$emulator_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3206 run_command
($emulator_cmd, outfunc
=> sub { print $1; });
3208 my $tries = 100; # swtpm may take a bit to start before daemonizing, wait up to 5s for pid
3209 while (! -e
$paths->{pid
}) {
3210 die "failed to start swtpm: pid file '$paths->{pid}' wasn't created.\n" if --$tries == 0;
3214 # return untainted PID of swtpm daemon so it can be killed on error
3215 file_read_firstline
($paths->{pid
}) =~ m/(\d+)/;
3219 sub vga_conf_has_spice
{
3222 my $vgaconf = parse_vga
($vga);
3223 my $vgatype = $vgaconf->{type
};
3224 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
3231 return get_host_arch
() eq $arch;
3236 return $conf->{arch
} // get_host_arch
();
3239 my $default_machines = {
3244 sub get_installed_machine_version
{
3245 my ($kvmversion) = @_;
3246 $kvmversion = kvm_user_version
() if !defined($kvmversion);
3247 $kvmversion =~ m/^(\d+\.\d+)/;
3251 sub windows_get_pinned_machine_version
{
3252 my ($machine, $base_version, $kvmversion) = @_;
3254 my $pin_version = $base_version;
3255 if (!defined($base_version) ||
3256 !PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($base_version, $kvmversion)
3258 $pin_version = get_installed_machine_version
($kvmversion);
3260 if (!$machine || $machine eq 'pc') {
3261 $machine = "pc-i440fx-$pin_version";
3262 } elsif ($machine eq 'q35') {
3263 $machine = "pc-q35-$pin_version";
3264 } elsif ($machine eq 'virt') {
3265 $machine = "virt-$pin_version";
3267 warn "unknown machine type '$machine', not touching that!\n";
3273 sub get_vm_machine
{
3274 my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
3276 my $machine = $forcemachine || $conf->{machine
};
3278 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
3279 $kvmversion //= kvm_user_version
();
3280 # we must pin Windows VMs without a specific version to 5.1, as 5.2 fixed a bug in ACPI
3281 # layout which confuses windows quite a bit and may result in various regressions..
3282 # see: https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg08484.html
3283 if (windows_version
($conf->{ostype
})) {
3284 $machine = windows_get_pinned_machine_version
($machine, '5.1', $kvmversion);
3287 $machine ||= $default_machines->{$arch};
3288 if ($add_pve_version) {
3289 my $pvever = PVE
::QemuServer
::Machine
::get_pve_version
($kvmversion);
3290 $machine .= "+pve$pvever";
3294 if ($add_pve_version && $machine !~ m/\+pve\d+?(?:\.pxe)?$/) {
3295 my $is_pxe = $machine =~ m/^(.*?)\.pxe$/;
3296 $machine = $1 if $is_pxe;
3298 # for version-pinned machines that do not include a pve-version (e.g.
3299 # pc-q35-4.1), we assume 0 to keep them stable in case we bump
3300 $machine .= '+pve0';
3302 $machine .= '.pxe' if $is_pxe;
3308 sub get_ovmf_files
($$$) {
3309 my ($arch, $efidisk, $smm) = @_;
3311 my $types = $OVMF->{$arch}
3312 or die "no OVMF images known for architecture '$arch'\n";
3314 my $type = 'default';
3315 if ($arch eq 'x86_64') {
3316 if (defined($efidisk->{efitype
}) && $efidisk->{efitype
} eq '4m') {
3317 $type = $smm ?
"4m" : "4m-no-smm";
3318 $type .= '-ms' if $efidisk->{'pre-enrolled-keys'};
3320 # TODO: log_warn about use of legacy images for x86_64 with Promxox VE 9
3324 my ($ovmf_code, $ovmf_vars) = $types->{$type}->@*;
3325 die "EFI base image '$ovmf_code' not found\n" if ! -f
$ovmf_code;
3326 die "EFI vars image '$ovmf_vars' not found\n" if ! -f
$ovmf_vars;
3328 return ($ovmf_code, $ovmf_vars);
3332 aarch64
=> '/usr/bin/qemu-system-aarch64',
3333 x86_64
=> '/usr/bin/qemu-system-x86_64',
3335 sub get_command_for_arch
($) {
3337 return '/usr/bin/kvm' if is_native
($arch);
3339 my $cmd = $Arch2Qemu->{$arch}
3340 or die "don't know how to emulate architecture '$arch'\n";
3344 # To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
3345 # to use in a QEMU command line (-cpu element), first array_intersect the result
3346 # of query_supported_ with query_understood_. This is necessary because:
3348 # a) query_understood_ returns flags the host cannot use and
3349 # b) query_supported_ (rather the QMP call) doesn't actually return CPU
3350 # flags, but CPU settings - with most of them being flags. Those settings
3351 # (and some flags, curiously) cannot be specified as a "-cpu" argument.
3353 # query_supported_ needs to start up to 2 temporary VMs and is therefore rather
3354 # expensive. If you need the value returned from this, you can get it much
3355 # cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags-$accel') with
3356 # $accel being 'kvm' or 'tcg'.
3358 # pvestatd calls this function on startup and whenever the QEMU/KVM version
3359 # changes, automatically populating pmxcfs.
3361 # Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
3362 # since kvm and tcg machines support different flags
3364 sub query_supported_cpu_flags
{
3367 $arch //= get_host_arch
();
3368 my $default_machine = $default_machines->{$arch};
3372 # FIXME: Once this is merged, the code below should work for ARM as well:
3373 # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
3374 die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
3377 my $kvm_supported = defined(kvm_version
());
3378 my $qemu_cmd = get_command_for_arch
($arch);
3380 my $pidfile = PVE
::QemuServer
::Helpers
::pidfile_name
($fakevmid);
3382 # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
3383 my $query_supported_run_qemu = sub {
3389 '-machine', $default_machine,
3391 '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
3392 '-mon', 'chardev=qmp,mode=control',
3393 '-pidfile', $pidfile,
3398 push @$cmd, '-accel', 'tcg';
3401 my $rc = run_command
($cmd, noerr
=> 1, quiet
=> 0);
3402 die "QEMU flag querying VM exited with code " . $rc if $rc;
3405 my $cmd_result = mon_cmd
(
3407 'query-cpu-model-expansion',
3409 model
=> { name
=> 'host' }
3412 my $props = $cmd_result->{model
}->{props
};
3413 foreach my $prop (keys %$props) {
3414 next if $props->{$prop} ne '1';
3415 # QEMU returns some flags multiple times, with '_', '.' or '-'
3416 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
3417 # We only keep those with underscores, to match /proc/cpuinfo
3418 $prop =~ s/\.|-/_/g;
3419 $flags->{$prop} = 1;
3424 # force stop with 10 sec timeout and 'nocheck', always stop, even if QMP failed
3425 vm_stop
(undef, $fakevmid, 1, 1, 10, 0, 1);
3429 return [ sort keys %$flags ];
3432 # We need to query QEMU twice, since KVM and TCG have different supported flags
3433 PVE
::QemuConfig-
>lock_config($fakevmid, sub {
3434 $flags->{tcg
} = eval { $query_supported_run_qemu->(0) };
3435 warn "warning: failed querying supported tcg flags: $@\n" if $@;
3437 if ($kvm_supported) {
3438 $flags->{kvm
} = eval { $query_supported_run_qemu->(1) };
3439 warn "warning: failed querying supported kvm flags: $@\n" if $@;
3446 # Understood CPU flags are written to a file at 'pve-qemu' compile time
3447 my $understood_cpu_flag_dir = "/usr/share/kvm";
3448 sub query_understood_cpu_flags
{
3449 my $arch = get_host_arch
();
3450 my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
3452 die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
3455 my $raw = file_get_contents
($filepath);
3456 $raw =~ s/^\s+|\s+$//g;
3457 my @flags = split(/\s+/, $raw);
3462 # Since commit 277d33454f77ec1d1e0bc04e37621e4dd2424b67 in pve-qemu, smm is not off by default
3463 # anymore. But smm=off seems to be required when using SeaBIOS and serial display.
3464 my sub should_disable_smm
{
3465 my ($conf, $vga, $machine) = @_;
3467 return if $machine =~ m/^virt/; # there is no smm flag that could be disabled
3469 return (!defined($conf->{bios
}) || $conf->{bios
} eq 'seabios') &&
3470 $vga->{type
} && $vga->{type
} =~ m/^(serial\d+|none)$/;
3473 my sub print_ovmf_drive_commandlines
{
3474 my ($conf, $storecfg, $vmid, $arch, $q35, $version_guard) = @_;
3476 my $d = $conf->{efidisk0
} ? parse_drive
('efidisk0', $conf->{efidisk0
}) : undef;
3478 my ($ovmf_code, $ovmf_vars) = get_ovmf_files
($arch, $d, $q35);
3480 my $var_drive_str = "if=pflash,unit=1,id=drive-efidisk0";
3482 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($d->{file
}, 1);
3483 my ($path, $format) = $d->@{'file', 'format'};
3485 $path = PVE
::Storage
::path
($storecfg, $d->{file
});
3486 if (!defined($format)) {
3487 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
3488 $format = qemu_img_format
($scfg, $volname);
3490 } elsif (!defined($format)) {
3491 die "efidisk format must be specified\n";
3493 # SPI flash does lots of read-modify-write OPs, without writeback this gets really slow #3329
3494 if ($path =~ m/^rbd:/) {
3495 $var_drive_str .= ',cache=writeback';
3496 $path .= ':rbd_cache_policy=writeback'; # avoid write-around, we *need* to cache writes too
3498 $var_drive_str .= ",format=$format,file=$path";
3500 $var_drive_str .= ",size=" . (-s
$ovmf_vars) if $format eq 'raw' && $version_guard->(4, 1, 2);
3501 $var_drive_str .= ',readonly=on' if drive_is_read_only
($conf, $d);
3503 log_warn
("no efidisk configured! Using temporary efivars disk.");
3504 my $path = "/tmp/$vmid-ovmf.fd";
3505 PVE
::Tools
::file_copy
($ovmf_vars, $path, -s
$ovmf_vars);
3506 $var_drive_str .= ",format=raw,file=$path";
3507 $var_drive_str .= ",size=" . (-s
$ovmf_vars) if $version_guard->(4, 1, 2);
3510 return ("if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code", $var_drive_str);
3513 sub config_to_command
{
3514 my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
3517 my ($globalFlags, $machineFlags, $rtcFlags) = ([], [], []);
3520 my $ostype = $conf->{ostype
};
3521 my $winversion = windows_version
($ostype);
3522 my $kvm = $conf->{kvm
};
3523 my $nodename = nodename
();
3525 my $arch = get_vm_arch
($conf);
3526 my $kvm_binary = get_command_for_arch
($arch);
3527 my $kvmver = kvm_user_version
($kvm_binary);
3529 if (!$kvmver || $kvmver !~ m/^(\d+)\.(\d+)/ || $1 < 3) {
3530 $kvmver //= "undefined";
3531 die "Detected old QEMU binary ('$kvmver', at least 3.0 is required)\n";
3534 my $add_pve_version = min_version
($kvmver, 4, 1);
3536 my $machine_type = get_vm_machine
($conf, $forcemachine, $arch, $add_pve_version);
3537 my $machine_version = extract_version
($machine_type, $kvmver);
3538 $kvm //= 1 if is_native
($arch);
3540 $machine_version =~ m/(\d+)\.(\d+)/;
3541 my ($machine_major, $machine_minor) = ($1, $2);
3543 if ($kvmver =~ m/^\d+\.\d+\.(\d+)/ && $1 >= 90) {
3544 warn "warning: Installed QEMU version ($kvmver) is a release candidate, ignoring version checks\n";
3545 } elsif (!min_version
($kvmver, $machine_major, $machine_minor)) {
3546 die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type',"
3547 ." please upgrade node '$nodename'\n"
3548 } elsif (!PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($machine_version, $kvmver)) {
3549 my $max_pve_version = PVE
::QemuServer
::Machine
::get_pve_version
($machine_version);
3550 die "Installed qemu-server (max feature level for $machine_major.$machine_minor is"
3551 ." pve$max_pve_version) is too old to run machine type '$machine_type', please upgrade"
3552 ." node '$nodename'\n";
3555 # if a specific +pve version is required for a feature, use $version_guard
3556 # instead of min_version to allow machines to be run with the minimum
3558 my $required_pve_version = 0;
3559 my $version_guard = sub {
3560 my ($major, $minor, $pve) = @_;
3561 return 0 if !min_version
($machine_version, $major, $minor, $pve);
3562 my $max_pve = PVE
::QemuServer
::Machine
::get_pve_version
("$major.$minor");
3563 return 1 if min_version
($machine_version, $major, $minor, $max_pve+1);
3564 $required_pve_version = $pve if $pve && $pve > $required_pve_version;
3568 if ($kvm && !defined kvm_version
()) {
3569 die "KVM virtualisation configured, but not available. Either disable in VM configuration"
3570 ." or enable in BIOS.\n";
3573 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3574 my $hotplug_features = parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
3575 my $use_old_bios_files = undef;
3576 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3579 if ($conf->{affinity
}) {
3580 push @$cmd, '/usr/bin/taskset', '--cpu-list', '--all-tasks', $conf->{affinity
};
3583 push @$cmd, $kvm_binary;
3585 push @$cmd, '-id', $vmid;
3587 my $vmname = $conf->{name
} || "vm$vmid";
3589 push @$cmd, '-name', "$vmname,debug-threads=on";
3591 push @$cmd, '-no-shutdown';
3595 my $qmpsocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid);
3596 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server=on,wait=off";
3597 push @$cmd, '-mon', "chardev=qmp,mode=control";
3599 if (min_version
($machine_version, 2, 12)) {
3600 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3601 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3604 push @$cmd, '-pidfile' , PVE
::QemuServer
::Helpers
::pidfile_name
($vmid);
3606 push @$cmd, '-daemonize';
3608 if ($conf->{smbios1
}) {
3609 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
3610 if ($smbios_conf->{base64
}) {
3611 # Do not pass base64 flag to qemu
3612 delete $smbios_conf->{base64
};
3613 my $smbios_string = "";
3614 foreach my $key (keys %$smbios_conf) {
3616 if ($key eq "uuid") {
3617 $value = $smbios_conf->{uuid
}
3619 $value = decode_base64
($smbios_conf->{$key});
3621 # qemu accepts any binary data, only commas need escaping by double comma
3623 $smbios_string .= "," . $key . "=" . $value if $value;
3625 push @$cmd, '-smbios', "type=1" . $smbios_string;
3627 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3631 if ($conf->{bios
} && $conf->{bios
} eq 'ovmf') {
3632 my ($code_drive_str, $var_drive_str) =
3633 print_ovmf_drive_commandlines
($conf, $storecfg, $vmid, $arch, $q35, $version_guard);
3634 push $cmd->@*, '-drive', $code_drive_str;
3635 push $cmd->@*, '-drive', $var_drive_str;
3638 if ($q35) { # tell QEMU to load q35 config early
3639 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3640 if (min_version
($machine_version, 4, 0)) {
3641 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3643 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3647 if (defined(my $fixups = qemu_created_version_fixups
($conf, $forcemachine, $kvmver))) {
3648 push @$cmd, $fixups->@*;
3651 if ($conf->{vmgenid
}) {
3652 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid
};
3655 # add usb controllers
3656 my @usbcontrollers = PVE
::QemuServer
::USB
::get_usb_controllers
(
3657 $conf, $bridges, $arch, $machine_type, $machine_version);
3658 push @$devices, @usbcontrollers if @usbcontrollers;
3659 my $vga = parse_vga
($conf->{vga
});
3661 my $qxlnum = vga_conf_has_spice
($conf->{vga
});
3662 $vga->{type
} = 'qxl' if $qxlnum;
3664 if (!$vga->{type
}) {
3665 if ($arch eq 'aarch64') {
3666 $vga->{type
} = 'virtio';
3667 } elsif (min_version
($machine_version, 2, 9)) {
3668 $vga->{type
} = (!$winversion || $winversion >= 6) ?
'std' : 'cirrus';
3670 $vga->{type
} = ($winversion >= 6) ?
'std' : 'cirrus';
3674 # enable absolute mouse coordinates (needed by vnc)
3675 my $tablet = $conf->{tablet
};
3676 if (!defined($tablet)) {
3677 $tablet = $defaults->{tablet
};
3678 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3679 $tablet = 0 if $vga->{type
} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3683 push @$devices, '-device', print_tabletdevice_full
($conf, $arch) if $tablet;
3684 my $kbd = print_keyboarddevice_full
($conf, $arch);
3685 push @$devices, '-device', $kbd if defined($kbd);
3688 my $bootorder = device_bootorder
($conf);
3690 # host pci device passthrough
3691 my ($kvm_off, $gpu_passthrough, $legacy_igd, $pci_devices) = PVE
::QemuServer
::PCI
::print_hostpci_devices
(
3692 $vmid, $conf, $devices, $vga, $winversion, $bridges, $arch, $machine_type, $bootorder);
3695 my $usb_dev_features = {};
3696 $usb_dev_features->{spice_usb3
} = 1 if min_version
($machine_version, 4, 0);
3698 my @usbdevices = PVE
::QemuServer
::USB
::get_usb_devices
(
3699 $conf, $usb_dev_features, $bootorder, $machine_version);
3700 push @$devices, @usbdevices if @usbdevices;
3703 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3704 my $path = $conf->{"serial$i"} or next;
3705 if ($path eq 'socket') {
3706 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3707 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
3708 # On aarch64, serial0 is the UART device. QEMU only allows
3709 # connecting UART devices via the '-serial' command line, as
3710 # the device has a fixed slot on the hardware...
3711 if ($arch eq 'aarch64' && $i == 0) {
3712 push @$devices, '-serial', "chardev:serial$i";
3714 push @$devices, '-device', "isa-serial,chardev=serial$i";
3717 die "no such serial device\n" if ! -c
$path;
3718 push @$devices, '-chardev', "serial,id=serial$i,path=$path";
3719 push @$devices, '-device', "isa-serial,chardev=serial$i";
3724 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3725 if (my $path = $conf->{"parallel$i"}) {
3726 die "no such parallel device\n" if ! -c
$path;
3727 my $devtype = $path =~ m!^/dev/usb/lp! ?
'serial' : 'parallel';
3728 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3729 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3733 if (min_version
($machine_version, 4, 0) && (my $audio = conf_has_audio
($conf))) {
3734 my $audiopciaddr = print_pci_addr
("audio0", $bridges, $arch, $machine_type);
3735 my $audio_devs = audio_devs
($audio, $audiopciaddr, $machine_version);
3736 push @$devices, @$audio_devs;
3739 # Add a TPM only if the VM is not a template,
3740 # to support backing up template VMs even if the TPM disk is write-protected.
3741 add_tpm_device
($vmid, $devices, $conf) if (!PVE
::QemuConfig-
>is_template($conf));
3744 $sockets = $conf->{smp
} if $conf->{smp
}; # old style - no longer iused
3745 $sockets = $conf->{sockets
} if $conf->{sockets
};
3747 my $cores = $conf->{cores
} || 1;
3749 my $maxcpus = $sockets * $cores;
3751 my $vcpus = $conf->{vcpus
} ?
$conf->{vcpus
} : $maxcpus;
3753 my $allowed_vcpus = $cpuinfo->{cpus
};
3755 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n" if ($allowed_vcpus < $maxcpus);
3757 if ($hotplug_features->{cpu
} && min_version
($machine_version, 2, 7)) {
3758 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3759 for (my $i = 2; $i <= $vcpus; $i++) {
3760 my $cpustr = print_cpu_device
($conf,$i);
3761 push @$cmd, '-device', $cpustr;
3766 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3768 push @$cmd, '-nodefaults';
3770 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3772 push $machineFlags->@*, 'acpi=off' if defined($conf->{acpi
}) && $conf->{acpi
} == 0;
3774 push @$cmd, '-no-reboot' if defined($conf->{reboot
}) && $conf->{reboot
} == 0;
3776 if ($vga->{type
} && $vga->{type
} !~ m/^serial\d+$/ && $vga->{type
} ne 'none'){
3777 push @$devices, '-device', print_vga_device
(
3778 $conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
3780 push @$cmd, '-display', 'egl-headless,gl=core' if $vga->{type
} eq 'virtio-gl'; # VIRGL
3782 my $socket = PVE
::QemuServer
::Helpers
::vnc_socket
($vmid);
3783 push @$cmd, '-vnc', "unix:$socket,password=on";
3785 push @$cmd, '-vga', 'none' if $vga->{type
} eq 'none';
3786 push @$cmd, '-nographic';
3790 my $tdf = defined($conf->{tdf
}) ?
$conf->{tdf
} : $defaults->{tdf
};
3791 my $useLocaltime = $conf->{localtime};
3793 if ($winversion >= 5) { # windows
3794 $useLocaltime = 1 if !defined($conf->{localtime});
3796 # use time drift fix when acpi is enabled
3797 if (!(defined($conf->{acpi
}) && $conf->{acpi
} == 0)) {
3798 $tdf = 1 if !defined($conf->{tdf
});
3802 if ($winversion >= 6) {
3803 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3804 push @$machineFlags, 'hpet=off';
3807 push @$rtcFlags, 'driftfix=slew' if $tdf;
3809 if ($conf->{startdate
} && $conf->{startdate
} ne 'now') {
3810 push @$rtcFlags, "base=$conf->{startdate}";
3811 } elsif ($useLocaltime) {
3812 push @$rtcFlags, 'base=localtime';
3816 push @$cmd, '-cpu', $forcecpu;
3818 push @$cmd, get_cpu_options
($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
3821 PVE
::QemuServer
::Memory
::config
(
3822 $conf, $vmid, $sockets, $cores, $hotplug_features->{memory
}, $cmd);
3824 push @$cmd, '-S' if $conf->{freeze
};
3826 push @$cmd, '-k', $conf->{keyboard
} if defined($conf->{keyboard
});
3828 my $guest_agent = parse_guest_agent
($conf);
3830 if ($guest_agent->{enabled
}) {
3831 my $qgasocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid, 1);
3832 push @$devices, '-chardev', "socket,path=$qgasocket,server=on,wait=off,id=qga0";
3834 if (!$guest_agent->{type
} || $guest_agent->{type
} eq 'virtio') {
3835 my $pciaddr = print_pci_addr
("qga0", $bridges, $arch, $machine_type);
3836 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3837 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3838 } elsif ($guest_agent->{type
} eq 'isa') {
3839 push @$devices, '-device', "isa-serial,chardev=qga0";
3843 my $rng = $conf->{rng0
} ? parse_rng
($conf->{rng0
}) : undef;
3844 if ($rng && $version_guard->(4, 1, 2)) {
3845 check_rng_source
($rng->{source
});
3847 my $max_bytes = $rng->{max_bytes
} // $rng_fmt->{max_bytes
}->{default};
3848 my $period = $rng->{period
} // $rng_fmt->{period
}->{default};
3849 my $limiter_str = "";
3851 $limiter_str = ",max-bytes=$max_bytes,period=$period";
3854 my $rng_addr = print_pci_addr
("rng0", $bridges, $arch, $machine_type);
3855 push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
3856 push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
3861 assert_clipboard_config
($vga);
3862 my $is_spice = $qxlnum || $vga->{type
} =~ /^virtio/;
3864 if ($is_spice || ($vga->{'clipboard'} && $vga->{'clipboard'} eq 'vnc')) {
3867 for (my $i = 1; $i < $qxlnum; $i++){
3868 push @$devices, '-device', print_vga_device
(
3869 $conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
3872 # assume other OS works like Linux
3873 my ($ram, $vram) = ("134217728", "67108864");
3874 if ($vga->{memory
}) {
3875 $ram = PVE
::Tools
::convert_size
($qxlnum*4*$vga->{memory
}, 'mb' => 'b');
3876 $vram = PVE
::Tools
::convert_size
($qxlnum*2*$vga->{memory
}, 'mb' => 'b');
3878 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3879 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3883 my $pciaddr = print_pci_addr
("spice", $bridges, $arch, $machine_type);
3885 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3886 if ($vga->{'clipboard'} && $vga->{'clipboard'} eq 'vnc') {
3887 push @$devices, '-chardev', 'qemu-vdagent,id=vdagent,name=vdagent,clipboard=on';
3889 push @$devices, '-chardev', 'spicevmc,id=vdagent,name=vdagent';
3891 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3894 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
3895 my @nodeaddrs = PVE
::Tools
::getaddrinfo_all
('localhost', family
=> $pfamily);
3896 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3898 my $localhost = PVE
::Network
::addr_to_ip
($nodeaddrs[0]->{addr
});
3899 $spice_port = PVE
::Tools
::next_spice_port
($pfamily, $localhost);
3901 my $spice_enhancement_str = $conf->{spice_enhancements
} // '';
3902 my $spice_enhancement = parse_property_string
($spice_enhancements_fmt, $spice_enhancement_str);
3903 if ($spice_enhancement->{foldersharing
}) {
3904 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3905 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3908 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3909 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}"
3910 if $spice_enhancement->{videostreaming
};
3911 push @$devices, '-spice', "$spice_opts";
3915 # enable balloon by default, unless explicitly disabled
3916 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
3917 my $pciaddr = print_pci_addr
("balloon0", $bridges, $arch, $machine_type);
3918 my $ballooncmd = "virtio-balloon-pci,id=balloon0$pciaddr";
3919 $ballooncmd .= ",free-page-reporting=on" if min_version
($machine_version, 6, 2);
3920 push @$devices, '-device', $ballooncmd;
3923 if ($conf->{watchdog
}) {
3924 my $wdopts = parse_watchdog
($conf->{watchdog
});
3925 my $pciaddr = print_pci_addr
("watchdog", $bridges, $arch, $machine_type);
3926 my $watchdog = $wdopts->{model
} || 'i6300esb';
3927 push @$devices, '-device', "$watchdog$pciaddr";
3928 push @$devices, '-watchdog-action', $wdopts->{action
} if $wdopts->{action
};
3932 my $scsicontroller = {};
3933 my $ahcicontroller = {};
3934 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : $defaults->{scsihw
};
3936 # Add iscsi initiator name if available
3937 if (my $initiator = get_initiator_name
()) {
3938 push @$devices, '-iscsi', "initiator-name=$initiator";
3941 PVE
::QemuConfig-
>foreach_volume($conf, sub {
3942 my ($ds, $drive) = @_;
3944 if (PVE
::Storage
::parse_volume_id
($drive->{file
}, 1)) {
3945 check_volume_storage_type
($storecfg, $drive->{file
});
3946 push @$vollist, $drive->{file
};
3949 # ignore efidisk here, already added in bios/fw handling code above
3950 return if $drive->{interface
} eq 'efidisk';
3952 return if $drive->{interface
} eq 'tpmstate';
3954 $use_virtio = 1 if $ds =~ m/^virtio/;
3956 $drive->{bootindex
} = $bootorder->{$ds} if $bootorder->{$ds};
3958 if ($drive->{interface
} eq 'virtio'){
3959 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread
};
3962 if ($drive->{interface
} eq 'scsi') {
3964 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
3966 die "scsi$drive->{index}: machine version 4.1~pve2 or higher is required to use more than 14 SCSI disks\n"
3967 if $drive->{index} > 13 && !&$version_guard(4, 1, 2);
3969 my $pciaddr = print_pci_addr
("$controller_prefix$controller", $bridges, $arch, $machine_type);
3970 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ?
"virtio-scsi-pci" : $scsihw;
3973 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{iothread
}){
3974 $iothread .= ",iothread=iothread-$controller_prefix$controller";
3975 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
3976 } elsif ($drive->{iothread
}) {
3978 "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n"
3983 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{queues
}){
3984 $queues = ",num_queues=$drive->{queues}";
3987 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues"
3988 if !$scsicontroller->{$controller};
3989 $scsicontroller->{$controller}=1;
3992 if ($drive->{interface
} eq 'sata') {
3993 my $controller = int($drive->{index} / $PVE::QemuServer
::Drive
::MAX_SATA_DISKS
);
3994 my $pciaddr = print_pci_addr
("ahci$controller", $bridges, $arch, $machine_type);
3995 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr"
3996 if !$ahcicontroller->{$controller};
3997 $ahcicontroller->{$controller}=1;
4000 my $pbs_conf = $pbs_backing->{$ds};
4001 my $pbs_name = undef;
4003 $pbs_name = "drive-$ds-pbs";
4004 push @$devices, '-blockdev', print_pbs_blockdev
($pbs_conf, $pbs_name);
4007 my $drive_cmd = print_drive_commandline_full
(
4008 $storecfg, $vmid, $drive, $pbs_name, min_version
($kvmver, 6, 0));
4010 # extra protection for templates, but SATA and IDE don't support it..
4011 $drive_cmd .= ',readonly=on' if drive_is_read_only
($conf, $drive);
4013 push @$devices, '-drive',$drive_cmd;
4014 push @$devices, '-device', print_drivedevice_full
(
4015 $storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
4018 for (my $i = 0; $i < $MAX_NETS; $i++) {
4019 my $netname = "net$i";
4021 next if !$conf->{$netname};
4022 my $d = parse_net
($conf->{$netname});
4024 # save the MAC addr here (could be auto-gen. in some odd setups) for FDB registering later?
4026 $use_virtio = 1 if $d->{model
} eq 'virtio';
4028 $d->{bootindex
} = $bootorder->{$netname} if $bootorder->{$netname};
4030 my $netdevfull = print_netdev_full
($vmid, $conf, $arch, $d, $netname);
4031 push @$devices, '-netdev', $netdevfull;
4033 my $netdevicefull = print_netdevice_full
(
4034 $vmid, $conf, $d, $netname, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version);
4036 push @$devices, '-device', $netdevicefull;
4039 if ($conf->{ivshmem
}) {
4040 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
4044 $bus = print_pcie_addr
("ivshmem");
4046 $bus = print_pci_addr
("ivshmem", $bridges, $arch, $machine_type);
4049 my $ivshmem_name = $ivshmem->{name
} // $vmid;
4050 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
4052 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
4053 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path"
4054 .",size=$ivshmem->{size}M";
4057 # pci.4 is nested in pci.1
4058 $bridges->{1} = 1 if $bridges->{4};
4060 if (!$q35) { # add pci bridges
4061 if (min_version
($machine_version, 2, 3)) {
4065 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
4068 for my $k (sort {$b cmp $a} keys %$bridges) {
4069 next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
4072 if ($k == 2 && $legacy_igd) {
4075 my $pciaddr = print_pci_addr
("pci.$k_name", undef, $arch, $machine_type);
4076 my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
4078 if ($q35) { # add after -readconfig pve-q35.cfg
4079 splice @$devices, 2, 0, '-device', $devstr;
4081 unshift @$devices, '-device', $devstr if $k > 0;
4086 push @$machineFlags, 'accel=tcg';
4089 push @$machineFlags, 'smm=off' if should_disable_smm
($conf, $vga, $machine_type);
4091 my $machine_type_min = $machine_type;
4092 if ($add_pve_version) {
4093 $machine_type_min =~ s/\+pve\d+$//;
4094 $machine_type_min .= "+pve$required_pve_version";
4096 push @$machineFlags, "type=${machine_type_min}";
4098 push @$cmd, @$devices;
4099 push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
4100 push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
4101 push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags);
4103 if (my $vmstate = $conf->{vmstate
}) {
4104 my $statepath = PVE
::Storage
::path
($storecfg, $vmstate);
4105 push @$vollist, $vmstate;
4106 push @$cmd, '-loadstate', $statepath;
4107 print "activating and using '$vmstate' as vmstate\n";
4110 if (PVE
::QemuConfig-
>is_template($conf)) {
4111 # needed to workaround base volumes being read-only
4112 push @$cmd, '-snapshot';
4116 if ($conf->{args
}) {
4117 my $aa = PVE
::Tools
::split_args
($conf->{args
});
4121 return wantarray ?
($cmd, $vollist, $spice_port, $pci_devices) : $cmd;
4124 sub check_rng_source
{
4127 # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
4128 die "cannot create VirtIO RNG device: source file '$source' doesn't exist\n"
4131 my $rng_current = '/sys/devices/virtual/misc/hw_random/rng_current';
4132 if ($source eq '/dev/hwrng' && file_read_firstline
($rng_current) eq 'none') {
4133 # Needs to abort, otherwise QEMU crashes on first rng access. Note that rng_current cannot
4134 # be changed to 'none' manually, so once the VM is past this point, it's no longer an issue.
4135 die "Cannot start VM with passed-through RNG device: '/dev/hwrng' exists, but"
4136 ." '$rng_current' is set to 'none'. Ensure that a compatible hardware-RNG is attached"
4144 my $res = mon_cmd
($vmid, 'query-spice');
4146 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
4149 sub vm_devices_list
{
4152 my $res = mon_cmd
($vmid, 'query-pci');
4153 my $devices_to_check = [];
4155 foreach my $pcibus (@$res) {
4156 push @$devices_to_check, @{$pcibus->{devices
}},
4159 while (@$devices_to_check) {
4161 for my $d (@$devices_to_check) {
4162 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
4163 next if !$d->{'pci_bridge'} || !$d->{'pci_bridge'}->{devices
};
4165 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices
}});
4166 push @$to_check, @{$d->{'pci_bridge'}->{devices
}};
4168 $devices_to_check = $to_check;
4171 my $resblock = mon_cmd
($vmid, 'query-block');
4172 foreach my $block (@$resblock) {
4173 if($block->{device
} =~ m/^drive-(\S+)/){
4178 my $resmice = mon_cmd
($vmid, 'query-mice');
4179 foreach my $mice (@$resmice) {
4180 if ($mice->{name
} eq 'QEMU HID Tablet') {
4181 $devices->{tablet
} = 1;
4186 # for usb devices there is no query-usb
4187 # but we can iterate over the entries in
4188 # qom-list path=/machine/peripheral
4189 my $resperipheral = mon_cmd
($vmid, 'qom-list', path
=> '/machine/peripheral');
4190 foreach my $per (@$resperipheral) {
4191 if ($per->{name
} =~ m/^usb(?:redirdev)?\d+$/) {
4192 $devices->{$per->{name
}} = 1;
4200 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4202 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
4204 my $devices_list = vm_devices_list
($vmid);
4205 return 1 if defined($devices_list->{$deviceid});
4207 # add PCI bridge if we need it for the device
4208 qemu_add_pci_bridge
($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type);
4210 if ($deviceid eq 'tablet') {
4211 qemu_deviceadd
($vmid, print_tabletdevice_full
($conf, $arch));
4212 } elsif ($deviceid eq 'keyboard') {
4213 qemu_deviceadd
($vmid, print_keyboarddevice_full
($conf, $arch));
4214 } elsif ($deviceid =~ m/^usbredirdev(\d+)$/) {
4216 qemu_spice_usbredir_chardev_add
($vmid, "usbredirchardev$id");
4217 qemu_deviceadd
($vmid, PVE
::QemuServer
::USB
::print_spice_usbdevice
($id, "xhci", $id + 1));
4218 } elsif ($deviceid =~ m/^usb(\d+)$/) {
4219 qemu_deviceadd
($vmid, PVE
::QemuServer
::USB
::print_usbdevice_full
($conf, $deviceid, $device, {}, $1 + 1));
4220 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4221 qemu_iothread_add
($vmid, $deviceid, $device);
4223 qemu_driveadd
($storecfg, $vmid, $device);
4224 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4226 qemu_deviceadd
($vmid, $devicefull);
4227 eval { qemu_deviceaddverify
($vmid, $deviceid); };
4229 eval { qemu_drivedel
($vmid, $deviceid); };
4233 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4234 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : "lsi";
4235 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4236 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ?
"virtio-scsi-pci" : $scsihw;
4238 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
4240 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread
}) {
4241 qemu_iothread_add
($vmid, $deviceid, $device);
4242 $devicefull .= ",iothread=iothread-$deviceid";
4245 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues
}) {
4246 $devicefull .= ",num_queues=$device->{queues}";
4249 qemu_deviceadd
($vmid, $devicefull);
4250 qemu_deviceaddverify
($vmid, $deviceid);
4251 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4252 qemu_findorcreatescsihw
($storecfg,$conf, $vmid, $device, $arch, $machine_type);
4253 qemu_driveadd
($storecfg, $vmid, $device);
4255 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4256 eval { qemu_deviceadd
($vmid, $devicefull); };
4258 eval { qemu_drivedel
($vmid, $deviceid); };
4262 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4263 return if !qemu_netdevadd
($vmid, $conf, $arch, $device, $deviceid);
4265 my $machine_type = PVE
::QemuServer
::Machine
::qemu_machine_pxe
($vmid, $conf);
4266 my $machine_version = PVE
::QemuServer
::Machine
::extract_version
($machine_type);
4267 my $use_old_bios_files = undef;
4268 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
4270 my $netdevicefull = print_netdevice_full
(
4271 $vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type, $machine_version);
4272 qemu_deviceadd
($vmid, $netdevicefull);
4274 qemu_deviceaddverify
($vmid, $deviceid);
4275 qemu_set_link_status
($vmid, $deviceid, !$device->{link_down
});
4278 eval { qemu_netdevdel
($vmid, $deviceid); };
4282 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
4284 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4285 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
4287 qemu_deviceadd
($vmid, $devicefull);
4288 qemu_deviceaddverify
($vmid, $deviceid);
4290 die "can't hotplug device '$deviceid'\n";
4296 # fixme: this should raise exceptions on error!
4297 sub vm_deviceunplug
{
4298 my ($vmid, $conf, $deviceid) = @_;
4300 my $devices_list = vm_devices_list
($vmid);
4301 return 1 if !defined($devices_list->{$deviceid});
4303 my $bootdisks = PVE
::QemuServer
::Drive
::get_bootdisks
($conf);
4304 die "can't unplug bootdisk '$deviceid'\n" if grep {$_ eq $deviceid} @$bootdisks;
4306 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard' || $deviceid eq 'xhci') {
4307 qemu_devicedel
($vmid, $deviceid);
4308 } elsif ($deviceid =~ m/^usbredirdev\d+$/) {
4309 qemu_devicedel
($vmid, $deviceid);
4310 qemu_devicedelverify
($vmid, $deviceid);
4311 } elsif ($deviceid =~ m/^usb\d+$/) {
4312 qemu_devicedel
($vmid, $deviceid);
4313 qemu_devicedelverify
($vmid, $deviceid);
4314 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4315 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4317 qemu_devicedel
($vmid, $deviceid);
4318 qemu_devicedelverify
($vmid, $deviceid);
4319 qemu_drivedel
($vmid, $deviceid);
4320 qemu_iothread_del
($vmid, $deviceid, $device);
4321 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4322 qemu_devicedel
($vmid, $deviceid);
4323 qemu_devicedelverify
($vmid, $deviceid);
4324 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4325 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4327 qemu_devicedel
($vmid, $deviceid);
4328 qemu_devicedelverify
($vmid, $deviceid);
4329 qemu_drivedel
($vmid, $deviceid);
4330 qemu_deletescsihw
($conf, $vmid, $deviceid);
4332 qemu_iothread_del
($vmid, "virtioscsi$device->{index}", $device)
4333 if $conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single');
4334 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4335 qemu_devicedel
($vmid, $deviceid);
4336 qemu_devicedelverify
($vmid, $deviceid);
4337 qemu_netdevdel
($vmid, $deviceid);
4339 die "can't unplug device '$deviceid'\n";
4345 sub qemu_spice_usbredir_chardev_add
{
4346 my ($vmid, $id) = @_;
4348 mon_cmd
($vmid, "chardev-add" , (
4359 sub qemu_iothread_add
{
4360 my ($vmid, $deviceid, $device) = @_;
4362 if ($device->{iothread
}) {
4363 my $iothreads = vm_iothreads_list
($vmid);
4364 qemu_objectadd
($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4368 sub qemu_iothread_del
{
4369 my ($vmid, $deviceid, $device) = @_;
4371 if ($device->{iothread
}) {
4372 my $iothreads = vm_iothreads_list
($vmid);
4373 qemu_objectdel
($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4378 my ($storecfg, $vmid, $device) = @_;
4380 my $kvmver = get_running_qemu_version
($vmid);
4381 my $io_uring = min_version
($kvmver, 6, 0);
4382 my $drive = print_drive_commandline_full
($storecfg, $vmid, $device, undef, $io_uring);
4383 $drive =~ s/\\/\\\\/g;
4384 my $ret = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "drive_add auto \"$drive\"");
4386 # If the command succeeds qemu prints: "OK
"
4387 return 1 if $ret =~ m/OK/s;
4389 die "adding drive failed
: $ret\n";
4393 my ($vmid, $deviceid) = @_;
4395 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-
$deviceid");
4398 return 1 if $ret eq "";
4400 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4401 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4403 die "deleting drive
$deviceid failed
: $ret\n";
4406 sub qemu_deviceaddverify {
4407 my ($vmid, $deviceid) = @_;
4409 for (my $i = 0; $i <= 5; $i++) {
4410 my $devices_list = vm_devices_list($vmid);
4411 return 1 if defined($devices_list->{$deviceid});
4415 die "error on hotplug device
'$deviceid'\n";
4419 sub qemu_devicedelverify {
4420 my ($vmid, $deviceid) = @_;
4422 # need to verify that the device is correctly removed as device_del
4423 # is async and empty return is not reliable
4425 for (my $i = 0; $i <= 5; $i++) {
4426 my $devices_list = vm_devices_list($vmid);
4427 return 1 if !defined($devices_list->{$deviceid});
4431 die "error on hot-unplugging device
'$deviceid'\n";
4434 sub qemu_findorcreatescsihw {
4435 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4437 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4439 my $scsihwid="$controller_prefix$controller";
4440 my $devices_list = vm_devices_list($vmid);
4442 if (!defined($devices_list->{$scsihwid})) {
4443 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4449 sub qemu_deletescsihw {
4450 my ($conf, $vmid, $opt) = @_;
4452 my $device = parse_drive($opt, $conf->{$opt});
4454 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4455 vm_deviceunplug($vmid, $conf, "virtioscsi
$device->{index}");
4459 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4461 my $devices_list = vm_devices_list($vmid);
4462 foreach my $opt (keys %{$devices_list}) {
4463 if (is_valid_drivename($opt)) {
4464 my $drive = parse_drive($opt, $conf->{$opt});
4465 if ($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4471 my $scsihwid="scsihw
$controller";
4473 vm_deviceunplug($vmid, $conf, $scsihwid);
4478 sub qemu_add_pci_bridge {
4479 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4485 print_pci_addr($device, $bridges, $arch, $machine_type);
4487 while (my ($k, $v) = each %$bridges) {
4490 return 1 if !defined($bridgeid) || $bridgeid < 1;
4492 my $bridge = "pci
.$bridgeid";
4493 my $devices_list = vm_devices_list($vmid);
4495 if (!defined($devices_list->{$bridge})) {
4496 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4502 sub qemu_set_link_status {
4503 my ($vmid, $device, $up) = @_;
4505 mon_cmd($vmid, "set_link
", name => $device,
4506 up => $up ? JSON::true : JSON::false);
4509 sub qemu_netdevadd {
4510 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4512 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4513 my %options = split(/[=,]/, $netdev);
4515 if (defined(my $vhost = $options{vhost})) {
4516 $options{vhost} = JSON::boolean(PVE::JSONSchema::parse_boolean($vhost));
4519 if (defined(my $queues = $options{queues})) {
4520 $options{queues} = $queues + 0;
4523 mon_cmd($vmid, "netdev_add
", %options);
4527 sub qemu_netdevdel {
4528 my ($vmid, $deviceid) = @_;
4530 mon_cmd($vmid, "netdev_del
", id => $deviceid);
4533 sub qemu_usb_hotplug {
4534 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4538 # remove the old one first
4539 vm_deviceunplug($vmid, $conf, $deviceid);
4541 # check if xhci controller is necessary and available
4542 my $devicelist = vm_devices_list($vmid);
4544 if (!$devicelist->{xhci}) {
4545 my $pciaddr = print_pci_addr("xhci
", undef, $arch, $machine_type);
4546 qemu_deviceadd($vmid, PVE::QemuServer::USB::print_qemu_xhci_controller($pciaddr));
4550 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type);
4553 sub qemu_cpu_hotplug {
4554 my ($vmid, $conf, $vcpus) = @_;
4556 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
4559 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4560 $sockets = $conf->{sockets} if $conf->{sockets};
4561 my $cores = $conf->{cores} || 1;
4562 my $maxcpus = $sockets * $cores;
4564 $vcpus = $maxcpus if !$vcpus;
4566 die "you can
't add more vcpus than maxcpus\n"
4567 if $vcpus > $maxcpus;
4569 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4571 if ($vcpus < $currentvcpus) {
4573 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4575 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4576 qemu_devicedel($vmid, "cpu$i");
4578 my $currentrunningvcpus = undef;
4580 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4581 last if scalar(@{$currentrunningvcpus}) == $i-1;
4582 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4586 #update conf after each succesfull cpu unplug
4587 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4588 PVE::QemuConfig->write_config($vmid, $conf);
4591 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4597 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4598 die "vcpus in running vm does not match its configuration\n"
4599 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4601 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4603 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4604 my $cpustr = print_cpu_device($conf, $i);
4605 qemu_deviceadd($vmid, $cpustr);
4608 my $currentrunningvcpus = undef;
4610 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4611 last if scalar(@{$currentrunningvcpus}) == $i;
4612 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4616 #update conf after each succesfull cpu hotplug
4617 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4618 PVE::QemuConfig->write_config($vmid, $conf);
4622 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4623 mon_cmd($vmid, "cpu-add", id => int($i));
4628 sub qemu_block_set_io_throttle {
4629 my ($vmid, $deviceid,
4630 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4631 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4632 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4633 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4635 return if !check_running($vmid) ;
4637 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4639 bps_rd => int($bps_rd),
4640 bps_wr => int($bps_wr),
4642 iops_rd => int($iops_rd),
4643 iops_wr => int($iops_wr),
4644 bps_max => int($bps_max),
4645 bps_rd_max => int($bps_rd_max),
4646 bps_wr_max => int($bps_wr_max),
4647 iops_max => int($iops_max),
4648 iops_rd_max => int($iops_rd_max),
4649 iops_wr_max => int($iops_wr_max),
4650 bps_max_length => int($bps_max_length),
4651 bps_rd_max_length => int($bps_rd_max_length),
4652 bps_wr_max_length => int($bps_wr_max_length),
4653 iops_max_length => int($iops_max_length),
4654 iops_rd_max_length => int($iops_rd_max_length),
4655 iops_wr_max_length => int($iops_wr_max_length),
4660 sub qemu_block_resize {
4661 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4663 my $running = check_running($vmid);
4665 PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4667 return if !$running;
4669 my $padding = (1024 - $size % 1024) % 1024;
4670 $size = $size + $padding;
4675 device => $deviceid,
4681 sub qemu_volume_snapshot {
4682 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4684 my $running = check_running($vmid);
4686 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4687 mon_cmd($vmid, 'blockdev-snapshot-internal-sync
', device => $deviceid, name => $snap);
4689 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4693 sub qemu_volume_snapshot_delete {
4694 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4696 my $running = check_running($vmid);
4701 my $conf = PVE::QemuConfig->load_config($vmid);
4702 PVE::QemuConfig->foreach_volume($conf, sub {
4703 my ($ds, $drive) = @_;
4704 $running = 1 if $drive->{file} eq $volid;
4708 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4709 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync
', device => $deviceid, name => $snap);
4711 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4715 sub set_migration_caps {
4716 my ($vmid, $savevm) = @_;
4718 my $qemu_support = eval { mon_cmd($vmid, "query-proxmox-support") };
4720 my $bitmap_prop = $savevm ? 'pbs-dirty-bitmap-savevm
' : 'pbs-dirty-bitmap-migration
';
4721 my $dirty_bitmaps = $qemu_support->{$bitmap_prop} ? 1 : 0;
4726 "auto-converge" => 1,
4728 "x-rdma-pin-all" => 0,
4731 "dirty-bitmaps" => $dirty_bitmaps,
4734 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4736 for my $supported_capability (@$supported_capabilities) {
4738 capability => $supported_capability->{capability},
4739 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4743 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4747 my ($conf, $func, @param) = @_;
4751 my $test_volid = sub {
4752 my ($key, $drive, $snapname, $pending) = @_;
4754 my $volid = $drive->{file};
4757 $volhash->{$volid}->{cdrom} //= 1;
4758 $volhash->{$volid}->{cdrom} = 0 if !drive_is_cdrom($drive);
4760 my $replicate = $drive->{replicate} // 1;
4761 $volhash->{$volid}->{replicate} //= 0;
4762 $volhash->{$volid}->{replicate} = 1 if $replicate;
4764 $volhash->{$volid}->{shared} //= 0;
4765 $volhash->{$volid}->{shared} = 1 if $drive->{shared};
4767 $volhash->{$volid}->{is_unused} //= 0;
4768 $volhash->{$volid}->{is_unused} = 1 if $key =~ /^unused\d+$/;
4770 $volhash->{$volid}->{is_attached} //= 0;
4771 $volhash->{$volid}->{is_attached} = 1
4772 if !$volhash->{$volid}->{is_unused} && !defined($snapname) && !$pending;
4774 $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
4775 if defined($snapname);
4777 $volhash->{$volid}->{referenced_in_pending} = 1 if $pending;
4779 my $size = $drive->{size};
4780 $volhash->{$volid}->{size} //= $size if $size;
4782 $volhash->{$volid}->{is_vmstate} //= 0;
4783 $volhash->{$volid}->{is_vmstate} = 1 if $key eq 'vmstate
';
4785 $volhash->{$volid}->{is_tpmstate} //= 0;
4786 $volhash->{$volid}->{is_tpmstate} = 1 if $key eq 'tpmstate0
';
4788 $volhash->{$volid}->{drivename} = $key if is_valid_drivename($key);
4791 my $include_opts = {
4792 extra_keys => ['vmstate
'],
4793 include_unused => 1,
4796 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $test_volid);
4798 PVE::QemuConfig->foreach_volume_full($conf->{pending}, $include_opts, $test_volid, undef, 1)
4799 if defined($conf->{pending}) && $conf->{pending}->%*;
4801 foreach my $snapname (keys %{$conf->{snapshots}}) {
4802 my $snap = $conf->{snapshots}->{$snapname};
4803 PVE::QemuConfig->foreach_volume_full($snap, $include_opts, $test_volid, $snapname);
4806 foreach my $volid (keys %$volhash) {
4807 &$func($volid, $volhash->{$volid}, @param);
4811 my $fast_plug_option = {
4815 'migrate_downtime
' => 1,
4816 'migrate_speed
' => 1,
4823 'vmstatestorage
' => 1,
4826 for my $opt (keys %$confdesc_cloudinit) {
4827 $fast_plug_option->{$opt} = 1;
4830 # hotplug changes in [PENDING]
4831 # $selection hash can be used to only apply specified options, for
4832 # example: { cores => 1 } (only apply changed 'cores
')
4833 # $errors ref is used to return error messages
4834 sub vmconfig_hotplug_pending {
4835 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4837 my $defaults = load_defaults();
4838 my $arch = get_vm_arch($conf);
4839 my $machine_type = get_vm_machine($conf, undef, $arch);
4841 # commit values which do not have any impact on running VM first
4842 # Note: those option cannot raise errors, we we do not care about
4843 # $selection and always apply them.
4845 my $add_error = sub {
4846 my ($opt, $msg) = @_;
4847 $errors->{$opt} = "hotplug problem - $msg";
4850 my $cloudinit_pending_properties = PVE::QemuServer::cloudinit_pending_properties();
4852 my $cloudinit_record_changed = sub {
4853 my ($conf, $opt, $old, $new) = @_;
4854 return if !$cloudinit_pending_properties->{$opt};
4856 my $ci = ($conf->{cloudinit} //= {});
4858 my $recorded = $ci->{$opt};
4859 my %added = map { $_ => 1 } PVE::Tools::split_list(delete($ci->{added}) // '');
4861 if (defined($new)) {
4862 if (defined($old)) {
4863 # an existing value is being modified
4864 if (defined($recorded)) {
4865 # the value was already not in sync
4866 if ($new eq $recorded) {
4867 # a value is being reverted to the cloud-init state:
4869 delete $added{$opt};
4871 # the value was changed multiple times, do nothing
4873 } elsif ($added{$opt}) {
4874 # the value had been marked as added and is being changed, do nothing
4876 # the value is new, record it:
4880 # a new value is being added
4881 if (defined($recorded)) {
4882 # it was already not in sync
4883 if ($new eq $recorded) {
4884 # a value is being reverted to the cloud-init state:
4886 delete $added{$opt};
4888 # the value had temporarily been removed, do nothing
4890 } elsif ($added{$opt}) {
4891 # the value had been marked as added already, do nothing
4893 # the value is new, add it
4897 } elsif (!defined($old)) {
4898 # a non-existent value is being removed? ignore...
4900 # a value is being deleted
4901 if (defined($recorded)) {
4902 # a value was already recorded, just keep it
4903 } elsif ($added{$opt}) {
4904 # the value was marked as added, remove it
4905 delete $added{$opt};
4907 # a previously unrecorded value is being removed, record the old value:
4912 my $added = join(',', sort keys %added);
4913 $ci->{added} = $added if length($added);
4917 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4918 if ($fast_plug_option->{$opt}) {
4919 my $new = delete $conf->{pending}->{$opt};
4920 $cloudinit_record_changed->($conf, $opt, $conf->{$opt}, $new);
4921 $conf->{$opt} = $new;
4927 PVE::QemuConfig->write_config($vmid, $conf);
4930 my $ostype = $conf->{ostype};
4931 my $version = extract_version($machine_type, get_running_qemu_version($vmid));
4932 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
4933 my $usb_hotplug = $hotplug_features->{usb}
4934 && min_version($version, 7, 1)
4935 && defined($ostype) && ($ostype eq 'l26
' || windows_version($ostype) > 7);
4937 my $cgroup = PVE::QemuServer::CGroup->new($vmid);
4938 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4940 foreach my $opt (sort keys %$pending_delete_hash) {
4941 next if $selection && !$selection->{$opt};
4942 my $force = $pending_delete_hash->{$opt}->{force};
4944 if ($opt eq 'hotplug
') {
4945 die "skip\n" if ($conf->{hotplug} =~ /(cpu|memory)/);
4946 } elsif ($opt eq 'tablet
') {
4947 die "skip\n" if !$hotplug_features->{usb};
4948 if ($defaults->{tablet}) {
4949 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4950 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4951 if $arch eq 'aarch64
';
4953 vm_deviceunplug($vmid, $conf, 'tablet
');
4954 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4956 } elsif ($opt =~ m/^usb(\d+)$/) {
4958 die "skip\n" if !$usb_hotplug;
4959 vm_deviceunplug($vmid, $conf, "usbredirdev$index"); # if it's a spice port
4960 vm_deviceunplug
($vmid, $conf, $opt);
4961 } elsif ($opt eq 'vcpus') {
4962 die "skip\n" if !$hotplug_features->{cpu
};
4963 qemu_cpu_hotplug
($vmid, $conf, undef);
4964 } elsif ($opt eq 'balloon') {
4965 # enable balloon device is not hotpluggable
4966 die "skip\n" if defined($conf->{balloon
}) && $conf->{balloon
} == 0;
4967 # here we reset the ballooning value to memory
4968 my $balloon = get_current_memory
($conf->{memory
});
4969 mon_cmd
($vmid, "balloon", value
=> $balloon*1024*1024);
4970 } elsif ($fast_plug_option->{$opt}) {
4972 } elsif ($opt =~ m/^net(\d+)$/) {
4973 die "skip\n" if !$hotplug_features->{network
};
4974 vm_deviceunplug
($vmid, $conf, $opt);
4976 my $net = PVE
::QemuServer
::parse_net
($conf->{$opt});
4977 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($net->{bridge
}, $net->{macaddr
}, $conf->{name
});
4979 } elsif (is_valid_drivename
($opt)) {
4980 die "skip\n" if !$hotplug_features->{disk
} || $opt =~ m/(ide|sata)(\d+)/;
4981 vm_deviceunplug
($vmid, $conf, $opt);
4982 vmconfig_delete_or_detach_drive
($vmid, $storecfg, $conf, $opt, $force);
4983 } elsif ($opt =~ m/^memory$/) {
4984 die "skip\n" if !$hotplug_features->{memory
};
4985 PVE
::QemuServer
::Memory
::qemu_memory_hotplug
($vmid, $conf);
4986 } elsif ($opt eq 'cpuunits') {
4987 $cgroup->change_cpu_shares(undef);
4988 } elsif ($opt eq 'cpulimit') {
4989 $cgroup->change_cpu_quota(undef, undef); # reset, cgroup module can better decide values
4995 &$add_error($opt, $err) if $err ne "skip\n";
4997 my $old = delete $conf->{$opt};
4998 $cloudinit_record_changed->($conf, $opt, $old, undef);
4999 PVE
::QemuConfig-
>remove_from_pending_delete($conf, $opt);
5004 foreach my $opt (keys %{$conf->{pending
}}) {
5005 next if $selection && !$selection->{$opt};
5006 my $value = $conf->{pending
}->{$opt};
5008 if ($opt eq 'hotplug') {
5009 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug
} =~ /memory/);
5010 die "skip\n" if ($value =~ /cpu/) || ($value !~ /cpu/ && $conf->{hotplug
} =~ /cpu/);
5011 } elsif ($opt eq 'tablet') {
5012 die "skip\n" if !$hotplug_features->{usb
};
5014 vm_deviceplug
($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
5015 vm_deviceplug
($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
5016 if $arch eq 'aarch64';
5017 } elsif ($value == 0) {
5018 vm_deviceunplug
($vmid, $conf, 'tablet');
5019 vm_deviceunplug
($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
5021 } elsif ($opt =~ m/^usb(\d+)$/) {
5023 die "skip\n" if !$usb_hotplug;
5024 my $d = eval { parse_property_string
('pve-qm-usb', $value) };
5026 if ($d->{host
} =~ m/^spice$/i) {
5027 $id = "usbredirdev$index";
5029 qemu_usb_hotplug
($storecfg, $conf, $vmid, $id, $d, $arch, $machine_type);
5030 } elsif ($opt eq 'vcpus') {
5031 die "skip\n" if !$hotplug_features->{cpu
};
5032 qemu_cpu_hotplug
($vmid, $conf, $value);
5033 } elsif ($opt eq 'balloon') {
5034 # enable/disable balloning device is not hotpluggable
5035 my $old_balloon_enabled = !!(!defined($conf->{balloon
}) || $conf->{balloon
});
5036 my $new_balloon_enabled = !!(!defined($conf->{pending
}->{balloon
}) || $conf->{pending
}->{balloon
});
5037 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
5039 # allow manual ballooning if shares is set to zero
5040 if ((defined($conf->{shares
}) && ($conf->{shares
} == 0))) {
5041 my $memory = get_current_memory
($conf->{memory
});
5042 my $balloon = $conf->{pending
}->{balloon
} || $memory;
5043 mon_cmd
($vmid, "balloon", value
=> $balloon*1024*1024);
5045 } elsif ($opt =~ m/^net(\d+)$/) {
5046 # some changes can be done without hotplug
5047 vmconfig_update_net
($storecfg, $conf, $hotplug_features->{network
},
5048 $vmid, $opt, $value, $arch, $machine_type);
5049 } elsif (is_valid_drivename
($opt)) {
5050 die "skip\n" if $opt eq 'efidisk0' || $opt eq 'tpmstate0';
5051 # some changes can be done without hotplug
5052 my $drive = parse_drive
($opt, $value);
5053 if (drive_is_cloudinit
($drive)) {
5054 $cloudinit_opt = [$opt, $drive];
5055 # apply all the other changes first, then generate the cloudinit disk
5058 vmconfig_update_disk
($storecfg, $conf, $hotplug_features->{disk
},
5059 $vmid, $opt, $value, $arch, $machine_type);
5060 } elsif ($opt =~ m/^memory$/) { #dimms
5061 die "skip\n" if !$hotplug_features->{memory
};
5062 $value = PVE
::QemuServer
::Memory
::qemu_memory_hotplug
($vmid, $conf, $value);
5063 } elsif ($opt eq 'cpuunits') {
5064 my $new_cpuunits = PVE
::CGroup
::clamp_cpu_shares
($conf->{pending
}->{$opt}); #clamp
5065 $cgroup->change_cpu_shares($new_cpuunits);
5066 } elsif ($opt eq 'cpulimit') {
5067 my $cpulimit = $conf->{pending
}->{$opt} == 0 ?
-1 : int($conf->{pending
}->{$opt} * 100000);
5068 $cgroup->change_cpu_quota($cpulimit, 100000);
5069 } elsif ($opt eq 'agent') {
5070 vmconfig_update_agent
($conf, $opt, $value);
5072 die "skip\n"; # skip non-hot-pluggable options
5076 &$add_error($opt, $err) if $err ne "skip\n";
5078 $cloudinit_record_changed->($conf, $opt, $conf->{$opt}, $value);
5079 $conf->{$opt} = $value;
5080 delete $conf->{pending
}->{$opt};
5084 if (defined($cloudinit_opt)) {
5085 my ($opt, $drive) = @$cloudinit_opt;
5086 my $value = $conf->{pending
}->{$opt};
5088 my $temp = {%$conf, $opt => $value};
5089 PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($temp, $vmid);
5090 vmconfig_update_disk
($storecfg, $conf, $hotplug_features->{disk
},
5091 $vmid, $opt, $value, $arch, $machine_type);
5094 &$add_error($opt, $err) if $err ne "skip\n";
5096 $conf->{$opt} = $value;
5097 delete $conf->{pending
}->{$opt};
5101 # unplug xhci controller if no usb device is left
5104 for (my $i = 0; $i < $PVE::QemuServer
::USB
::MAX_USB_DEVICES
; $i++) {
5105 next if !defined($conf->{"usb$i"});
5110 vm_deviceunplug
($vmid, $conf, 'xhci');
5114 PVE
::QemuConfig-
>write_config($vmid, $conf);
5116 if ($hotplug_features->{cloudinit
} && PVE
::QemuServer
::Cloudinit
::has_changes
($conf)) {
5117 PVE
::QemuServer
::vmconfig_update_cloudinit_drive
($storecfg, $conf, $vmid);
5121 sub try_deallocate_drive
{
5122 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
5124 if (($force || $key =~ /^unused/) && !drive_is_cdrom
($drive, 1)) {
5125 my $volid = $drive->{file
};
5126 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
5127 my $sid = PVE
::Storage
::parse_volume_id
($volid);
5128 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
5130 # check if the disk is really unused
5131 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
5132 if PVE
::QemuServer
::Drive
::is_volume_in_use
($storecfg, $conf, $key, $volid);
5133 PVE
::Storage
::vdisk_free
($storecfg, $volid);
5136 # If vm is not owner of this disk remove from config
5144 sub vmconfig_delete_or_detach_drive
{
5145 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
5147 my $drive = parse_drive
($opt, $conf->{$opt});
5149 my $rpcenv = PVE
::RPCEnvironment
::get
();
5150 my $authuser = $rpcenv->get_user();
5153 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
5154 try_deallocate_drive
($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
5156 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $drive);
5162 sub vmconfig_apply_pending
{
5163 my ($vmid, $conf, $storecfg, $errors, $skip_cloud_init) = @_;
5165 return if !scalar(keys %{$conf->{pending
}});
5167 my $add_apply_error = sub {
5168 my ($opt, $msg) = @_;
5169 my $err_msg = "unable to apply pending change $opt : $msg";
5170 $errors->{$opt} = $err_msg;
5176 my $pending_delete_hash = PVE
::QemuConfig-
>parse_pending_delete($conf->{pending
}->{delete});
5177 foreach my $opt (sort keys %$pending_delete_hash) {
5178 my $force = $pending_delete_hash->{$opt}->{force
};
5180 if ($opt =~ m/^unused/) {
5181 die "internal error";
5182 } elsif (defined($conf->{$opt}) && is_valid_drivename
($opt)) {
5183 vmconfig_delete_or_detach_drive
($vmid, $storecfg, $conf, $opt, $force);
5184 } elsif (defined($conf->{$opt}) && $opt =~ m/^net\d+$/) {
5186 my $net = PVE
::QemuServer
::parse_net
($conf->{$opt});
5187 eval { PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($net->{bridge
}, $net->{macaddr
}, $conf->{name
}) };
5193 $add_apply_error->($opt, $err);
5195 PVE
::QemuConfig-
>remove_from_pending_delete($conf, $opt);
5196 delete $conf->{$opt};
5200 PVE
::QemuConfig-
>cleanup_pending($conf);
5202 my $generate_cloudinit = $skip_cloud_init ?
0 : undef;
5204 foreach my $opt (keys %{$conf->{pending
}}) { # add/change
5205 next if $opt eq 'delete'; # just to be sure
5207 if (defined($conf->{$opt}) && is_valid_drivename
($opt)) {
5208 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, parse_drive
($opt, $conf->{$opt}))
5209 } elsif (defined($conf->{pending
}->{$opt}) && $opt =~ m/^net\d+$/) {
5211 my $new_net = PVE
::QemuServer
::parse_net
($conf->{pending
}->{$opt});
5213 my $old_net = PVE
::QemuServer
::parse_net
($conf->{$opt});
5215 if ($old_net->{bridge
} ne $new_net->{bridge
} ||
5216 $old_net->{macaddr
} ne $new_net->{macaddr
}) {
5217 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($old_net->{bridge
}, $old_net->{macaddr
}, $conf->{name
});
5220 #fixme: reuse ip if mac change && same bridge
5221 PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($new_net->{bridge
}, $conf->{name
}, $new_net->{macaddr
}, $vmid, undef, 1);
5226 $add_apply_error->($opt, $err);
5229 if (is_valid_drivename
($opt)) {
5230 my $drive = parse_drive
($opt, $conf->{pending
}->{$opt});
5231 $generate_cloudinit //= 1 if drive_is_cloudinit
($drive);
5234 $conf->{$opt} = delete $conf->{pending
}->{$opt};
5238 # write all changes at once to avoid unnecessary i/o
5239 PVE
::QemuConfig-
>write_config($vmid, $conf);
5240 if ($generate_cloudinit) {
5241 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5242 # After successful generation and if there were changes to be applied, update the
5243 # config to drop the {cloudinit} entry.
5244 PVE
::QemuConfig-
>write_config($vmid, $conf);
5249 sub vmconfig_update_net
{
5250 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5252 my $newnet = parse_net
($value);
5254 if ($conf->{$opt}) {
5255 my $oldnet = parse_net
($conf->{$opt});
5257 if (safe_string_ne
($oldnet->{model
}, $newnet->{model
}) ||
5258 safe_string_ne
($oldnet->{macaddr
}, $newnet->{macaddr
}) ||
5259 safe_num_ne
($oldnet->{queues
}, $newnet->{queues
}) ||
5260 safe_num_ne
($oldnet->{mtu
}, $newnet->{mtu
}) ||
5261 !($newnet->{bridge
} && $oldnet->{bridge
})) { # bridge/nat mode change
5263 # for non online change, we try to hot-unplug
5264 die "skip\n" if !$hotplug;
5265 vm_deviceunplug
($vmid, $conf, $opt);
5268 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($oldnet->{bridge
}, $oldnet->{macaddr
}, $conf->{name
});
5273 die "internal error" if $opt !~ m/net(\d+)/;
5274 my $iface = "tap${vmid}i$1";
5276 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
}) ||
5277 safe_num_ne
($oldnet->{tag
}, $newnet->{tag
}) ||
5278 safe_string_ne
($oldnet->{trunks
}, $newnet->{trunks
}) ||
5279 safe_num_ne
($oldnet->{firewall
}, $newnet->{firewall
})) {
5280 PVE
::Network
::tap_unplug
($iface);
5282 #set link_down in guest if bridge or vlan change to notify guest (dhcp renew for example)
5283 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
}) ||
5284 safe_num_ne
($oldnet->{tag
}, $newnet->{tag
})) {
5285 qemu_set_link_status
($vmid, $opt, 0);
5288 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
})) {
5290 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($oldnet->{bridge
}, $oldnet->{macaddr
}, $conf->{name
});
5291 PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($newnet->{bridge
}, $conf->{name
}, $newnet->{macaddr
}, $vmid, undef, 1);
5296 PVE
::Network
::SDN
::Zones
::tap_plug
($iface, $newnet->{bridge
}, $newnet->{tag
}, $newnet->{firewall
}, $newnet->{trunks
}, $newnet->{rate
});
5298 PVE
::Network
::tap_plug
($iface, $newnet->{bridge
}, $newnet->{tag
}, $newnet->{firewall
}, $newnet->{trunks
}, $newnet->{rate
});
5301 #set link_up in guest if bridge or vlan change to notify guest (dhcp renew for example)
5302 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
}) ||
5303 safe_num_ne
($oldnet->{tag
}, $newnet->{tag
})) {
5304 qemu_set_link_status
($vmid, $opt, 1);
5307 } elsif (safe_num_ne
($oldnet->{rate
}, $newnet->{rate
})) {
5308 # Rate can be applied on its own but any change above needs to
5309 # include the rate in tap_plug since OVS resets everything.
5310 PVE
::Network
::tap_rate_limit
($iface, $newnet->{rate
});
5313 if (safe_string_ne
($oldnet->{link_down
}, $newnet->{link_down
})) {
5314 qemu_set_link_status
($vmid, $opt, !$newnet->{link_down
});
5323 PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($newnet->{bridge
}, $conf->{name
}, $newnet->{macaddr
}, $vmid, undef, 1);
5324 PVE
::Network
::SDN
::Vnets
::add_dhcp_mapping
($newnet->{bridge
}, $newnet->{macaddr
}, $vmid, $conf->{name
});
5326 vm_deviceplug
($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
5332 sub vmconfig_update_agent
{
5333 my ($conf, $opt, $value) = @_;
5335 die "skip\n" if !$conf->{$opt};
5337 my $hotplug_options = { fstrim_cloned_disks
=> 1 };
5339 my $old_agent = parse_guest_agent
($conf);
5340 my $agent = parse_guest_agent
({$opt => $value});
5342 for my $option (keys %$agent) { # added/changed options
5343 next if defined($hotplug_options->{$option});
5344 die "skip\n" if safe_string_ne
($agent->{$option}, $old_agent->{$option});
5347 for my $option (keys %$old_agent) { # removed options
5348 next if defined($hotplug_options->{$option});
5349 die "skip\n" if safe_string_ne
($old_agent->{$option}, $agent->{$option});
5352 return; # either no actual change (e.g., format string reordered) or just hotpluggable changes
5355 sub vmconfig_update_disk
{
5356 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5358 my $drive = parse_drive
($opt, $value);
5360 if ($conf->{$opt} && (my $old_drive = parse_drive
($opt, $conf->{$opt}))) {
5361 my $media = $drive->{media
} || 'disk';
5362 my $oldmedia = $old_drive->{media
} || 'disk';
5363 die "unable to change media type\n" if $media ne $oldmedia;
5365 if (!drive_is_cdrom
($old_drive)) {
5367 if ($drive->{file
} ne $old_drive->{file
}) {
5369 die "skip\n" if !$hotplug;
5371 # unplug and register as unused
5372 vm_deviceunplug
($vmid, $conf, $opt);
5373 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $old_drive)
5376 # update existing disk
5378 # skip non hotpluggable value
5379 if (safe_string_ne
($drive->{aio
}, $old_drive->{aio
}) ||
5380 safe_string_ne
($drive->{discard
}, $old_drive->{discard
}) ||
5381 safe_string_ne
($drive->{iothread
}, $old_drive->{iothread
}) ||
5382 safe_string_ne
($drive->{queues
}, $old_drive->{queues
}) ||
5383 safe_string_ne
($drive->{cache
}, $old_drive->{cache
}) ||
5384 safe_string_ne
($drive->{ssd
}, $old_drive->{ssd
}) ||
5385 safe_string_ne
($drive->{ro
}, $old_drive->{ro
})) {
5390 if (safe_num_ne
($drive->{mbps
}, $old_drive->{mbps
}) ||
5391 safe_num_ne
($drive->{mbps_rd
}, $old_drive->{mbps_rd
}) ||
5392 safe_num_ne
($drive->{mbps_wr
}, $old_drive->{mbps_wr
}) ||
5393 safe_num_ne
($drive->{iops
}, $old_drive->{iops
}) ||
5394 safe_num_ne
($drive->{iops_rd
}, $old_drive->{iops_rd
}) ||
5395 safe_num_ne
($drive->{iops_wr
}, $old_drive->{iops_wr
}) ||
5396 safe_num_ne
($drive->{mbps_max
}, $old_drive->{mbps_max
}) ||
5397 safe_num_ne
($drive->{mbps_rd_max
}, $old_drive->{mbps_rd_max
}) ||
5398 safe_num_ne
($drive->{mbps_wr_max
}, $old_drive->{mbps_wr_max
}) ||
5399 safe_num_ne
($drive->{iops_max
}, $old_drive->{iops_max
}) ||
5400 safe_num_ne
($drive->{iops_rd_max
}, $old_drive->{iops_rd_max
}) ||
5401 safe_num_ne
($drive->{iops_wr_max
}, $old_drive->{iops_wr_max
}) ||
5402 safe_num_ne
($drive->{bps_max_length
}, $old_drive->{bps_max_length
}) ||
5403 safe_num_ne
($drive->{bps_rd_max_length
}, $old_drive->{bps_rd_max_length
}) ||
5404 safe_num_ne
($drive->{bps_wr_max_length
}, $old_drive->{bps_wr_max_length
}) ||
5405 safe_num_ne
($drive->{iops_max_length
}, $old_drive->{iops_max_length
}) ||
5406 safe_num_ne
($drive->{iops_rd_max_length
}, $old_drive->{iops_rd_max_length
}) ||
5407 safe_num_ne
($drive->{iops_wr_max_length
}, $old_drive->{iops_wr_max_length
})) {
5409 qemu_block_set_io_throttle
(
5411 ($drive->{mbps
} || 0)*1024*1024,
5412 ($drive->{mbps_rd
} || 0)*1024*1024,
5413 ($drive->{mbps_wr
} || 0)*1024*1024,
5414 $drive->{iops
} || 0,
5415 $drive->{iops_rd
} || 0,
5416 $drive->{iops_wr
} || 0,
5417 ($drive->{mbps_max
} || 0)*1024*1024,
5418 ($drive->{mbps_rd_max
} || 0)*1024*1024,
5419 ($drive->{mbps_wr_max
} || 0)*1024*1024,
5420 $drive->{iops_max
} || 0,
5421 $drive->{iops_rd_max
} || 0,
5422 $drive->{iops_wr_max
} || 0,
5423 $drive->{bps_max_length
} || 1,
5424 $drive->{bps_rd_max_length
} || 1,
5425 $drive->{bps_wr_max_length
} || 1,
5426 $drive->{iops_max_length
} || 1,
5427 $drive->{iops_rd_max_length
} || 1,
5428 $drive->{iops_wr_max_length
} || 1,
5438 if ($drive->{file
} eq 'none') {
5439 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$opt");
5440 if (drive_is_cloudinit
($old_drive)) {
5441 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $old_drive);
5444 my $path = get_iso_path
($storecfg, $vmid, $drive->{file
});
5446 # force eject if locked
5447 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$opt");
5450 mon_cmd
($vmid, "blockdev-change-medium",
5451 id
=> "$opt", filename
=> "$path");
5459 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
5461 PVE
::Storage
::activate_volumes
($storecfg, [$drive->{file
}]) if $drive->{file
} !~ m
|^/dev/.+|;
5462 vm_deviceplug
($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
5465 sub vmconfig_update_cloudinit_drive
{
5466 my ($storecfg, $conf, $vmid) = @_;
5468 my $cloudinit_ds = undef;
5469 my $cloudinit_drive = undef;
5471 PVE
::QemuConfig-
>foreach_volume($conf, sub {
5472 my ($ds, $drive) = @_;
5473 if (PVE
::QemuServer
::drive_is_cloudinit
($drive)) {
5474 $cloudinit_ds = $ds;
5475 $cloudinit_drive = $drive;
5479 return if !$cloudinit_drive;
5481 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5482 PVE
::QemuConfig-
>write_config($vmid, $conf);
5485 my $running = PVE
::QemuServer
::check_running
($vmid);
5488 my $path = PVE
::Storage
::path
($storecfg, $cloudinit_drive->{file
});
5490 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$cloudinit_ds");
5491 mon_cmd
($vmid, "blockdev-change-medium", id
=> "$cloudinit_ds", filename
=> "$path");
5496 # called in locked context by incoming migration
5497 sub vm_migrate_get_nbd_disks
{
5498 my ($storecfg, $conf, $replicated_volumes) = @_;
5500 my $local_volumes = {};
5501 PVE
::QemuConfig-
>foreach_volume($conf, sub {
5502 my ($ds, $drive) = @_;
5504 return if drive_is_cdrom
($drive);
5505 return if $ds eq 'tpmstate0';
5507 my $volid = $drive->{file
};
5511 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
5513 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
5514 return if $scfg->{shared
};
5516 my $format = qemu_img_format
($scfg, $volname);
5518 # replicated disks re-use existing state via bitmap
5519 my $use_existing = $replicated_volumes->{$volid} ?
1 : 0;
5520 $local_volumes->{$ds} = [$volid, $storeid, $drive, $use_existing, $format];
5522 return $local_volumes;
5525 # called in locked context by incoming migration
5526 sub vm_migrate_alloc_nbd_disks
{
5527 my ($storecfg, $vmid, $source_volumes, $storagemap) = @_;
5530 foreach my $opt (sort keys %$source_volumes) {
5531 my ($volid, $storeid, $drive, $use_existing, $format) = @{$source_volumes->{$opt}};
5533 if ($use_existing) {
5534 $nbd->{$opt}->{drivestr
} = print_drive
($drive);
5535 $nbd->{$opt}->{volid
} = $volid;
5536 $nbd->{$opt}->{replicated
} = 1;
5540 $storeid = PVE
::JSONSchema
::map_id
($storagemap, $storeid);
5542 # order of precedence, filtered by whether storage supports it:
5543 # 1. explicit requested format
5544 # 2. default format of storage
5545 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
5546 $format = $defFormat if !$format || !grep { $format eq $_ } $validFormats->@*;
5548 my $size = $drive->{size
} / 1024;
5549 my $newvolid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $format, undef, $size);
5550 my $newdrive = $drive;
5551 $newdrive->{format
} = $format;
5552 $newdrive->{file
} = $newvolid;
5553 my $drivestr = print_drive
($newdrive);
5554 $nbd->{$opt}->{drivestr
} = $drivestr;
5555 $nbd->{$opt}->{volid
} = $newvolid;
5561 # see vm_start_nolock for parameters, additionally:
5563 # storagemap = parsed storage map for allocating NBD disks
5565 my ($storecfg, $vmid, $params, $migrate_opts) = @_;
5567 return PVE
::QemuConfig-
>lock_config($vmid, sub {
5568 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migrate_opts->{migratedfrom
});
5570 die "you can't start a vm if it's a template\n"
5571 if !$params->{skiptemplate
} && PVE
::QemuConfig-
>is_template($conf);
5573 my $has_suspended_lock = PVE
::QemuConfig-
>has_lock($conf, 'suspended');
5574 my $has_backup_lock = PVE
::QemuConfig-
>has_lock($conf, 'backup');
5576 my $running = check_running
($vmid, undef, $migrate_opts->{migratedfrom
});
5578 if ($has_backup_lock && $running) {
5579 # a backup is currently running, attempt to start the guest in the
5580 # existing QEMU instance
5581 return vm_resume
($vmid);
5584 PVE
::QemuConfig-
>check_lock($conf)
5585 if !($params->{skiplock
} || $has_suspended_lock);
5587 $params->{resume
} = $has_suspended_lock || defined($conf->{vmstate
});
5589 die "VM $vmid already running\n" if $running;
5591 if (my $storagemap = $migrate_opts->{storagemap
}) {
5592 my $replicated = $migrate_opts->{replicated_volumes
};
5593 my $disks = vm_migrate_get_nbd_disks
($storecfg, $conf, $replicated);
5594 $migrate_opts->{nbd
} = vm_migrate_alloc_nbd_disks
($storecfg, $vmid, $disks, $storagemap);
5596 foreach my $opt (keys %{$migrate_opts->{nbd
}}) {
5597 $conf->{$opt} = $migrate_opts->{nbd
}->{$opt}->{drivestr
};
5601 return vm_start_nolock
($storecfg, $vmid, $conf, $params, $migrate_opts);
5607 # statefile => 'tcp', 'unix' for migration or path/volid for RAM state
5608 # skiplock => 0/1, skip checking for config lock
5609 # skiptemplate => 0/1, skip checking whether VM is template
5610 # forcemachine => to force QEMU machine (rollback/migration)
5611 # forcecpu => a QEMU '-cpu' argument string to override get_cpu_options
5612 # timeout => in seconds
5613 # paused => start VM in paused state (backup)
5614 # resume => resume from hibernation
5625 # nbd => volumes for NBD exports (vm_migrate_alloc_nbd_disks)
5626 # migratedfrom => source node
5627 # spice_ticket => used for spice migration, passed via tunnel/stdin
5628 # network => CIDR of migration network
5629 # type => secure/insecure - tunnel over encrypted connection or plain-text
5630 # nbd_proto_version => int, 0 for TCP, 1 for UNIX
5631 # replicated_volumes => which volids should be re-used with bitmaps for nbd migration
5632 # offline_volumes => new volids of offline migrated disks like tpmstate and cloudinit, not yet
5633 # contained in config
5634 sub vm_start_nolock
{
5635 my ($storecfg, $vmid, $conf, $params, $migrate_opts) = @_;
5637 my $statefile = $params->{statefile
};
5638 my $resume = $params->{resume
};
5640 my $migratedfrom = $migrate_opts->{migratedfrom
};
5641 my $migration_type = $migrate_opts->{type
};
5645 # clean up leftover reboot request files
5646 eval { clear_reboot_request
($vmid); };
5649 if (!$statefile && scalar(keys %{$conf->{pending
}})) {
5650 vmconfig_apply_pending
($vmid, $conf, $storecfg);
5651 $conf = PVE
::QemuConfig-
>load_config($vmid); # update/reload
5654 # don't regenerate the ISO if the VM is started as part of a live migration
5655 # this way we can reuse the old ISO with the correct config
5656 if (!$migratedfrom) {
5657 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5658 # FIXME: apply_cloudinit_config updates $conf in this case, and it would only drop
5659 # $conf->{cloudinit}, so we could just not do this?
5660 # But we do it above, so for now let's be consistent.
5661 $conf = PVE
::QemuConfig-
>load_config($vmid); # update/reload
5665 # override offline migrated volumes, conf is out of date still
5666 if (my $offline_volumes = $migrate_opts->{offline_volumes
}) {
5667 for my $key (sort keys $offline_volumes->%*) {
5668 my $parsed = parse_drive
($key, $conf->{$key});
5669 $parsed->{file
} = $offline_volumes->{$key};
5670 $conf->{$key} = print_drive
($parsed);
5674 my $defaults = load_defaults
();
5676 # set environment variable useful inside network script
5677 # for remote migration the config is available on the target node!
5678 if (!$migrate_opts->{remote_node
}) {
5679 $ENV{PVE_MIGRATED_FROM
} = $migratedfrom;
5682 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-start', 1);
5684 my $forcemachine = $params->{forcemachine
};
5685 my $forcecpu = $params->{forcecpu
};
5687 # enforce machine and CPU type on suspended vm to ensure HW compatibility
5688 $forcemachine = $conf->{runningmachine
};
5689 $forcecpu = $conf->{runningcpu
};
5690 print "Resuming suspended VM\n";
5693 my ($cmd, $vollist, $spice_port, $pci_devices) = config_to_command
($storecfg, $vmid,
5694 $conf, $defaults, $forcemachine, $forcecpu, $params->{'pbs-backing'});
5697 my $get_migration_ip = sub {
5698 my ($nodename) = @_;
5700 return $migration_ip if defined($migration_ip);
5702 my $cidr = $migrate_opts->{network
};
5704 if (!defined($cidr)) {
5705 my $dc_conf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5706 $cidr = $dc_conf->{migration
}->{network
};
5709 if (defined($cidr)) {
5710 my $ips = PVE
::Network
::get_local_ip_from_cidr
($cidr);
5712 die "could not get IP: no address configured on local " .
5713 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5715 die "could not get IP: multiple addresses configured on local " .
5716 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5718 $migration_ip = @$ips[0];
5721 $migration_ip = PVE
::Cluster
::remote_node_ip
($nodename, 1)
5722 if !defined($migration_ip);
5724 return $migration_ip;
5728 if ($statefile eq 'tcp') {
5729 my $migrate = $res->{migrate
} = { proto
=> 'tcp' };
5730 $migrate->{addr
} = "localhost";
5731 my $datacenterconf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5732 my $nodename = nodename
();
5734 if (!defined($migration_type)) {
5735 if (defined($datacenterconf->{migration
}->{type
})) {
5736 $migration_type = $datacenterconf->{migration
}->{type
};
5738 $migration_type = 'secure';
5742 if ($migration_type eq 'insecure') {
5743 $migrate->{addr
} = $get_migration_ip->($nodename);
5744 $migrate->{addr
} = "[$migrate->{addr}]" if Net
::IP
::ip_is_ipv6
($migrate->{addr
});
5747 # see #4501: port reservation should be done close to usage - tell QEMU where to listen
5749 push @$cmd, '-incoming', 'defer';
5752 } elsif ($statefile eq 'unix') {
5753 # should be default for secure migrations as a ssh TCP forward
5754 # tunnel is not deterministic reliable ready and fails regurarly
5755 # to set up in time, so use UNIX socket forwards
5756 my $migrate = $res->{migrate
} = { proto
=> 'unix' };
5757 $migrate->{addr
} = "/run/qemu-server/$vmid.migrate";
5758 unlink $migrate->{addr
};
5760 $migrate->{uri
} = "unix:$migrate->{addr}";
5761 push @$cmd, '-incoming', $migrate->{uri
};
5764 } elsif (-e
$statefile) {
5765 push @$cmd, '-loadstate', $statefile;
5767 my $statepath = PVE
::Storage
::path
($storecfg, $statefile);
5768 push @$vollist, $statefile;
5769 push @$cmd, '-loadstate', $statepath;
5771 } elsif ($params->{paused
}) {
5775 my $memory = get_current_memory
($conf->{memory
});
5776 my $start_timeout = $params->{timeout
} // config_aware_timeout
($conf, $memory, $resume);
5778 my $pci_reserve_list = [];
5779 for my $device (values $pci_devices->%*) {
5780 next if $device->{mdev
}; # we don't reserve for mdev devices
5781 push $pci_reserve_list->@*, map { $_->{id
} } $device->{ids
}->@*;
5784 # reserve all PCI IDs before actually doing anything with them
5785 PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_reserve_list, $vmid, $start_timeout);
5789 for my $id (sort keys %$pci_devices) {
5790 my $d = $pci_devices->{$id};
5791 my ($index) = ($id =~ m/^hostpci(\d+)$/);
5794 for my $dev ($d->{ids
}->@*) {
5795 my $info = eval { PVE
::QemuServer
::PCI
::prepare_pci_device
($vmid, $dev->{id
}, $index, $d->{mdev
}) };
5798 $chosen_mdev = $info;
5799 last if $chosen_mdev; # if successful, we're done
5805 next if !$d->{mdev
};
5806 die "could not create mediated device\n" if !defined($chosen_mdev);
5808 # nvidia grid needs the uuid of the mdev as qemu parameter
5809 if (!defined($uuid) && $chosen_mdev->{vendor
} =~ m/^(0x)?10de$/) {
5810 if (defined($conf->{smbios1
})) {
5811 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
5812 $uuid = $smbios_conf->{uuid
} if defined($smbios_conf->{uuid
});
5814 $uuid = PVE
::QemuServer
::PCI
::generate_mdev_uuid
($vmid, $index) if !defined($uuid);
5817 push @$cmd, '-uuid', $uuid if defined($uuid);
5820 eval { cleanup_pci_devices
($vmid, $conf) };
5825 PVE
::Storage
::activate_volumes
($storecfg, $vollist);
5828 my %silence_std_outs = (outfunc
=> sub {}, errfunc
=> sub {});
5829 eval { run_command
(['/bin/systemctl', 'reset-failed', "$vmid.scope"], %silence_std_outs) };
5830 eval { run_command
(['/bin/systemctl', 'stop', "$vmid.scope"], %silence_std_outs) };
5831 # Issues with the above 'stop' not being fully completed are extremely rare, a very low
5832 # timeout should be more than enough here...
5833 PVE
::Systemd
::wait_for_unit_removed
("$vmid.scope", 20);
5835 my $cpuunits = PVE
::CGroup
::clamp_cpu_shares
($conf->{cpuunits
});
5838 timeout
=> $statefile ?
undef : $start_timeout,
5843 # when migrating, prefix QEMU output so other side can pick up any
5844 # errors that might occur and show the user
5845 if ($migratedfrom) {
5846 $run_params{quiet
} = 1;
5847 $run_params{logfunc
} = sub { print "QEMU: $_[0]\n" };
5850 my %systemd_properties = (
5851 Slice
=> 'qemu.slice',
5852 KillMode
=> 'process',
5854 TimeoutStopUSec
=> ULONG_MAX
, # infinity
5857 if (PVE
::CGroup
::cgroup_mode
() == 2) {
5858 $systemd_properties{CPUWeight
} = $cpuunits;
5860 $systemd_properties{CPUShares
} = $cpuunits;
5863 if (my $cpulimit = $conf->{cpulimit
}) {
5864 $systemd_properties{CPUQuota
} = int($cpulimit * 100);
5866 $systemd_properties{timeout
} = 10 if $statefile; # setting up the scope shoul be quick
5868 my $run_qemu = sub {
5869 PVE
::Tools
::run_fork
sub {
5870 PVE
::Systemd
::enter_systemd_scope
($vmid, "Proxmox VE VM $vmid", %systemd_properties);
5873 if ((my $tpm = $conf->{tpmstate0
}) && !PVE
::QemuConfig-
>is_template($conf)) {
5874 # start the TPM emulator so QEMU can connect on start
5875 $tpmpid = start_swtpm
($storecfg, $vmid, $tpm, $migratedfrom);
5878 my $exitcode = run_command
($cmd, %run_params);
5881 warn "stopping swtpm instance (pid $tpmpid) due to QEMU startup error\n";
5882 kill 'TERM', $tpmpid;
5884 die "QEMU exited with code $exitcode\n";
5889 if ($conf->{hugepages
}) {
5892 my $hotplug_features =
5893 parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
5894 my $hugepages_topology =
5895 PVE
::QemuServer
::Memory
::hugepages_topology
($conf, $hotplug_features->{memory
});
5897 my $hugepages_host_topology = PVE
::QemuServer
::Memory
::hugepages_host_topology
();
5899 PVE
::QemuServer
::Memory
::hugepages_mount
();
5900 PVE
::QemuServer
::Memory
::hugepages_allocate
($hugepages_topology, $hugepages_host_topology);
5902 eval { $run_qemu->() };
5904 PVE
::QemuServer
::Memory
::hugepages_reset
($hugepages_host_topology)
5905 if !$conf->{keephugepages
};
5909 PVE
::QemuServer
::Memory
::hugepages_pre_deallocate
($hugepages_topology)
5910 if !$conf->{keephugepages
};
5912 eval { PVE
::QemuServer
::Memory
::hugepages_update_locked
($code); };
5915 eval { $run_qemu->() };
5919 # deactivate volumes if start fails
5920 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
5922 eval { cleanup_pci_devices
($vmid, $conf) };
5925 die "start failed: $err";
5928 # re-reserve all PCI IDs now that we can know the actual VM PID
5929 my $pid = PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
5930 eval { PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_reserve_list, $vmid, undef, $pid) };
5933 if (defined(my $migrate = $res->{migrate
})) {
5934 if ($migrate->{proto
} eq 'tcp') {
5935 my $nodename = nodename
();
5936 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
5937 $migrate->{port
} = PVE
::Tools
::next_migrate_port
($pfamily);
5938 $migrate->{uri
} = "tcp:$migrate->{addr}:$migrate->{port}";
5939 mon_cmd
($vmid, "migrate-incoming", uri
=> $migrate->{uri
});
5941 print "migration listens on $migrate->{uri}\n";
5942 } elsif ($statefile) {
5943 eval { mon_cmd
($vmid, "cont"); };
5947 #start nbd server for storage migration
5948 if (my $nbd = $migrate_opts->{nbd
}) {
5949 my $nbd_protocol_version = $migrate_opts->{nbd_proto_version
} // 0;
5951 my $migrate_storage_uri;
5952 # nbd_protocol_version > 0 for unix socket support
5953 if ($nbd_protocol_version > 0 && ($migration_type eq 'secure' || $migration_type eq 'websocket')) {
5954 my $socket_path = "/run/qemu-server/$vmid\_nbd.migrate";
5955 mon_cmd
($vmid, "nbd-server-start", addr
=> { type
=> 'unix', data
=> { path
=> $socket_path } } );
5956 $migrate_storage_uri = "nbd:unix:$socket_path";
5957 $res->{migrate
}->{unix_sockets
} = [$socket_path];
5959 my $nodename = nodename
();
5960 my $localip = $get_migration_ip->($nodename);
5961 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
5962 my $storage_migrate_port = PVE
::Tools
::next_migrate_port
($pfamily);
5964 mon_cmd
($vmid, "nbd-server-start", addr
=> {
5967 host
=> "${localip}",
5968 port
=> "${storage_migrate_port}",
5971 $localip = "[$localip]" if Net
::IP
::ip_is_ipv6
($localip);
5972 $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}";
5975 my $block_info = mon_cmd
($vmid, "query-block");
5976 $block_info = { map { $_->{device
} => $_ } $block_info->@* };
5978 foreach my $opt (sort keys %$nbd) {
5979 my $drivestr = $nbd->{$opt}->{drivestr
};
5980 my $volid = $nbd->{$opt}->{volid
};
5982 my $block_node = $block_info->{"drive-$opt"}->{inserted
}->{'node-name'};
5988 'node-name' => $block_node,
5989 writable
=> JSON
::true
,
5991 name
=> "drive-$opt", # NBD export name
5994 my $nbd_uri = "$migrate_storage_uri:exportname=drive-$opt";
5995 print "storage migration listens on $nbd_uri volume:$drivestr\n";
5996 print "re-using replicated volume: $opt - $volid\n"
5997 if $nbd->{$opt}->{replicated
};
5999 $res->{drives
}->{$opt} = $nbd->{$opt};
6000 $res->{drives
}->{$opt}->{nbd_uri
} = $nbd_uri;
6004 if ($migratedfrom) {
6006 set_migration_caps
($vmid);
6011 print "spice listens on port $spice_port\n";
6012 $res->{spice_port
} = $spice_port;
6013 if ($migrate_opts->{spice_ticket
}) {
6014 mon_cmd
($vmid, "set_password", protocol
=> 'spice', password
=>
6015 $migrate_opts->{spice_ticket
});
6016 mon_cmd
($vmid, "expire_password", protocol
=> 'spice', time => "+30");
6021 mon_cmd
($vmid, "balloon", value
=> $conf->{balloon
}*1024*1024)
6022 if !$statefile && $conf->{balloon
};
6024 foreach my $opt (keys %$conf) {
6025 next if $opt !~ m/^net\d+$/;
6026 my $nicconf = parse_net
($conf->{$opt});
6027 qemu_set_link_status
($vmid, $opt, 0) if $nicconf->{link_down
};
6029 add_nets_bridge_fdb
($conf, $vmid);
6032 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
6037 path
=> "machine/peripheral/balloon0",
6038 property
=> "guest-stats-polling-interval",
6042 log_warn
("could not set polling interval for ballooning - $@") if $@;
6046 print "Resumed VM, removing state\n";
6047 if (my $vmstate = $conf->{vmstate
}) {
6048 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
6049 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
6051 delete $conf->@{qw(lock vmstate runningmachine runningcpu)};
6052 PVE
::QemuConfig-
>write_config($vmid, $conf);
6055 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'post-start');
6057 my ($current_machine, $is_deprecated) =
6058 PVE
::QemuServer
::Machine
::get_current_qemu_machine
($vmid);
6059 if ($is_deprecated) {
6061 "current machine version '$current_machine' is deprecated - see the documentation and ".
6062 "change to a newer one",
6069 sub vm_commandline
{
6070 my ($storecfg, $vmid, $snapname) = @_;
6072 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6074 my ($forcemachine, $forcecpu);
6076 my $snapshot = $conf->{snapshots
}->{$snapname};
6077 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
6079 # check for machine or CPU overrides in snapshot
6080 $forcemachine = $snapshot->{runningmachine
};
6081 $forcecpu = $snapshot->{runningcpu
};
6083 $snapshot->{digest
} = $conf->{digest
}; # keep file digest for API
6088 my $defaults = load_defaults
();
6090 my $cmd = config_to_command
($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu);
6092 return PVE
::Tools
::cmd2string
($cmd);
6096 my ($vmid, $skiplock) = @_;
6098 PVE
::QemuConfig-
>lock_config($vmid, sub {
6100 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6102 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
6104 mon_cmd
($vmid, "system_reset");
6108 sub get_vm_volumes
{
6112 foreach_volid
($conf, sub {
6113 my ($volid, $attr) = @_;
6115 return if $volid =~ m
|^/|;
6117 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
6120 push @$vollist, $volid;
6126 sub cleanup_pci_devices
{
6127 my ($vmid, $conf) = @_;
6129 foreach my $key (keys %$conf) {
6130 next if $key !~ m/^hostpci(\d+)$/;
6131 my $hostpciindex = $1;
6132 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $hostpciindex);
6133 my $d = parse_hostpci
($conf->{$key});
6135 # NOTE: avoid PVE::SysFSTools::pci_cleanup_mdev_device as it requires PCI ID and we
6136 # don't want to break ABI just for this two liner
6137 my $dev_sysfs_dir = "/sys/bus/mdev/devices/$uuid";
6139 # some nvidia vgpu driver versions want to clean the mdevs up themselves, and error
6140 # out when we do it first. so wait for 10 seconds and then try it
6141 if ($d->{ids
}->[0]->[0]->{vendor
} =~ m/^(0x)?10de$/) {
6145 PVE
::SysFSTools
::file_write
("$dev_sysfs_dir/remove", "1") if -e
$dev_sysfs_dir;
6148 PVE
::QemuServer
::PCI
::remove_pci_reservation
($vmid);
6151 sub vm_stop_cleanup
{
6152 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
6157 my $vollist = get_vm_volumes
($conf);
6158 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
6160 if (my $tpmdrive = $conf->{tpmstate0
}) {
6161 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
6162 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
6164 PVE
::Storage
::unmap_volume
($storecfg, $tpm->{file
});
6169 foreach my $ext (qw(mon qmp pid vnc qga)) {
6170 unlink "/var/run/qemu-server/${vmid}.$ext";
6173 if ($conf->{ivshmem
}) {
6174 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
6175 # just delete it for now, VMs which have this already open do not
6176 # are affected, but new VMs will get a separated one. If this
6177 # becomes an issue we either add some sort of ref-counting or just
6178 # add a "don't delete on stop" flag to the ivshmem format.
6179 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name
} // $vmid);
6182 cleanup_pci_devices
($vmid, $conf);
6184 vmconfig_apply_pending
($vmid, $conf, $storecfg) if $apply_pending_changes;
6186 warn $@ if $@; # avoid errors - just warn
6189 # call only in locked context
6191 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
6193 my $pid = check_running
($vmid, $nocheck);
6198 $conf = PVE
::QemuConfig-
>load_config($vmid);
6199 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
6200 if (!defined($timeout) && $shutdown && $conf->{startup
}) {
6201 my $opts = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
6202 $timeout = $opts->{down
} if $opts->{down
};
6204 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-stop');
6209 if (defined($conf) && get_qga_key
($conf, 'enabled')) {
6210 mon_cmd
($vmid, "guest-shutdown", timeout
=> $timeout);
6212 mon_cmd
($vmid, "system_powerdown");
6215 mon_cmd
($vmid, "quit");
6221 $timeout = 60 if !defined($timeout);
6224 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
6229 if ($count >= $timeout) {
6231 warn "VM still running - terminating now with SIGTERM\n";
6234 die "VM quit/powerdown failed - got timeout\n";
6237 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6241 if (!check_running
($vmid, $nocheck)) {
6242 warn "Unexpected: VM shutdown command failed, but VM not running anymore..\n";
6246 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
6249 die "VM quit/powerdown failed\n";
6257 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
6262 if ($count >= $timeout) {
6263 warn "VM still running - terminating now with SIGKILL\n";
6268 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6271 # Note: use $nocheck to skip tests if VM configuration file exists.
6272 # We need that when migration VMs to other nodes (files already moved)
6273 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
6275 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
6277 $force = 1 if !defined($force) && !$shutdown;
6280 my $pid = check_running
($vmid, $nocheck, $migratedfrom);
6281 kill 15, $pid if $pid;
6282 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migratedfrom);
6283 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 0);
6287 PVE
::QemuConfig-
>lock_config($vmid, sub {
6288 _do_vm_stop
($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
6293 my ($vmid, $timeout) = @_;
6295 PVE
::QemuConfig-
>lock_config($vmid, sub {
6298 # only reboot if running, as qmeventd starts it again on a stop event
6299 return if !check_running
($vmid);
6301 create_reboot_request
($vmid);
6303 my $storecfg = PVE
::Storage
::config
();
6304 _do_vm_stop
($storecfg, $vmid, undef, undef, $timeout, 1);
6308 # avoid that the next normal shutdown will be confused for a reboot
6309 clear_reboot_request
($vmid);
6315 # note: if using the statestorage parameter, the caller has to check privileges
6317 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
6324 PVE
::QemuConfig-
>lock_config($vmid, sub {
6326 $conf = PVE
::QemuConfig-
>load_config($vmid);
6328 my $is_backing_up = PVE
::QemuConfig-
>has_lock($conf, 'backup');
6329 PVE
::QemuConfig-
>check_lock($conf)
6330 if !($skiplock || $is_backing_up);
6332 die "cannot suspend to disk during backup\n"
6333 if $is_backing_up && $includestate;
6335 if ($includestate) {
6336 $conf->{lock} = 'suspending';
6337 my $date = strftime
("%Y-%m-%d", localtime(time()));
6338 $storecfg = PVE
::Storage
::config
();
6339 if (!$statestorage) {
6340 $statestorage = find_vmstate_storage
($conf, $storecfg);
6341 # check permissions for the storage
6342 my $rpcenv = PVE
::RPCEnvironment
::get
();
6343 if ($rpcenv->{type
} ne 'cli') {
6344 my $authuser = $rpcenv->get_user();
6345 $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
6350 $vmstate = PVE
::QemuConfig-
>__snapshot_save_vmstate(
6351 $vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
6352 $path = PVE
::Storage
::path
($storecfg, $vmstate);
6353 PVE
::QemuConfig-
>write_config($vmid, $conf);
6355 mon_cmd
($vmid, "stop");
6359 if ($includestate) {
6361 PVE
::Storage
::activate_volumes
($storecfg, [$vmstate]);
6364 set_migration_caps
($vmid, 1);
6365 mon_cmd
($vmid, "savevm-start", statefile
=> $path);
6367 my $state = mon_cmd
($vmid, "query-savevm");
6368 if (!$state->{status
}) {
6369 die "savevm not active\n";
6370 } elsif ($state->{status
} eq 'active') {
6373 } elsif ($state->{status
} eq 'completed') {
6374 print "State saved, quitting\n";
6376 } elsif ($state->{status
} eq 'failed' && $state->{error
}) {
6377 die "query-savevm failed with error '$state->{error}'\n"
6379 die "query-savevm returned status '$state->{status}'\n";
6385 PVE
::QemuConfig-
>lock_config($vmid, sub {
6386 $conf = PVE
::QemuConfig-
>load_config($vmid);
6388 # cleanup, but leave suspending lock, to indicate something went wrong
6390 mon_cmd
($vmid, "savevm-end");
6391 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
6392 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
6393 delete $conf->@{qw(vmstate runningmachine runningcpu)};
6394 PVE
::QemuConfig-
>write_config($vmid, $conf);
6400 die "lock changed unexpectedly\n"
6401 if !PVE
::QemuConfig-
>has_lock($conf, 'suspending');
6403 mon_cmd
($vmid, "quit");
6404 $conf->{lock} = 'suspended';
6405 PVE
::QemuConfig-
>write_config($vmid, $conf);
6410 # $nocheck is set when called as part of a migration - in this context the
6411 # location of the config file (source or target node) is not deterministic,
6412 # since migration cannot wait for pmxcfs to process the rename
6414 my ($vmid, $skiplock, $nocheck) = @_;
6416 PVE
::QemuConfig-
>lock_config($vmid, sub {
6417 my $res = mon_cmd
($vmid, 'query-status');
6418 my $resume_cmd = 'cont';
6422 $conf = eval { PVE
::QemuConfig-
>load_config($vmid) }; # try on target node
6424 my $vmlist = PVE
::Cluster
::get_vmlist
();
6425 if (exists($vmlist->{ids
}->{$vmid})) {
6426 my $node = $vmlist->{ids
}->{$vmid}->{node
};
6427 $conf = eval { PVE
::QemuConfig-
>load_config($vmid, $node) }; # try on source node
6430 PVE
::Cluster
::cfs_update
(); # vmlist was wrong, invalidate cache
6431 $conf = PVE
::QemuConfig-
>load_config($vmid); # last try on target node again
6435 $conf = PVE
::QemuConfig-
>load_config($vmid);
6438 if ($res->{status
}) {
6439 return if $res->{status
} eq 'running'; # job done, go home
6440 $resume_cmd = 'system_wakeup' if $res->{status
} eq 'suspended';
6441 $reset = 1 if $res->{status
} eq 'shutdown';
6445 PVE
::QemuConfig-
>check_lock($conf)
6446 if !($skiplock || PVE
::QemuConfig-
>has_lock($conf, 'backup'));
6450 # required if a VM shuts down during a backup and we get a resume
6451 # request before the backup finishes for example
6452 mon_cmd
($vmid, "system_reset");
6455 add_nets_bridge_fdb
($conf, $vmid) if $resume_cmd eq 'cont';
6457 mon_cmd
($vmid, $resume_cmd);
6462 my ($vmid, $skiplock, $key) = @_;
6464 PVE
::QemuConfig-
>lock_config($vmid, sub {
6466 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6468 # there is no qmp command, so we use the human monitor command
6469 my $res = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "sendkey $key");
6470 die $res if $res ne '';
6474 sub check_bridge_access
{
6475 my ($rpcenv, $authuser, $conf) = @_;
6477 return 1 if $authuser eq 'root@pam';
6479 for my $opt (sort keys $conf->%*) {
6480 next if $opt !~ m/^net\d+$/;
6481 my $net = parse_net
($conf->{$opt});
6482 my ($bridge, $tag, $trunks) = $net->@{'bridge', 'tag', 'trunks'};
6483 PVE
::GuestHelpers
::check_vnet_access
($rpcenv, $authuser, $bridge, $tag, $trunks);
6488 sub check_mapping_access
{
6489 my ($rpcenv, $user, $conf) = @_;
6491 for my $opt (keys $conf->%*) {
6492 if ($opt =~ m/^usb\d+$/) {
6493 my $device = PVE
::JSONSchema
::parse_property_string
('pve-qm-usb', $conf->{$opt});
6494 if (my $host = $device->{host
}) {
6495 die "only root can set '$opt' config for real devices\n"
6496 if $host !~ m/^spice$/i && $user ne 'root@pam';
6497 } elsif ($device->{mapping
}) {
6498 $rpcenv->check_full($user, "/mapping/usb/$device->{mapping}", ['Mapping.Use']);
6500 die "either 'host' or 'mapping' must be set.\n";
6502 } elsif ($opt =~ m/^hostpci\d+$/) {
6503 my $device = PVE
::JSONSchema
::parse_property_string
('pve-qm-hostpci', $conf->{$opt});
6504 if ($device->{host
}) {
6505 die "only root can set '$opt' config for non-mapped devices\n" if $user ne 'root@pam';
6506 } elsif ($device->{mapping
}) {
6507 $rpcenv->check_full($user, "/mapping/pci/$device->{mapping}", ['Mapping.Use']);
6509 die "either 'host' or 'mapping' must be set.\n";
6515 sub check_restore_permissions
{
6516 my ($rpcenv, $user, $conf) = @_;
6518 check_bridge_access
($rpcenv, $user, $conf);
6519 check_mapping_access
($rpcenv, $user, $conf);
6521 # vzdump restore implementaion
6523 sub tar_archive_read_firstfile
{
6524 my $archive = shift;
6526 die "ERROR: file '$archive' does not exist\n" if ! -f
$archive;
6528 # try to detect archive type first
6529 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
6530 die "unable to open file '$archive'\n";
6531 my $firstfile = <$fh>;
6535 die "ERROR: archive contaions no data\n" if !$firstfile;
6541 sub tar_restore_cleanup
{
6542 my ($storecfg, $statfile) = @_;
6544 print STDERR
"starting cleanup\n";
6546 if (my $fd = IO
::File-
>new($statfile, "r")) {
6547 while (defined(my $line = <$fd>)) {
6548 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6551 if ($volid =~ m
|^/|) {
6552 unlink $volid || die 'unlink failed\n';
6554 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6556 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6558 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6560 print STDERR
"unable to parse line in statfile - $line";
6567 sub restore_file_archive
{
6568 my ($archive, $vmid, $user, $opts) = @_;
6570 return restore_vma_archive
($archive, $vmid, $user, $opts)
6573 my $info = PVE
::Storage
::archive_info
($archive);
6574 my $format = $opts->{format
} // $info->{format
};
6575 my $comp = $info->{compression
};
6577 # try to detect archive format
6578 if ($format eq 'tar') {
6579 return restore_tar_archive
($archive, $vmid, $user, $opts);
6581 return restore_vma_archive
($archive, $vmid, $user, $opts, $comp);
6585 # hepler to remove disks that will not be used after restore
6586 my $restore_cleanup_oldconf = sub {
6587 my ($storecfg, $vmid, $oldconf, $virtdev_hash) = @_;
6589 my $kept_disks = {};
6591 PVE
::QemuConfig-
>foreach_volume($oldconf, sub {
6592 my ($ds, $drive) = @_;
6594 return if drive_is_cdrom
($drive, 1);
6596 my $volid = $drive->{file
};
6597 return if !$volid || $volid =~ m
|^/|;
6599 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
6600 return if !$path || !$owner || ($owner != $vmid);
6602 # Note: only delete disk we want to restore
6603 # other volumes will become unused
6604 if ($virtdev_hash->{$ds}) {
6605 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
6610 $kept_disks->{$volid} = 1;
6614 # after the restore we have no snapshots anymore
6615 for my $snapname (keys $oldconf->{snapshots
}->%*) {
6616 my $snap = $oldconf->{snapshots
}->{$snapname};
6617 if ($snap->{vmstate
}) {
6618 eval { PVE
::Storage
::vdisk_free
($storecfg, $snap->{vmstate
}); };
6624 for my $volid (keys $kept_disks->%*) {
6625 eval { PVE
::Storage
::volume_snapshot_delete
($storecfg, $volid, $snapname); };
6631 # Helper to parse vzdump backup device hints
6633 # $rpcenv: Environment, used to ckeck storage permissions
6634 # $user: User ID, to check storage permissions
6635 # $storecfg: Storage configuration
6636 # $fh: the file handle for reading the configuration
6637 # $devinfo: should contain device sizes for all backu-up'ed devices
6638 # $options: backup options (pool, default storage)
6640 # Return: $virtdev_hash, updates $devinfo (add devname, virtdev, format, storeid)
6641 my $parse_backup_hints = sub {
6642 my ($rpcenv, $user, $storecfg, $fh, $devinfo, $options) = @_;
6644 my $check_storage = sub { # assert if an image can be allocate
6645 my ($storeid, $scfg) = @_;
6646 die "Content type 'images' is not available on storage '$storeid'\n"
6647 if !$scfg->{content
}->{images
};
6648 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace'])
6649 if $user ne 'root@pam';
6652 my $virtdev_hash = {};
6653 while (defined(my $line = <$fh>)) {
6654 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
6655 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
6656 die "archive does not contain data for drive '$virtdev'\n"
6657 if !$devinfo->{$devname};
6659 if (defined($options->{storage
})) {
6660 $storeid = $options->{storage
} || 'local';
6661 } elsif (!$storeid) {
6664 $format = 'raw' if !$format;
6665 $devinfo->{$devname}->{devname
} = $devname;
6666 $devinfo->{$devname}->{virtdev
} = $virtdev;
6667 $devinfo->{$devname}->{format
} = $format;
6668 $devinfo->{$devname}->{storeid
} = $storeid;
6670 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6671 $check_storage->($storeid, $scfg); # permission and content type check
6673 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
6674 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
6676 my $drive = parse_drive
($virtdev, $2);
6678 if (drive_is_cloudinit
($drive)) {
6679 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
6680 $storeid = $options->{storage
} if defined ($options->{storage
});
6681 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6682 my $format = qemu_img_format
($scfg, $volname); # has 'raw' fallback
6684 $check_storage->($storeid, $scfg); # permission and content type check
6686 $virtdev_hash->{$virtdev} = {
6688 storeid
=> $storeid,
6689 size
=> PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
,
6696 return $virtdev_hash;
6699 # Helper to allocate and activate all volumes required for a restore
6701 # $storecfg: Storage configuration
6702 # $virtdev_hash: as returned by parse_backup_hints()
6704 # Returns: { $virtdev => $volid }
6705 my $restore_allocate_devices = sub {
6706 my ($storecfg, $virtdev_hash, $vmid) = @_;
6709 foreach my $virtdev (sort keys %$virtdev_hash) {
6710 my $d = $virtdev_hash->{$virtdev};
6711 my $alloc_size = int(($d->{size
} + 1024 - 1)/1024);
6712 my $storeid = $d->{storeid
};
6713 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6715 # test if requested format is supported
6716 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
6717 my $supported = grep { $_ eq $d->{format
} } @$validFormats;
6718 $d->{format
} = $defFormat if !$supported;
6721 if ($d->{is_cloudinit
}) {
6722 $name = "vm-$vmid-cloudinit";
6723 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6724 if ($scfg->{path
}) {
6725 $name .= ".$d->{format}";
6729 my $volid = PVE
::Storage
::vdisk_alloc
(
6730 $storecfg, $storeid, $vmid, $d->{format
}, $name, $alloc_size);
6732 print STDERR
"new volume ID is '$volid'\n";
6733 $d->{volid
} = $volid;
6735 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
6737 $map->{$virtdev} = $volid;
6743 sub restore_update_config_line
{
6744 my ($cookie, $map, $line, $unique) = @_;
6746 return '' if $line =~ m/^\#qmdump\#/;
6747 return '' if $line =~ m/^\#vzdump\#/;
6748 return '' if $line =~ m/^lock:/;
6749 return '' if $line =~ m/^unused\d+:/;
6750 return '' if $line =~ m/^parent:/;
6754 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
6755 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
6756 # try to convert old 1.X settings
6757 my ($id, $ind, $ethcfg) = ($1, $2, $3);
6758 foreach my $devconfig (PVE
::Tools
::split_list
($ethcfg)) {
6759 my ($model, $macaddr) = split(/\=/, $devconfig);
6760 $macaddr = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if !$macaddr || $unique;
6763 bridge
=> "vmbr$ind",
6764 macaddr
=> $macaddr,
6766 my $netstr = print_net
($net);
6768 $res .= "net$cookie->{netcount}: $netstr\n";
6769 $cookie->{netcount
}++;
6771 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
6772 my ($id, $netstr) = ($1, $2);
6773 my $net = parse_net
($netstr);
6774 $net->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if $net->{macaddr
};
6775 $netstr = print_net
($net);
6776 $res .= "$id: $netstr\n";
6777 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk|tpmstate)\d+):\s*(\S+)\s*$/) {
6780 my $di = parse_drive
($virtdev, $value);
6781 if (defined($di->{backup
}) && !$di->{backup
}) {
6783 } elsif ($map->{$virtdev}) {
6784 delete $di->{format
}; # format can change on restore
6785 $di->{file
} = $map->{$virtdev};
6786 $value = print_drive
($di);
6787 $res .= "$virtdev: $value\n";
6791 } elsif (($line =~ m/^vmgenid: (.*)/)) {
6793 if ($vmgenid ne '0') {
6794 # always generate a new vmgenid if there was a valid one setup
6795 $vmgenid = generate_uuid
();
6797 $res .= "vmgenid: $vmgenid\n";
6798 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
6799 my ($uuid, $uuid_str);
6800 UUID
::generate
($uuid);
6801 UUID
::unparse
($uuid, $uuid_str);
6802 my $smbios1 = parse_smbios1
($2);
6803 $smbios1->{uuid
} = $uuid_str;
6804 $res .= $1.print_smbios1
($smbios1)."\n";
6812 my $restore_deactivate_volumes = sub {
6813 my ($storecfg, $virtdev_hash) = @_;
6816 for my $dev (values $virtdev_hash->%*) {
6817 push $vollist->@*, $dev->{volid
} if $dev->{volid
};
6820 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
6821 print STDERR
$@ if $@;
6824 my $restore_destroy_volumes = sub {
6825 my ($storecfg, $virtdev_hash) = @_;
6827 for my $dev (values $virtdev_hash->%*) {
6828 my $volid = $dev->{volid
} or next;
6830 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6831 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6833 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6837 sub restore_merge_config
{
6838 my ($filename, $backup_conf_raw, $override_conf) = @_;
6840 my $backup_conf = parse_vm_config
($filename, $backup_conf_raw);
6841 for my $key (keys $override_conf->%*) {
6842 $backup_conf->{$key} = $override_conf->{$key};
6845 return $backup_conf;
6849 my ($cfg, $vmid) = @_;
6851 my $info = PVE
::Storage
::vdisk_list
($cfg, undef, $vmid, undef, 'images');
6853 my $volid_hash = {};
6854 foreach my $storeid (keys %$info) {
6855 foreach my $item (@{$info->{$storeid}}) {
6856 next if !($item->{volid
} && $item->{size
});
6857 $item->{path
} = PVE
::Storage
::path
($cfg, $item->{volid
});
6858 $volid_hash->{$item->{volid
}} = $item;
6865 sub update_disk_config
{
6866 my ($vmid, $conf, $volid_hash) = @_;
6869 my $prefix = "VM $vmid";
6871 # used and unused disks
6872 my $referenced = {};
6874 # Note: it is allowed to define multiple storages with same path (alias), so
6875 # we need to check both 'volid' and real 'path' (two different volid can point
6876 # to the same path).
6878 my $referencedpath = {};
6881 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6882 my ($opt, $drive) = @_;
6884 my $volid = $drive->{file
};
6886 my $volume = $volid_hash->{$volid};
6888 # mark volid as "in-use" for next step
6889 $referenced->{$volid} = 1;
6890 if ($volume && (my $path = $volume->{path
})) {
6891 $referencedpath->{$path} = 1;
6894 return if drive_is_cdrom
($drive);
6897 my ($updated, $msg) = PVE
::QemuServer
::Drive
::update_disksize
($drive, $volume->{size
});
6898 if (defined($updated)) {
6900 $conf->{$opt} = print_drive
($updated);
6901 print "$prefix ($opt): $msg\n";
6905 # remove 'unusedX' entry if volume is used
6906 PVE
::QemuConfig-
>foreach_unused_volume($conf, sub {
6907 my ($opt, $drive) = @_;
6909 my $volid = $drive->{file
};
6913 $path = $volid_hash->{$volid}->{path
} if $volid_hash->{$volid};
6914 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6915 print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
6917 delete $conf->{$opt};
6920 $referenced->{$volid} = 1;
6921 $referencedpath->{$path} = 1 if $path;
6924 foreach my $volid (sort keys %$volid_hash) {
6925 next if $volid =~ m/vm-$vmid-state-/;
6926 next if $referenced->{$volid};
6927 my $path = $volid_hash->{$volid}->{path
};
6928 next if !$path; # just to be sure
6929 next if $referencedpath->{$path};
6931 my $key = PVE
::QemuConfig-
>add_unused_volume($conf, $volid);
6932 print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
6933 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6940 my ($vmid, $nolock, $dryrun) = @_;
6942 my $cfg = PVE
::Storage
::config
();
6944 print "rescan volumes...\n";
6945 my $volid_hash = scan_volids
($cfg, $vmid);
6947 my $updatefn = sub {
6950 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6952 PVE
::QemuConfig-
>check_lock($conf);
6955 foreach my $volid (keys %$volid_hash) {
6956 my $info = $volid_hash->{$volid};
6957 $vm_volids->{$volid} = $info if $info->{vmid
} && $info->{vmid
} == $vmid;
6960 my $changes = update_disk_config
($vmid, $conf, $vm_volids);
6962 PVE
::QemuConfig-
>write_config($vmid, $conf) if $changes && !$dryrun;
6965 if (defined($vmid)) {
6969 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6972 my $vmlist = config_list
();
6973 foreach my $vmid (keys %$vmlist) {
6977 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6983 sub restore_proxmox_backup_archive
{
6984 my ($archive, $vmid, $user, $options) = @_;
6986 my $storecfg = PVE
::Storage
::config
();
6988 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($archive);
6989 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6991 my $fingerprint = $scfg->{fingerprint
};
6992 my $keyfile = PVE
::Storage
::PBSPlugin
::pbs_encryption_key_file_name
($storecfg, $storeid);
6994 my $repo = PVE
::PBSClient
::get_repository
($scfg);
6995 my $namespace = $scfg->{namespace
};
6997 # This is only used for `pbs-restore` and the QEMU PBS driver (live-restore)
6998 my $password = PVE
::Storage
::PBSPlugin
::pbs_get_password
($scfg, $storeid);
6999 local $ENV{PBS_PASSWORD
} = $password;
7000 local $ENV{PBS_FINGERPRINT
} = $fingerprint if defined($fingerprint);
7002 my ($vtype, $pbs_backup_name, undef, undef, undef, undef, $format) =
7003 PVE
::Storage
::parse_volname
($storecfg, $archive);
7005 die "got unexpected vtype '$vtype'\n" if $vtype ne 'backup';
7007 die "got unexpected backup format '$format'\n" if $format ne 'pbs-vm';
7009 my $tmpdir = "/var/tmp/vzdumptmp$$";
7013 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7014 # disable interrupts (always do cleanups)
7018 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
7020 # Note: $oldconf is undef if VM does not exists
7021 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
7022 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
7023 my $new_conf_raw = '';
7025 my $rpcenv = PVE
::RPCEnvironment
::get
();
7026 my $devinfo = {}; # info about drives included in backup
7027 my $virtdev_hash = {}; # info about allocated drives
7035 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7037 my $cfgfn = "$tmpdir/qemu-server.conf";
7038 my $firewall_config_fn = "$tmpdir/fw.conf";
7039 my $index_fn = "$tmpdir/index.json";
7041 my $cmd = "restore";
7043 my $param = [$pbs_backup_name, "index.json", $index_fn];
7044 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7045 my $index = PVE
::Tools
::file_get_contents
($index_fn);
7046 $index = decode_json
($index);
7048 foreach my $info (@{$index->{files
}}) {
7049 if ($info->{filename
} =~ m/^(drive-\S+).img.fidx$/) {
7051 if ($info->{size
} =~ m/^(\d+)$/) { # untaint size
7052 $devinfo->{$devname}->{size
} = $1;
7054 die "unable to parse file size in 'index.json' - got '$info->{size}'\n";
7059 my $is_qemu_server_backup = scalar(
7060 grep { $_->{filename
} eq 'qemu-server.conf.blob' } @{$index->{files
}}
7062 if (!$is_qemu_server_backup) {
7063 die "backup does not look like a qemu-server backup (missing 'qemu-server.conf' file)\n";
7065 my $has_firewall_config = scalar(grep { $_->{filename
} eq 'fw.conf.blob' } @{$index->{files
}});
7067 $param = [$pbs_backup_name, "qemu-server.conf", $cfgfn];
7068 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7070 if ($has_firewall_config) {
7071 $param = [$pbs_backup_name, "fw.conf", $firewall_config_fn];
7072 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7074 my $pve_firewall_dir = '/etc/pve/firewall';
7075 mkdir $pve_firewall_dir; # make sure the dir exists
7076 PVE
::Tools
::file_copy
($firewall_config_fn, "${pve_firewall_dir}/$vmid.fw");
7079 my $fh = IO
::File-
>new($cfgfn, "r") ||
7080 die "unable to read qemu-server.conf - $!\n";
7082 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $storecfg, $fh, $devinfo, $options);
7084 # fixme: rate limit?
7086 # create empty/temp config
7087 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\nlock: create");
7089 $restore_cleanup_oldconf->($storecfg, $vmid, $oldconf, $virtdev_hash) if $oldconf;
7092 my $map = $restore_allocate_devices->($storecfg, $virtdev_hash, $vmid);
7094 foreach my $virtdev (sort keys %$virtdev_hash) {
7095 my $d = $virtdev_hash->{$virtdev};
7096 next if $d->{is_cloudinit
}; # no need to restore cloudinit
7098 # this fails if storage is unavailable
7099 my $volid = $d->{volid
};
7100 my $path = PVE
::Storage
::path
($storecfg, $volid);
7102 # for live-restore we only want to preload the efidisk and TPM state
7103 next if $options->{live
} && $virtdev ne 'efidisk0' && $virtdev ne 'tpmstate0';
7106 if (defined(my $ns = $scfg->{namespace
})) {
7107 @ns_arg = ('--ns', $ns);
7110 my $pbs_restore_cmd = [
7111 '/usr/bin/pbs-restore',
7112 '--repository', $repo,
7115 "$d->{devname}.img.fidx",
7120 push @$pbs_restore_cmd, '--format', $d->{format
} if $d->{format
};
7121 push @$pbs_restore_cmd, '--keyfile', $keyfile if -e
$keyfile;
7123 if (PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $volid)) {
7124 push @$pbs_restore_cmd, '--skip-zero';
7127 my $dbg_cmdstring = PVE
::Tools
::cmd2string
($pbs_restore_cmd);
7128 print "restore proxmox backup image: $dbg_cmdstring\n";
7129 run_command
($pbs_restore_cmd);
7132 $fh->seek(0, 0) || die "seek failed - $!\n";
7134 my $cookie = { netcount
=> 0 };
7135 while (defined(my $line = <$fh>)) {
7136 $new_conf_raw .= restore_update_config_line
(
7148 if ($err || !$options->{live
}) {
7149 $restore_deactivate_volumes->($storecfg, $virtdev_hash);
7155 $restore_destroy_volumes->($storecfg, $virtdev_hash);
7159 if ($options->{live
}) {
7160 # keep lock during live-restore
7161 $new_conf_raw .= "\nlock: create";
7164 my $new_conf = restore_merge_config
($conffile, $new_conf_raw, $options->{override_conf
});
7165 check_restore_permissions
($rpcenv, $user, $new_conf);
7166 PVE
::QemuConfig-
>write_config($vmid, $new_conf);
7168 eval { rescan
($vmid, 1); };
7171 PVE
::AccessControl
::add_vm_to_pool
($vmid, $options->{pool
}) if $options->{pool
};
7173 if ($options->{live
}) {
7179 local $SIG{PIPE
} = sub { die "got signal ($!) - abort\n"; };
7181 my $conf = PVE
::QemuConfig-
>load_config($vmid);
7182 die "cannot do live-restore for template\n" if PVE
::QemuConfig-
>is_template($conf);
7184 # these special drives are already restored before start
7185 delete $devinfo->{'drive-efidisk0'};
7186 delete $devinfo->{'drive-tpmstate0-backup'};
7190 keyfile
=> $keyfile,
7191 snapshot
=> $pbs_backup_name,
7192 namespace
=> $namespace,
7194 pbs_live_restore
($vmid, $conf, $storecfg, $devinfo, $pbs_opts);
7196 PVE
::QemuConfig-
>remove_lock($vmid, "create");
7200 sub pbs_live_restore
{
7201 my ($vmid, $conf, $storecfg, $restored_disks, $opts) = @_;
7203 print "starting VM for live-restore\n";
7204 print "repository: '$opts->{repo}', snapshot: '$opts->{snapshot}'\n";
7206 my $pbs_backing = {};
7207 for my $ds (keys %$restored_disks) {
7208 $ds =~ m/^drive-(.*)$/;
7210 $pbs_backing->{$confname} = {
7211 repository
=> $opts->{repo
},
7212 snapshot
=> $opts->{snapshot
},
7213 archive
=> "$ds.img.fidx",
7215 $pbs_backing->{$confname}->{keyfile
} = $opts->{keyfile
} if -e
$opts->{keyfile
};
7216 $pbs_backing->{$confname}->{namespace
} = $opts->{namespace
} if defined($opts->{namespace
});
7218 my $drive = parse_drive
($confname, $conf->{$confname});
7219 print "restoring '$ds' to '$drive->{file}'\n";
7222 my $drives_streamed = 0;
7224 # make sure HA doesn't interrupt our restore by stopping the VM
7225 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid)) {
7226 run_command
(['ha-manager', 'set', "vm:$vmid", '--state', 'started']);
7229 # start VM with backing chain pointing to PBS backup, environment vars for PBS driver
7230 # in QEMU (PBS_PASSWORD and PBS_FINGERPRINT) are already set by our caller
7231 vm_start_nolock
($storecfg, $vmid, $conf, {paused
=> 1, 'pbs-backing' => $pbs_backing}, {});
7233 my $qmeventd_fd = register_qmeventd_handle
($vmid);
7235 # begin streaming, i.e. data copy from PBS to target disk for every vol,
7236 # this will effectively collapse the backing image chain consisting of
7237 # [target <- alloc-track -> PBS snapshot] to just [target] (alloc-track
7238 # removes itself once all backing images vanish with 'auto-remove=on')
7240 for my $ds (sort keys %$restored_disks) {
7241 my $job_id = "restore-$ds";
7242 mon_cmd
($vmid, 'block-stream',
7243 'job-id' => $job_id,
7246 $jobs->{$job_id} = {};
7249 mon_cmd
($vmid, 'cont');
7250 qemu_drive_mirror_monitor
($vmid, undef, $jobs, 'auto', 0, 'stream');
7252 print "restore-drive jobs finished successfully, removing all tracking block devices"
7253 ." to disconnect from Proxmox Backup Server\n";
7255 for my $ds (sort keys %$restored_disks) {
7256 mon_cmd
($vmid, 'blockdev-del', 'node-name' => "$ds-pbs");
7259 close($qmeventd_fd);
7265 warn "An error occurred during live-restore: $err\n";
7266 _do_vm_stop
($storecfg, $vmid, 1, 1, 10, 0, 1);
7267 die "live-restore failed\n";
7271 sub restore_vma_archive
{
7272 my ($archive, $vmid, $user, $opts, $comp) = @_;
7274 my $readfrom = $archive;
7276 my $cfg = PVE
::Storage
::config
();
7278 my $bwlimit = $opts->{bwlimit
};
7280 my $dbg_cmdstring = '';
7281 my $add_pipe = sub {
7283 push @$commands, $cmd;
7284 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
7285 $dbg_cmdstring .= PVE
::Tools
::cmd2string
($cmd);
7290 if ($archive eq '-') {
7293 # If we use a backup from a PVE defined storage we also consider that
7294 # storage's rate limit:
7295 my (undef, $volid) = PVE
::Storage
::path_to_volume_id
($cfg, $archive);
7296 if (defined($volid)) {
7297 my ($sid, undef) = PVE
::Storage
::parse_volume_id
($volid);
7298 my $readlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [$sid], $bwlimit);
7300 print STDERR
"applying read rate limit: $readlimit\n";
7301 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
7302 $add_pipe->($cstream);
7308 my $info = PVE
::Storage
::decompressor_info
('vma', $comp);
7309 my $cmd = $info->{decompressor
};
7310 push @$cmd, $readfrom;
7314 my $tmpdir = "/var/tmp/vzdumptmp$$";
7317 # disable interrupts (always do cleanups)
7321 local $SIG{HUP
} = sub { warn "got interrupt - ignored\n"; };
7323 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
7324 POSIX
::mkfifo
($mapfifo, 0600);
7326 my $openfifo = sub { open($fifofh, '>', $mapfifo) or die $! };
7328 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
7330 my $devinfo = {}; # info about drives included in backup
7331 my $virtdev_hash = {}; # info about allocated drives
7333 my $rpcenv = PVE
::RPCEnvironment
::get
();
7335 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7337 # Note: $oldconf is undef if VM does not exist
7338 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
7339 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
7340 my $new_conf_raw = '';
7344 my $print_devmap = sub {
7345 my $cfgfn = "$tmpdir/qemu-server.conf";
7347 # we can read the config - that is already extracted
7348 my $fh = IO
::File-
>new($cfgfn, "r") ||
7349 die "unable to read qemu-server.conf - $!\n";
7351 my $fwcfgfn = "$tmpdir/qemu-server.fw";
7353 my $pve_firewall_dir = '/etc/pve/firewall';
7354 mkdir $pve_firewall_dir; # make sure the dir exists
7355 PVE
::Tools
::file_copy
($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
7358 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $cfg, $fh, $devinfo, $opts);
7360 foreach my $info (values %{$virtdev_hash}) {
7361 my $storeid = $info->{storeid
};
7362 next if defined($storage_limits{$storeid});
7364 my $limit = PVE
::Storage
::get_bandwidth_limit
('restore', [$storeid], $bwlimit) // 0;
7365 print STDERR
"rate limit for storage $storeid: $limit KiB/s\n" if $limit;
7366 $storage_limits{$storeid} = $limit * 1024;
7369 foreach my $devname (keys %$devinfo) {
7370 die "found no device mapping information for device '$devname'\n"
7371 if !$devinfo->{$devname}->{virtdev
};
7374 # create empty/temp config
7376 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\n");
7377 $restore_cleanup_oldconf->($cfg, $vmid, $oldconf, $virtdev_hash);
7381 my $map = $restore_allocate_devices->($cfg, $virtdev_hash, $vmid);
7383 # print restore information to $fifofh
7384 foreach my $virtdev (sort keys %$virtdev_hash) {
7385 my $d = $virtdev_hash->{$virtdev};
7386 next if $d->{is_cloudinit
}; # no need to restore cloudinit
7388 my $storeid = $d->{storeid
};
7389 my $volid = $d->{volid
};
7392 if (my $limit = $storage_limits{$storeid}) {
7393 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
7396 my $write_zeros = 1;
7397 if (PVE
::Storage
::volume_has_feature
($cfg, 'sparseinit', $volid)) {
7401 my $path = PVE
::Storage
::path
($cfg, $volid);
7403 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
7405 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
7408 $fh->seek(0, 0) || die "seek failed - $!\n";
7410 my $cookie = { netcount
=> 0 };
7411 while (defined(my $line = <$fh>)) {
7412 $new_conf_raw .= restore_update_config_line
(
7431 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7432 local $SIG{ALRM
} = sub { die "got timeout\n"; };
7434 $oldtimeout = alarm(5); # for reading the VMA header - might hang with a corrupted one
7441 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
7442 my ($dev_id, $size, $devname) = ($1, $2, $3);
7443 $devinfo->{$devname} = { size
=> $size, dev_id
=> $dev_id };
7444 } elsif ($line =~ m/^CTIME: /) {
7445 # we correctly received the vma config, so we can disable
7446 # the timeout now for disk allocation
7447 alarm($oldtimeout || 0);
7448 $oldtimeout = undef;
7450 print $fifofh "done\n";
7456 print "restore vma archive: $dbg_cmdstring\n";
7457 run_command
($commands, input
=> $input, outfunc
=> $parser, afterfork
=> $openfifo);
7461 alarm($oldtimeout) if $oldtimeout;
7463 $restore_deactivate_volumes->($cfg, $virtdev_hash);
7465 close($fifofh) if $fifofh;
7470 $restore_destroy_volumes->($cfg, $virtdev_hash);
7474 my $new_conf = restore_merge_config
($conffile, $new_conf_raw, $opts->{override_conf
});
7475 check_restore_permissions
($rpcenv, $user, $new_conf);
7476 PVE
::QemuConfig-
>write_config($vmid, $new_conf);
7478 eval { rescan
($vmid, 1); };
7481 PVE
::AccessControl
::add_vm_to_pool
($vmid, $opts->{pool
}) if $opts->{pool
};
7484 sub restore_tar_archive
{
7485 my ($archive, $vmid, $user, $opts) = @_;
7487 if (scalar(keys $opts->{override_conf
}->%*) > 0) {
7488 my $keystring = join(' ', keys $opts->{override_conf
}->%*);
7489 die "cannot pass along options ($keystring) when restoring from tar archive\n";
7492 if ($archive ne '-') {
7493 my $firstfile = tar_archive_read_firstfile
($archive);
7494 die "ERROR: file '$archive' does not look like a QemuServer vzdump backup\n"
7495 if $firstfile ne 'qemu-server.conf';
7498 my $storecfg = PVE
::Storage
::config
();
7500 # avoid zombie disks when restoring over an existing VM -> cleanup first
7501 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
7502 # skiplock=1 because qmrestore has set the 'create' lock itself already
7503 my $vmcfgfn = PVE
::QemuConfig-
>config_file($vmid);
7504 destroy_vm
($storecfg, $vmid, 1, { lock => 'restore' }) if -f
$vmcfgfn;
7506 my $tocmd = "/usr/lib/qemu-server/qmextract";
7508 $tocmd .= " --storage " . PVE
::Tools
::shellquote
($opts->{storage
}) if $opts->{storage
};
7509 $tocmd .= " --pool " . PVE
::Tools
::shellquote
($opts->{pool
}) if $opts->{pool
};
7510 $tocmd .= ' --prealloc' if $opts->{prealloc
};
7511 $tocmd .= ' --info' if $opts->{info
};
7513 # tar option "xf" does not autodetect compression when read from STDIN,
7514 # so we pipe to zcat
7515 my $cmd = "zcat -f|tar xf " . PVE
::Tools
::shellquote
($archive) . " " .
7516 PVE
::Tools
::shellquote
("--to-command=$tocmd");
7518 my $tmpdir = "/var/tmp/vzdumptmp$$";
7521 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
7522 local $ENV{VZDUMP_VMID
} = $vmid;
7523 local $ENV{VZDUMP_USER
} = $user;
7525 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7526 my $new_conf_raw = '';
7528 # disable interrupts (always do cleanups)
7532 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
7540 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7542 if ($archive eq '-') {
7543 print "extracting archive from STDIN\n";
7544 run_command
($cmd, input
=> "<&STDIN");
7546 print "extracting archive '$archive'\n";
7550 return if $opts->{info
};
7554 my $statfile = "$tmpdir/qmrestore.stat";
7555 if (my $fd = IO
::File-
>new($statfile, "r")) {
7556 while (defined (my $line = <$fd>)) {
7557 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
7558 $map->{$1} = $2 if $1;
7560 print STDERR
"unable to parse line in statfile - $line\n";
7566 my $confsrc = "$tmpdir/qemu-server.conf";
7568 my $srcfd = IO
::File-
>new($confsrc, "r") || die "unable to open file '$confsrc'\n";
7570 my $cookie = { netcount
=> 0 };
7571 while (defined (my $line = <$srcfd>)) {
7572 $new_conf_raw .= restore_update_config_line
(
7583 tar_restore_cleanup
($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info
};
7589 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
7591 PVE
::Cluster
::cfs_update
(); # make sure we read new file
7593 eval { rescan
($vmid, 1); };
7597 sub foreach_storage_used_by_vm
{
7598 my ($conf, $func) = @_;
7602 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7603 my ($ds, $drive) = @_;
7604 return if drive_is_cdrom
($drive);
7606 my $volid = $drive->{file
};
7608 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
7609 $sidhash->{$sid} = $sid if $sid;
7612 foreach my $sid (sort keys %$sidhash) {
7617 my $qemu_snap_storage = {
7620 sub do_snapshots_with_qemu
{
7621 my ($storecfg, $volid, $deviceid) = @_;
7623 return if $deviceid =~ m/tpmstate0/;
7625 my $storage_name = PVE
::Storage
::parse_volume_id
($volid);
7626 my $scfg = $storecfg->{ids
}->{$storage_name};
7627 die "could not find storage '$storage_name'\n" if !defined($scfg);
7629 if ($qemu_snap_storage->{$scfg->{type
}} && !$scfg->{krbd
}){
7633 if ($volid =~ m/\.(qcow2|qed)$/){
7640 sub qga_check_running
{
7641 my ($vmid, $nowarn) = @_;
7643 eval { mon_cmd
($vmid, "guest-ping", timeout
=> 3); };
7645 warn "QEMU Guest Agent is not running - $@" if !$nowarn;
7651 sub template_create
{
7652 my ($vmid, $conf, $disk) = @_;
7654 my $storecfg = PVE
::Storage
::config
();
7656 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7657 my ($ds, $drive) = @_;
7659 return if drive_is_cdrom
($drive);
7660 return if $disk && $ds ne $disk;
7662 my $volid = $drive->{file
};
7663 return if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
7665 my $voliddst = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
7666 $drive->{file
} = $voliddst;
7667 $conf->{$ds} = print_drive
($drive);
7668 PVE
::QemuConfig-
>write_config($vmid, $conf);
7672 sub convert_iscsi_path
{
7675 if ($path =~ m
|^iscsi
://([^/]+)/([^/]+)/(.+)$|) {
7680 my $initiator_name = get_initiator_name
();
7682 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
7683 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
7686 die "cannot convert iscsi path '$path', unkown format\n";
7689 sub qemu_img_convert
{
7690 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized, $bwlimit) = @_;
7692 my $storecfg = PVE
::Storage
::config
();
7693 my ($src_storeid, $src_volname) = PVE
::Storage
::parse_volume_id
($src_volid, 1);
7694 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid, 1);
7696 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
7700 my $src_is_iscsi = 0;
7704 PVE
::Storage
::activate_volumes
($storecfg, [$src_volid], $snapname);
7705 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
7706 $src_format = qemu_img_format
($src_scfg, $src_volname);
7707 $src_path = PVE
::Storage
::path
($storecfg, $src_volid, $snapname);
7708 $src_is_iscsi = ($src_path =~ m
|^iscsi
://|);
7709 $cachemode = 'none' if $src_scfg->{type
} eq 'zfspool';
7710 } elsif (-f
$src_volid || -b
$src_volid) {
7711 $src_path = $src_volid;
7712 if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7717 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
7719 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7720 my $dst_format = qemu_img_format
($dst_scfg, $dst_volname);
7721 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7722 my $dst_is_iscsi = ($dst_path =~ m
|^iscsi
://|);
7725 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
7726 push @$cmd, '-l', "snapshot.name=$snapname"
7727 if $snapname && $src_format && $src_format eq "qcow2";
7728 push @$cmd, '-t', 'none' if $dst_scfg->{type
} eq 'zfspool';
7729 push @$cmd, '-T', $cachemode if defined($cachemode);
7730 push @$cmd, '-r', "${bwlimit}K" if defined($bwlimit);
7732 if ($src_is_iscsi) {
7733 push @$cmd, '--image-opts';
7734 $src_path = convert_iscsi_path
($src_path);
7735 } elsif ($src_format) {
7736 push @$cmd, '-f', $src_format;
7739 if ($dst_is_iscsi) {
7740 push @$cmd, '--target-image-opts';
7741 $dst_path = convert_iscsi_path
($dst_path);
7743 push @$cmd, '-O', $dst_format;
7746 push @$cmd, $src_path;
7748 if (!$dst_is_iscsi && $is_zero_initialized) {
7749 push @$cmd, "zeroinit:$dst_path";
7751 push @$cmd, $dst_path;
7756 if($line =~ m/\((\S+)\/100\
%\)/){
7758 my $transferred = int($size * $percent / 100);
7759 my $total_h = render_bytes
($size, 1);
7760 my $transferred_h = render_bytes
($transferred, 1);
7762 print "transferred $transferred_h of $total_h ($percent%)\n";
7767 eval { run_command
($cmd, timeout
=> undef, outfunc
=> $parser); };
7769 die "copy failed: $err" if $err;
7772 sub qemu_img_format
{
7773 my ($scfg, $volname) = @_;
7775 if ($scfg->{path
} && $volname =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7782 sub qemu_drive_mirror
{
7783 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
7785 $jobs = {} if !$jobs;
7789 $jobs->{"drive-$drive"} = {};
7791 if ($dst_volid =~ /^nbd:/) {
7792 $qemu_target = $dst_volid;
7795 my $storecfg = PVE
::Storage
::config
();
7796 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid);
7798 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7800 $format = qemu_img_format
($dst_scfg, $dst_volname);
7802 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7804 $qemu_target = $is_zero_initialized ?
"zeroinit:$dst_path" : $dst_path;
7807 my $opts = { timeout
=> 10, device
=> "drive-$drive", mode
=> "existing", sync
=> "full", target
=> $qemu_target };
7808 $opts->{format
} = $format if $format;
7810 if (defined($src_bitmap)) {
7811 $opts->{sync
} = 'incremental';
7812 $opts->{bitmap
} = $src_bitmap;
7813 print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
7816 if (defined($bwlimit)) {
7817 $opts->{speed
} = $bwlimit * 1024;
7818 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
7820 print "drive mirror is starting for drive-$drive\n";
7823 # if a job already runs for this device we get an error, catch it for cleanup
7824 eval { mon_cmd
($vmid, "drive-mirror", %$opts); };
7826 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7828 die "mirroring error: $err\n";
7831 qemu_drive_mirror_monitor
($vmid, $vmiddst, $jobs, $completion, $qga);
7834 # $completion can be either
7835 # 'complete': wait until all jobs are ready, block-job-complete them (default)
7836 # 'cancel': wait until all jobs are ready, block-job-cancel them
7837 # 'skip': wait until all jobs are ready, return with block jobs in ready state
7838 # 'auto': wait until all jobs disappear, only use for jobs which complete automatically
7839 sub qemu_drive_mirror_monitor
{
7840 my ($vmid, $vmiddst, $jobs, $completion, $qga, $op) = @_;
7842 $completion //= 'complete';
7846 my $err_complete = 0;
7848 my $starttime = time ();
7850 die "block job ('$op') timed out\n" if $err_complete > 300;
7852 my $stats = mon_cmd
($vmid, "query-block-jobs");
7855 my $running_jobs = {};
7856 for my $stat (@$stats) {
7857 next if $stat->{type
} ne $op;
7858 $running_jobs->{$stat->{device
}} = $stat;
7861 my $readycounter = 0;
7863 for my $job_id (sort keys %$jobs) {
7864 my $job = $running_jobs->{$job_id};
7866 my $vanished = !defined($job);
7867 my $complete = defined($jobs->{$job_id}->{complete
}) && $vanished;
7868 if($complete || ($vanished && $completion eq 'auto')) {
7869 print "$job_id: $op-job finished\n";
7870 delete $jobs->{$job_id};
7874 die "$job_id: '$op' has been cancelled\n" if !defined($job);
7876 my $busy = $job->{busy
};
7877 my $ready = $job->{ready
};
7878 if (my $total = $job->{len
}) {
7879 my $transferred = $job->{offset
} || 0;
7880 my $remaining = $total - $transferred;
7881 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
7883 my $duration = $ctime - $starttime;
7884 my $total_h = render_bytes
($total, 1);
7885 my $transferred_h = render_bytes
($transferred, 1);
7887 my $status = sprintf(
7888 "transferred $transferred_h of $total_h ($percent%%) in %s",
7889 render_duration
($duration),
7894 $status .= ", still busy"; # shouldn't even happen? but mirror is weird
7896 $status .= ", ready";
7899 print "$job_id: $status\n" if !$jobs->{$job_id}->{ready
};
7900 $jobs->{$job_id}->{ready
} = $ready;
7903 $readycounter++ if $job->{ready
};
7906 last if scalar(keys %$jobs) == 0;
7908 if ($readycounter == scalar(keys %$jobs)) {
7909 print "all '$op' jobs are ready\n";
7911 # do the complete later (or has already been done)
7912 last if $completion eq 'skip' || $completion eq 'auto';
7914 if ($vmiddst && $vmiddst != $vmid) {
7915 my $agent_running = $qga && qga_check_running
($vmid);
7916 if ($agent_running) {
7917 print "freeze filesystem\n";
7918 eval { mon_cmd
($vmid, "guest-fsfreeze-freeze"); };
7921 print "suspend vm\n";
7922 eval { PVE
::QemuServer
::vm_suspend
($vmid, 1); };
7926 # if we clone a disk for a new target vm, we don't switch the disk
7927 PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs);
7929 if ($agent_running) {
7930 print "unfreeze filesystem\n";
7931 eval { mon_cmd
($vmid, "guest-fsfreeze-thaw"); };
7934 print "resume vm\n";
7935 eval { PVE
::QemuServer
::vm_resume
($vmid, 1, 1); };
7942 for my $job_id (sort keys %$jobs) {
7943 # try to switch the disk if source and destination are on the same guest
7944 print "$job_id: Completing block job_id...\n";
7947 if ($completion eq 'complete') {
7948 $op = 'block-job-complete';
7949 } elsif ($completion eq 'cancel') {
7950 $op = 'block-job-cancel';
7952 die "invalid completion value: $completion\n";
7954 eval { mon_cmd
($vmid, $op, device
=> $job_id) };
7955 if ($@ =~ m/cannot be completed/) {
7956 print "$job_id: block job cannot be completed, trying again.\n";
7959 print "$job_id: Completed successfully.\n";
7960 $jobs->{$job_id}->{complete
} = 1;
7971 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7972 die "block job ($op) error: $err";
7976 sub qemu_blockjobs_cancel
{
7977 my ($vmid, $jobs) = @_;
7979 foreach my $job (keys %$jobs) {
7980 print "$job: Cancelling block job\n";
7981 eval { mon_cmd
($vmid, "block-job-cancel", device
=> $job); };
7982 $jobs->{$job}->{cancel
} = 1;
7986 my $stats = mon_cmd
($vmid, "query-block-jobs");
7988 my $running_jobs = {};
7989 foreach my $stat (@$stats) {
7990 $running_jobs->{$stat->{device
}} = $stat;
7993 foreach my $job (keys %$jobs) {
7995 if (defined($jobs->{$job}->{cancel
}) && !defined($running_jobs->{$job})) {
7996 print "$job: Done.\n";
7997 delete $jobs->{$job};
8001 last if scalar(keys %$jobs) == 0;
8007 # Check for bug #4525: drive-mirror will open the target drive with the same aio setting as the
8008 # source, but some storages have problems with io_uring, sometimes even leading to crashes.
8009 my sub clone_disk_check_io_uring
{
8010 my ($src_drive, $storecfg, $src_storeid, $dst_storeid, $use_drive_mirror) = @_;
8012 return if !$use_drive_mirror;
8014 # Don't complain when not changing storage.
8015 # Assume if it works for the source, it'll work for the target too.
8016 return if $src_storeid eq $dst_storeid;
8018 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
8019 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
8021 my $cache_direct = drive_uses_cache_direct
($src_drive);
8023 my $src_uses_io_uring;
8024 if ($src_drive->{aio
}) {
8025 $src_uses_io_uring = $src_drive->{aio
} eq 'io_uring';
8027 $src_uses_io_uring = storage_allows_io_uring_default
($src_scfg, $cache_direct);
8030 die "target storage is known to cause issues with aio=io_uring (used by current drive)\n"
8031 if $src_uses_io_uring && !storage_allows_io_uring_default
($dst_scfg, $cache_direct);
8035 my ($storecfg, $source, $dest, $full, $newvollist, $jobs, $completion, $qga, $bwlimit) = @_;
8037 my ($vmid, $running) = $source->@{qw(vmid running)};
8038 my ($src_drivename, $drive, $snapname) = $source->@{qw(drivename drive snapname)};
8040 my ($newvmid, $dst_drivename, $efisize) = $dest->@{qw(vmid drivename efisize)};
8041 my ($storage, $format) = $dest->@{qw(storage format)};
8043 my $use_drive_mirror = $full && $running && $src_drivename && !$snapname;
8045 if ($src_drivename && $dst_drivename && $src_drivename ne $dst_drivename) {
8046 die "cloning from/to EFI disk requires EFI disk\n"
8047 if $src_drivename eq 'efidisk0' || $dst_drivename eq 'efidisk0';
8048 die "cloning from/to TPM state requires TPM state\n"
8049 if $src_drivename eq 'tpmstate0' || $dst_drivename eq 'tpmstate0';
8051 # This would lead to two device nodes in QEMU pointing to the same backing image!
8052 die "cannot change drive name when cloning disk from/to the same VM\n"
8053 if $use_drive_mirror && $vmid == $newvmid;
8056 die "cannot move TPM state while VM is running\n"
8057 if $use_drive_mirror && $src_drivename eq 'tpmstate0';
8061 print "create " . ($full ?
'full' : 'linked') . " clone of drive ";
8062 print "$src_drivename " if $src_drivename;
8063 print "($drive->{file})\n";
8066 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $drive->{file
}, $newvmid, $snapname);
8067 push @$newvollist, $newvolid;
8069 my ($src_storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
8070 my $storeid = $storage || $src_storeid;
8072 my $dst_format = resolve_dst_disk_format
($storecfg, $storeid, $volname, $format);
8076 if (drive_is_cloudinit
($drive)) {
8077 $name = "vm-$newvmid-cloudinit";
8078 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8079 if ($scfg->{path
}) {
8080 $name .= ".$dst_format";
8083 $size = PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
;
8084 } elsif ($dst_drivename eq 'efidisk0') {
8085 $size = $efisize or die "internal error - need to specify EFI disk size\n";
8086 } elsif ($dst_drivename eq 'tpmstate0') {
8087 $dst_format = 'raw';
8088 $size = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
8090 clone_disk_check_io_uring
($drive, $storecfg, $src_storeid, $storeid, $use_drive_mirror);
8092 $size = PVE
::Storage
::volume_size_info
($storecfg, $drive->{file
}, 10);
8094 $newvolid = PVE
::Storage
::vdisk_alloc
(
8095 $storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024)
8097 push @$newvollist, $newvolid;
8099 PVE
::Storage
::activate_volumes
($storecfg, [$newvolid]);
8101 if (drive_is_cloudinit
($drive)) {
8102 # when cloning multiple disks (e.g. during clone_vm) it might be the last disk
8103 # if this is the case, we have to complete any block-jobs still there from
8104 # previous drive-mirrors
8105 if (($completion eq 'complete') && (scalar(keys %$jobs) > 0)) {
8106 qemu_drive_mirror_monitor
($vmid, $newvmid, $jobs, $completion, $qga);
8111 my $sparseinit = PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $newvolid);
8112 if ($use_drive_mirror) {
8113 qemu_drive_mirror
($vmid, $src_drivename, $newvolid, $newvmid, $sparseinit, $jobs,
8114 $completion, $qga, $bwlimit);
8116 if ($dst_drivename eq 'efidisk0') {
8117 # the relevant data on the efidisk may be smaller than the source
8118 # e.g. on RBD/ZFS, so we use dd to copy only the amount
8119 # that is given by the OVMF_VARS.fd
8120 my $src_path = PVE
::Storage
::path
($storecfg, $drive->{file
}, $snapname);
8121 my $dst_path = PVE
::Storage
::path
($storecfg, $newvolid);
8123 my $src_format = (PVE
::Storage
::parse_volname
($storecfg, $drive->{file
}))[6];
8125 # better for Ceph if block size is not too small, see bug #3324
8128 my $cmd = ['qemu-img', 'dd', '-n', '-O', $dst_format];
8130 if ($src_format eq 'qcow2' && $snapname) {
8131 die "cannot clone qcow2 EFI disk snapshot - requires QEMU >= 6.2\n"
8132 if !min_version
(kvm_user_version
(), 6, 2);
8133 push $cmd->@*, '-l', $snapname;
8135 push $cmd->@*, "bs=$bs", "osize=$size", "if=$src_path", "of=$dst_path";
8138 qemu_img_convert
($drive->{file
}, $newvolid, $size, $snapname, $sparseinit, $bwlimit);
8144 my $size = eval { PVE
::Storage
::volume_size_info
($storecfg, $newvolid, 10) };
8146 my $disk = dclone
($drive);
8147 delete $disk->{format
};
8148 $disk->{file
} = $newvolid;
8149 $disk->{size
} = $size if defined($size);
8154 sub get_running_qemu_version
{
8156 my $res = mon_cmd
($vmid, "query-version");
8157 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
8160 sub qemu_use_old_bios_files
{
8161 my ($machine_type) = @_;
8163 return if !$machine_type;
8165 my $use_old_bios_files = undef;
8167 if ($machine_type =~ m/^(\S+)\.pxe$/) {
8169 $use_old_bios_files = 1;
8171 my $version = extract_version
($machine_type, kvm_user_version
());
8172 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
8173 # load new efi bios files on migration. So this hack is required to allow
8174 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
8175 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
8176 $use_old_bios_files = !min_version
($version, 2, 4);
8179 return ($use_old_bios_files, $machine_type);
8182 sub get_efivars_size
{
8183 my ($conf, $efidisk) = @_;
8185 my $arch = get_vm_arch
($conf);
8186 $efidisk //= $conf->{efidisk0
} ? parse_drive
('efidisk0', $conf->{efidisk0
}) : undef;
8187 my $smm = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
8188 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
8189 return -s
$ovmf_vars;
8192 sub update_efidisk_size
{
8195 return if !defined($conf->{efidisk0
});
8197 my $disk = PVE
::QemuServer
::parse_drive
('efidisk0', $conf->{efidisk0
});
8198 $disk->{size
} = get_efivars_size
($conf);
8199 $conf->{efidisk0
} = print_drive
($disk);
8204 sub update_tpmstate_size
{
8207 my $disk = PVE
::QemuServer
::parse_drive
('tpmstate0', $conf->{tpmstate0
});
8208 $disk->{size
} = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
8209 $conf->{tpmstate0
} = print_drive
($disk);
8212 sub create_efidisk
($$$$$$$) {
8213 my ($storecfg, $storeid, $vmid, $fmt, $arch, $efidisk, $smm) = @_;
8215 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
8217 my $vars_size_b = -s
$ovmf_vars;
8218 my $vars_size = PVE
::Tools
::convert_size
($vars_size_b, 'b' => 'kb');
8219 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
8220 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
8222 qemu_img_convert
($ovmf_vars, $volid, $vars_size_b, undef, 0);
8223 my $size = PVE
::Storage
::volume_size_info
($storecfg, $volid, 3);
8225 return ($volid, $size/1024);
8228 sub vm_iothreads_list
{
8231 my $res = mon_cmd
($vmid, 'query-iothreads');
8234 foreach my $iothread (@$res) {
8235 $iothreads->{ $iothread->{id
} } = $iothread->{"thread-id"};
8242 my ($conf, $drive) = @_;
8246 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)) {
8248 } elsif ($conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single')) {
8254 my $controller = int($drive->{index} / $maxdev);
8255 my $controller_prefix = ($conf->{scsihw
} && $conf->{scsihw
} eq 'virtio-scsi-single')
8259 return ($maxdev, $controller, $controller_prefix);
8262 sub resolve_dst_disk_format
{
8263 my ($storecfg, $storeid, $src_volname, $format) = @_;
8264 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
8267 # if no target format is specified, use the source disk format as hint
8269 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8270 $format = qemu_img_format
($scfg, $src_volname);
8276 # test if requested format is supported - else use default
8277 my $supported = grep { $_ eq $format } @$validFormats;
8278 $format = $defFormat if !$supported;
8282 # NOTE: if this logic changes, please update docs & possibly gui logic
8283 sub find_vmstate_storage
{
8284 my ($conf, $storecfg) = @_;
8286 # first, return storage from conf if set
8287 return $conf->{vmstatestorage
} if $conf->{vmstatestorage
};
8289 my ($target, $shared, $local);
8291 foreach_storage_used_by_vm
($conf, sub {
8293 my $scfg = PVE
::Storage
::storage_config
($storecfg, $sid);
8294 my $dst = $scfg->{shared
} ? \
$shared : \
$local;
8295 $$dst = $sid if !$$dst || $scfg->{path
}; # prefer file based storage
8298 # second, use shared storage where VM has at least one disk
8299 # third, use local storage where VM has at least one disk
8300 # fall back to local storage
8301 $target = $shared // $local // 'local';
8307 my ($uuid, $uuid_str);
8308 UUID
::generate
($uuid);
8309 UUID
::unparse
($uuid, $uuid_str);
8313 sub generate_smbios1_uuid
{
8314 return "uuid=".generate_uuid
();
8320 mon_cmd
($vmid, 'nbd-server-stop', timeout
=> 25);
8323 sub create_reboot_request
{
8325 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
8326 or die "failed to create reboot trigger file: $!\n";
8330 sub clear_reboot_request
{
8332 my $path = "/run/qemu-server/$vmid.reboot";
8335 $res = unlink($path);
8336 die "could not remove reboot request for $vmid: $!"
8337 if !$res && $! != POSIX
::ENOENT
;
8342 sub bootorder_from_legacy
{
8343 my ($conf, $bootcfg) = @_;
8345 my $boot = $bootcfg->{legacy
} || $boot_fmt->{legacy
}->{default};
8346 my $bootindex_hash = {};
8348 foreach my $o (split(//, $boot)) {
8349 $bootindex_hash->{$o} = $i*100;
8355 PVE
::QemuConfig-
>foreach_volume($conf, sub {
8356 my ($ds, $drive) = @_;
8358 if (drive_is_cdrom
($drive, 1)) {
8359 if ($bootindex_hash->{d
}) {
8360 $bootorder->{$ds} = $bootindex_hash->{d
};
8361 $bootindex_hash->{d
} += 1;
8363 } elsif ($bootindex_hash->{c
}) {
8364 $bootorder->{$ds} = $bootindex_hash->{c
}
8365 if $conf->{bootdisk
} && $conf->{bootdisk
} eq $ds;
8366 $bootindex_hash->{c
} += 1;
8370 if ($bootindex_hash->{n
}) {
8371 for (my $i = 0; $i < $MAX_NETS; $i++) {
8372 my $netname = "net$i";
8373 next if !$conf->{$netname};
8374 $bootorder->{$netname} = $bootindex_hash->{n
};
8375 $bootindex_hash->{n
} += 1;
8382 # Generate default device list for 'boot: order=' property. Matches legacy
8383 # default boot order, but with explicit device names. This is important, since
8384 # the fallback for when neither 'order' nor the old format is specified relies
8385 # on 'bootorder_from_legacy' above, and it would be confusing if this diverges.
8386 sub get_default_bootdevices
{
8392 my $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 0);
8393 push @ret, $first if $first;
8396 $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 1);
8397 push @ret, $first if $first;
8400 for (my $i = 0; $i < $MAX_NETS; $i++) {
8401 my $netname = "net$i";
8402 next if !$conf->{$netname};
8403 push @ret, $netname;
8410 sub device_bootorder
{
8413 return bootorder_from_legacy
($conf) if !defined($conf->{boot
});
8415 my $boot = parse_property_string
($boot_fmt, $conf->{boot
});
8418 if (!defined($boot) || $boot->{legacy
}) {
8419 $bootorder = bootorder_from_legacy
($conf, $boot);
8420 } elsif ($boot->{order
}) {
8421 my $i = 100; # start at 100 to allow user to insert devices before us with -args
8422 for my $dev (PVE
::Tools
::split_list
($boot->{order
})) {
8423 $bootorder->{$dev} = $i++;
8430 sub register_qmeventd_handle
{
8434 my $peer = "/var/run/qmeventd.sock";
8439 $fh = IO
::Socket
::UNIX-
>new(Peer
=> $peer, Blocking
=> 0, Timeout
=> 1);
8441 if ($! != EINTR
&& $! != EAGAIN
) {
8442 die "unable to connect to qmeventd socket (vmid: $vmid) - $!\n";
8445 die "unable to connect to qmeventd socket (vmid: $vmid) - timeout "
8446 . "after $count retries\n";
8451 # send handshake to mark VM as backing up
8452 print $fh to_json
({vzdump
=> {vmid
=> "$vmid"}});
8454 # return handle to be closed later when inhibit is no longer required
8458 # bash completion helper
8460 sub complete_backup_archives
{
8461 my ($cmdname, $pname, $cvalue) = @_;
8463 my $cfg = PVE
::Storage
::config
();
8467 if ($cvalue =~ m/^([^:]+):/) {
8471 my $data = PVE
::Storage
::template_list
($cfg, $storeid, 'backup');
8474 foreach my $id (keys %$data) {
8475 foreach my $item (@{$data->{$id}}) {
8476 next if $item->{format
} !~ m/^vma\.(${\PVE::Storage::Plugin::COMPRESSOR_RE})$/;
8477 push @$res, $item->{volid
} if defined($item->{volid
});
8484 my $complete_vmid_full = sub {
8487 my $idlist = vmstatus
();
8491 foreach my $id (keys %$idlist) {
8492 my $d = $idlist->{$id};
8493 if (defined($running)) {
8494 next if $d->{template
};
8495 next if $running && $d->{status
} ne 'running';
8496 next if !$running && $d->{status
} eq 'running';
8505 return &$complete_vmid_full();
8508 sub complete_vmid_stopped
{
8509 return &$complete_vmid_full(0);
8512 sub complete_vmid_running
{
8513 return &$complete_vmid_full(1);
8516 sub complete_storage
{
8518 my $cfg = PVE
::Storage
::config
();
8519 my $ids = $cfg->{ids
};
8522 foreach my $sid (keys %$ids) {
8523 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, undef, 1);
8524 next if !$ids->{$sid}->{content
}->{images
};
8531 sub complete_migration_storage
{
8532 my ($cmd, $param, $current_value, $all_args) = @_;
8534 my $targetnode = @$all_args[1];
8536 my $cfg = PVE
::Storage
::config
();
8537 my $ids = $cfg->{ids
};
8540 foreach my $sid (keys %$ids) {
8541 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, $targetnode, 1);
8542 next if !$ids->{$sid}->{content
}->{images
};
8550 my ($vmid, $include_suspended) = @_;
8551 my $qmpstatus = eval {
8552 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid);
8553 mon_cmd
($vmid, "query-status");
8556 return $qmpstatus && (
8557 $qmpstatus->{status
} eq "paused" ||
8558 $qmpstatus->{status
} eq "prelaunch" ||
8559 ($include_suspended && $qmpstatus->{status
} eq "suspended")
8563 sub check_volume_storage_type
{
8564 my ($storecfg, $vol) = @_;
8566 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($vol);
8567 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8568 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $vol);
8570 die "storage '$storeid' does not support content-type '$vtype'\n"
8571 if !$scfg->{content
}->{$vtype};
8576 sub add_nets_bridge_fdb
{
8577 my ($conf, $vmid) = @_;
8579 for my $opt (keys %$conf) {
8580 next if $opt !~ m/^net(\d+)$/;
8581 my $iface = "tap${vmid}i$1";
8582 # NOTE: expect setups with learning off to *not* use auto-random-generation of MAC on start
8583 my $net = parse_net
($conf->{$opt}, 1) or next;
8585 my $mac = $net->{macaddr
};
8587 log_warn
("MAC learning disabled, but vNIC '$iface' has no static MAC to add to forwarding DB!")
8588 if !file_read_firstline
("/sys/class/net/$iface/brport/learning");
8592 my $bridge = $net->{bridge
};
8594 log_warn
("Interface '$iface' not attached to any bridge.");
8598 PVE
::Network
::SDN
::Zones
::add_bridge_fdb
($iface, $mac, $bridge);
8599 } elsif (-d
"/sys/class/net/$bridge/bridge") { # avoid fdb management with OVS for now
8600 PVE
::Network
::add_bridge_fdb
($iface, $mac);
8605 sub del_nets_bridge_fdb
{
8606 my ($conf, $vmid) = @_;
8608 for my $opt (keys %$conf) {
8609 next if $opt !~ m/^net(\d+)$/;
8610 my $iface = "tap${vmid}i$1";
8612 my $net = parse_net
($conf->{$opt}) or next;
8613 my $mac = $net->{macaddr
} or next;
8615 my $bridge = $net->{bridge
};
8617 PVE
::Network
::SDN
::Zones
::del_bridge_fdb
($iface, $mac, $bridge);
8618 } elsif (-d
"/sys/class/net/$bridge/bridge") { # avoid fdb management with OVS for now
8619 PVE
::Network
::del_bridge_fdb
($iface, $mac);
8624 sub create_ifaces_ipams_ips
{
8625 my ($conf, $vmid) = @_;
8627 return if !$have_sdn;
8629 foreach my $opt (keys %$conf) {
8630 if ($opt =~ m/^net(\d+)$/) {
8631 my $value = $conf->{$opt};
8632 my $net = PVE
::QemuServer
::parse_net
($value);
8633 eval { PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($net->{bridge
}, $conf->{name
}, $net->{macaddr
}, $vmid, undef, 1) };
8639 sub delete_ifaces_ipams_ips
{
8640 my ($conf, $vmid) = @_;
8642 return if !$have_sdn;
8644 foreach my $opt (keys %$conf) {
8645 if ($opt =~ m/^net(\d+)$/) {
8646 my $net = PVE
::QemuServer
::parse_net
($conf->{$opt});
8647 eval { PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($net->{bridge
}, $net->{macaddr
}, $conf->{name
}) };