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);
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";
1369 my($fh, $noerr) = @_;
1372 my $SG_GET_VERSION_NUM = 0x2282;
1374 my $versionbuf = "\x00" x
8;
1375 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1377 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
1380 my $version = unpack("I", $versionbuf);
1381 if ($version < 30000) {
1382 die "scsi generic interface too old\n" if !$noerr;
1386 my $buf = "\x00" x
36;
1387 my $sensebuf = "\x00" x
8;
1388 my $cmd = pack("C x3 C x1", 0x12, 36);
1390 # see /usr/include/scsi/sg.h
1391 my $sg_io_hdr_t = "i i C C s I P P P I I i P C C C C S S i I I";
1394 $sg_io_hdr_t, ord('S'), -3, length($cmd), length($sensebuf), 0, length($buf), $buf, $cmd, $sensebuf, 6000
1397 $ret = ioctl($fh, $SG_IO, $packet);
1399 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
1403 my @res = unpack($sg_io_hdr_t, $packet);
1404 if ($res[17] || $res[18]) {
1405 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
1410 $res->@{qw(type removable vendor product revision)} = unpack("C C x6 A8 A16 A4", $buf);
1412 $res->{removable
} = $res->{removable
} & 128 ?
1 : 0;
1413 $res->{type
} &= 0x1F;
1421 my $fh = IO
::File-
>new("+<$path") || return;
1422 my $res = scsi_inquiry
($fh, 1);
1428 sub print_tabletdevice_full
{
1429 my ($conf, $arch) = @_;
1431 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1433 # we use uhci for old VMs because tablet driver was buggy in older qemu
1435 if ($q35 || $arch eq 'aarch64') {
1441 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1444 sub print_keyboarddevice_full
{
1445 my ($conf, $arch) = @_;
1447 return if $arch ne 'aarch64';
1449 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1452 my sub get_drive_id
{
1454 return "$drive->{interface}$drive->{index}";
1457 sub print_drivedevice_full
{
1458 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1463 my $drive_id = get_drive_id
($drive);
1464 if ($drive->{interface
} eq 'virtio') {
1465 my $pciaddr = print_pci_addr
("$drive_id", $bridges, $arch, $machine_type);
1466 $device = "virtio-blk-pci,drive=drive-$drive_id,id=${drive_id}${pciaddr}";
1467 $device .= ",iothread=iothread-$drive_id" if $drive->{iothread
};
1468 } elsif ($drive->{interface
} eq 'scsi') {
1470 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
1471 my $unit = $drive->{index} % $maxdev;
1472 my $devicetype = 'hd';
1474 if (drive_is_cdrom
($drive)) {
1477 if ($drive->{file
} =~ m
|^/|) {
1478 $path = $drive->{file
};
1479 if (my $info = path_is_scsi
($path)) {
1480 if ($info->{type
} == 0 && $drive->{scsiblock
}) {
1481 $devicetype = 'block';
1482 } elsif ($info->{type
} == 1) { # tape
1483 $devicetype = 'generic';
1487 $path = PVE
::Storage
::path
($storecfg, $drive->{file
});
1490 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1491 my $version = extract_version
($machine_type, kvm_user_version
());
1492 if ($path =~ m/^iscsi\:\/\
// &&
1493 !min_version
($version, 4, 1)) {
1494 $devicetype = 'generic';
1498 if (!$conf->{scsihw
} || $conf->{scsihw
} =~ m/^lsi/ || $conf->{scsihw
} eq 'pvscsi') {
1499 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
1501 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
1502 .",lun=$drive->{index}";
1504 $device .= ",drive=drive-$drive_id,id=$drive_id";
1506 if ($drive->{ssd
} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1507 $device .= ",rotation_rate=1";
1509 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1511 } elsif ($drive->{interface
} eq 'ide' || $drive->{interface
} eq 'sata') {
1512 my $maxdev = ($drive->{interface
} eq 'sata') ?
$PVE::QemuServer
::Drive
::MAX_SATA_DISKS
: 2;
1513 my $controller = int($drive->{index} / $maxdev);
1514 my $unit = $drive->{index} % $maxdev;
1516 # machine type q35 only supports unit=0 for IDE rather than 2 units. This wasn't handled
1517 # correctly before, so e.g. index=2 was mapped to controller=1,unit=0 rather than
1518 # controller=2,unit=0. Note that odd indices never worked, as they would be mapped to
1519 # unit=1, so to keep backwards compat for migration, it suffices to keep even ones as they
1520 # were before. Move odd ones up by 2 where they don't clash.
1521 if (PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf) && $drive->{interface
} eq 'ide') {
1522 $controller += 2 * ($unit % 2);
1526 my $devicetype = ($drive->{media
} && $drive->{media
} eq 'cdrom') ?
"cd" : "hd";
1528 $device = "ide-$devicetype";
1529 if ($drive->{interface
} eq 'ide') {
1530 $device .= ",bus=ide.$controller,unit=$unit";
1532 $device .= ",bus=ahci$controller.$unit";
1534 $device .= ",drive=drive-$drive_id,id=$drive_id";
1536 if ($devicetype eq 'hd') {
1537 if (my $model = $drive->{model
}) {
1538 $model = URI
::Escape
::uri_unescape
($model);
1539 $device .= ",model=$model";
1541 if ($drive->{ssd
}) {
1542 $device .= ",rotation_rate=1";
1545 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1546 } elsif ($drive->{interface
} eq 'usb') {
1548 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1550 die "unsupported interface type";
1553 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex
};
1555 if (my $serial = $drive->{serial
}) {
1556 $serial = URI
::Escape
::uri_unescape
($serial);
1557 $device .= ",serial=$serial";
1564 sub get_initiator_name
{
1567 my $fh = IO
::File-
>new('/etc/iscsi/initiatorname.iscsi') || return;
1568 while (defined(my $line = <$fh>)) {
1569 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1578 my sub storage_allows_io_uring_default
{
1579 my ($scfg, $cache_direct) = @_;
1581 # io_uring with cache mode writeback or writethrough on krbd will hang...
1582 return if $scfg && $scfg->{type
} eq 'rbd' && $scfg->{krbd
} && !$cache_direct;
1584 # io_uring with cache mode writeback or writethrough on LVM will hang, without cache only
1585 # sometimes, just plain disable...
1586 return if $scfg && $scfg->{type
} eq 'lvm';
1588 # io_uring causes problems when used with CIFS since kernel 5.15
1589 # Some discussion: https://www.spinics.net/lists/linux-cifs/msg26734.html
1590 return if $scfg && $scfg->{type
} eq 'cifs';
1595 my sub drive_uses_cache_direct
{
1596 my ($drive, $scfg) = @_;
1598 my $cache_direct = 0;
1600 if (my $cache = $drive->{cache
}) {
1601 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1602 } elsif (!drive_is_cdrom
($drive) && !($scfg && $scfg->{type
} eq 'btrfs' && !$scfg->{nocow
})) {
1606 return $cache_direct;
1609 sub print_drive_commandline_full
{
1610 my ($storecfg, $vmid, $drive, $pbs_name, $io_uring) = @_;
1613 my $volid = $drive->{file
};
1614 my $format = $drive->{format
};
1615 my $drive_id = get_drive_id
($drive);
1617 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1618 my $scfg = $storeid ? PVE
::Storage
::storage_config
($storecfg, $storeid) : undef;
1620 if (drive_is_cdrom
($drive)) {
1621 $path = get_iso_path
($storecfg, $vmid, $volid);
1622 die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
1625 $path = PVE
::Storage
::path
($storecfg, $volid);
1626 $format //= qemu_img_format
($scfg, $volname);
1633 my $is_rbd = $path =~ m/^rbd:/;
1636 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1637 foreach my $o (@qemu_drive_options) {
1638 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1641 # snapshot only accepts on|off
1642 if (defined($drive->{snapshot
})) {
1643 my $v = $drive->{snapshot
} ?
'on' : 'off';
1644 $opts .= ",snapshot=$v";
1647 if (defined($drive->{ro
})) { # ro maps to QEMUs `readonly`, which accepts `on` or `off` only
1648 $opts .= ",readonly=" . ($drive->{ro
} ?
'on' : 'off');
1651 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1652 my ($dir, $qmpname) = @$type;
1653 if (my $v = $drive->{"mbps$dir"}) {
1654 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1656 if (my $v = $drive->{"mbps${dir}_max"}) {
1657 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1659 if (my $v = $drive->{"bps${dir}_max_length"}) {
1660 $opts .= ",throttling.bps$qmpname-max-length=$v";
1662 if (my $v = $drive->{"iops${dir}"}) {
1663 $opts .= ",throttling.iops$qmpname=$v";
1665 if (my $v = $drive->{"iops${dir}_max"}) {
1666 $opts .= ",throttling.iops$qmpname-max=$v";
1668 if (my $v = $drive->{"iops${dir}_max_length"}) {
1669 $opts .= ",throttling.iops$qmpname-max-length=$v";
1674 $format = "rbd" if $is_rbd;
1675 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1677 $opts .= ",format=alloc-track,file.driver=$format";
1679 $opts .= ",format=$format";
1682 my $cache_direct = drive_uses_cache_direct
($drive, $scfg);
1684 $opts .= ",cache=none" if !$drive->{cache
} && $cache_direct;
1686 if (!$drive->{aio
}) {
1687 if ($io_uring && storage_allows_io_uring_default
($scfg, $cache_direct)) {
1688 # io_uring supports all cache modes
1689 $opts .= ",aio=io_uring";
1691 # aio native works only with O_DIRECT
1693 $opts .= ",aio=native";
1695 $opts .= ",aio=threads";
1700 if (!drive_is_cdrom
($drive)) {
1702 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
1703 $detectzeroes = 'off';
1704 } elsif ($drive->{discard
}) {
1705 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
1707 # This used to be our default with discard not being specified:
1708 $detectzeroes = 'on';
1711 # note: 'detect-zeroes' works per blockdev and we want it to persist
1712 # after the alloc-track is removed, so put it on 'file' directly
1713 my $dz_param = $pbs_name ?
"file.detect-zeroes" : "detect-zeroes";
1714 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
1718 $opts .= ",backing=$pbs_name";
1719 $opts .= ",auto-remove=on";
1722 # my $file_param = $pbs_name ? "file.file.filename" : "file";
1723 my $file_param = "file";
1725 # non-rbd drivers require the underlying file to be a seperate block
1726 # node, so add a second .file indirection
1727 $file_param .= ".file" if !$is_rbd;
1728 $file_param .= ".filename";
1730 my $pathinfo = $path ?
"$file_param=$path," : '';
1732 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1735 sub print_pbs_blockdev
{
1736 my ($pbs_conf, $pbs_name) = @_;
1737 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1738 $blockdev .= ",repository=$pbs_conf->{repository}";
1739 $blockdev .= ",namespace=$pbs_conf->{namespace}" if $pbs_conf->{namespace
};
1740 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1741 $blockdev .= ",archive=$pbs_conf->{archive}";
1742 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile
};
1746 sub print_netdevice_full
{
1747 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version) = @_;
1749 my $device = $net->{model
};
1750 if ($net->{model
} eq 'virtio') {
1751 $device = 'virtio-net-pci';
1754 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
1755 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
1756 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
1757 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1758 # and out of each queue plus one config interrupt and control vector queue
1759 my $vectors = $net->{queues
} * 2 + 2;
1760 $tmpstr .= ",vectors=$vectors,mq=on";
1761 if (min_version
($machine_version, 7, 1)) {
1762 $tmpstr .= ",packed=on";
1766 if (min_version
($machine_version, 7, 1) && $net->{model
} eq 'virtio'){
1767 $tmpstr .= ",rx_queue_size=1024,tx_queue_size=256";
1770 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
1772 if (my $mtu = $net->{mtu
}) {
1773 if ($net->{model
} eq 'virtio' && $net->{bridge
}) {
1774 my $bridge_mtu = PVE
::Network
::read_bridge_mtu
($net->{bridge
});
1777 } elsif ($mtu < 576) {
1778 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1779 } elsif ($mtu > $bridge_mtu) {
1780 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1782 $tmpstr .= ",host_mtu=$mtu";
1784 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
1788 if ($use_old_bios_files) {
1790 if ($device eq 'virtio-net-pci') {
1791 $romfile = 'pxe-virtio.rom';
1792 } elsif ($device eq 'e1000') {
1793 $romfile = 'pxe-e1000.rom';
1794 } elsif ($device eq 'e1000e') {
1795 $romfile = 'pxe-e1000e.rom';
1796 } elsif ($device eq 'ne2k') {
1797 $romfile = 'pxe-ne2k_pci.rom';
1798 } elsif ($device eq 'pcnet') {
1799 $romfile = 'pxe-pcnet.rom';
1800 } elsif ($device eq 'rtl8139') {
1801 $romfile = 'pxe-rtl8139.rom';
1803 $tmpstr .= ",romfile=$romfile" if $romfile;
1809 sub print_netdev_full
{
1810 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
1813 if ($netid =~ m/^net(\d+)$/) {
1817 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
1819 my $ifname = "tap${vmid}i$i";
1821 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1822 die "interface name '$ifname' is too long (max 15 character)\n"
1823 if length($ifname) >= 16;
1825 my $vhostparam = '';
1826 if (is_native
($arch)) {
1827 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
1830 my $vmname = $conf->{name
} || "vm$vmid";
1833 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
1835 if ($net->{bridge
}) {
1836 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1837 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
1839 $netdev = "type=user,id=$netid,hostname=$vmname";
1842 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
1848 'cirrus' => 'cirrus-vga',
1850 'vmware' => 'vmware-svga',
1851 'virtio' => 'virtio-vga',
1852 'virtio-gl' => 'virtio-vga-gl',
1855 sub print_vga_device
{
1856 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
1858 my $type = $vga_map->{$vga->{type
}};
1859 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
1860 $type = 'virtio-gpu';
1862 my $vgamem_mb = $vga->{memory
};
1864 my $max_outputs = '';
1866 $type = $id ?
'qxl' : 'qxl-vga';
1868 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
1869 # set max outputs so linux can have up to 4 qxl displays with one device
1870 if (min_version
($machine_version, 4, 1)) {
1871 $max_outputs = ",max_outputs=4";
1876 die "no devicetype for $vga->{type}\n" if !$type;
1880 if ($vga->{type
} =~ /^virtio/) {
1881 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
1882 $memory = ",max_hostmem=$bytes";
1884 # from https://www.spice-space.org/multiple-monitors.html
1885 $memory = ",vgamem_mb=$vga->{memory}";
1886 my $ram = $vgamem_mb * 4;
1887 my $vram = $vgamem_mb * 2;
1888 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1890 $memory = ",vgamem_mb=$vga->{memory}";
1892 } elsif ($qxlnum && $id) {
1893 $memory = ",ram_size=67108864,vram_size=33554432";
1897 if ($type eq 'VGA' && windows_version
($conf->{ostype
})) {
1898 $edidoff=",edid=off" if (!defined($conf->{bios
}) || $conf->{bios
} ne 'ovmf');
1901 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1902 my $vgaid = "vga" . ($id // '');
1904 if ($q35 && $vgaid eq 'vga') {
1905 # the first display uses pcie.0 bus on q35 machines
1906 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
1908 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
1911 if ($vga->{type
} eq 'virtio-gl') {
1912 my $base = '/usr/lib/x86_64-linux-gnu/lib';
1913 die "missing libraries for '$vga->{type}' detected! Please install 'libgl1' and 'libegl1'\n"
1914 if !-e
"${base}EGL.so.1" || !-e
"${base}GL.so.1";
1916 die "no DRM render node detected (/dev/dri/renderD*), no GPU? - needed for '$vga->{type}' display\n"
1917 if !PVE
::Tools
::dir_glob_regex
('/dev/dri/', "renderD.*");
1920 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
1923 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1925 my ($data, $disable_mac_autogen) = @_;
1927 my $res = eval { parse_property_string
($net_fmt, $data) };
1932 if (!defined($res->{macaddr
}) && !$disable_mac_autogen) {
1933 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1934 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1939 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1940 sub parse_ipconfig
{
1943 my $res = eval { parse_property_string
($ipconfig_fmt, $data) };
1949 if ($res->{gw
} && !$res->{ip
}) {
1950 warn 'gateway specified without specifying an IP address';
1953 if ($res->{gw6
} && !$res->{ip6
}) {
1954 warn 'IPv6 gateway specified without specifying an IPv6 address';
1957 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
1958 warn 'gateway specified together with DHCP';
1961 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
1963 warn "IPv6 gateway specified together with $res->{ip6} address";
1967 if (!$res->{ip
} && !$res->{ip6
}) {
1968 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
1977 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
1980 sub add_random_macs
{
1981 my ($settings) = @_;
1983 foreach my $opt (keys %$settings) {
1984 next if $opt !~ m/^net(\d+)$/;
1985 my $net = parse_net
($settings->{$opt});
1987 $settings->{$opt} = print_net
($net);
1991 sub vm_is_volid_owner
{
1992 my ($storecfg, $vmid, $volid) = @_;
1994 if ($volid !~ m
|^/|) {
1996 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
1997 if ($owner && ($owner == $vmid)) {
2005 sub vmconfig_register_unused_drive
{
2006 my ($storecfg, $vmid, $conf, $drive) = @_;
2008 if (drive_is_cloudinit
($drive)) {
2009 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
2011 delete $conf->{cloudinit
};
2012 } elsif (!drive_is_cdrom
($drive)) {
2013 my $volid = $drive->{file
};
2014 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
2015 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
2020 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
2024 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
2025 format_description
=> 'UUID',
2026 description
=> "Set SMBIOS1 UUID.",
2031 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2032 format_description
=> 'Base64 encoded string',
2033 description
=> "Set SMBIOS1 version.",
2038 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2039 format_description
=> 'Base64 encoded string',
2040 description
=> "Set SMBIOS1 serial number.",
2045 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2046 format_description
=> 'Base64 encoded string',
2047 description
=> "Set SMBIOS1 manufacturer.",
2052 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2053 format_description
=> 'Base64 encoded string',
2054 description
=> "Set SMBIOS1 product ID.",
2059 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2060 format_description
=> 'Base64 encoded string',
2061 description
=> "Set SMBIOS1 SKU string.",
2066 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2067 format_description
=> 'Base64 encoded string',
2068 description
=> "Set SMBIOS1 family string.",
2073 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
2081 my $res = eval { parse_property_string
($smbios1_fmt, $data) };
2088 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2091 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2093 sub parse_watchdog
{
2098 my $res = eval { parse_property_string
($watchdog_fmt, $value) };
2103 sub parse_guest_agent
{
2106 return {} if !defined($conf->{agent
});
2108 my $res = eval { parse_property_string
($agent_fmt, $conf->{agent
}) };
2111 # if the agent is disabled ignore the other potentially set properties
2112 return {} if !$res->{enabled
};
2117 my ($conf, $key) = @_;
2118 return undef if !defined($conf->{agent
});
2120 my $agent = parse_guest_agent
($conf);
2121 return $agent->{$key};
2127 return {} if !$value;
2128 my $res = eval { parse_property_string
($vga_fmt, $value) };
2138 my $res = eval { parse_property_string
($rng_fmt, $value) };
2143 sub parse_meta_info
{
2148 my $res = eval { parse_property_string
($meta_info_fmt, $value) };
2153 sub new_meta_info_string
{
2154 my () = @_; # for now do not allow to override any value
2156 return PVE
::JSONSchema
::print_property_string
(
2158 'creation-qemu' => kvm_user_version
(),
2159 ctime
=> "". int(time()),
2165 sub qemu_created_version_fixups
{
2166 my ($conf, $forcemachine, $kvmver) = @_;
2168 my $meta = parse_meta_info
($conf->{meta
}) // {};
2169 my $forced_vers = PVE
::QemuServer
::Machine
::extract_version
($forcemachine);
2171 # check if we need to apply some handling for VMs that always use the latest machine version but
2172 # had a machine version transition happen that affected HW such that, e.g., an OS config change
2173 # would be required (we do not want to pin machine version for non-windows OS type)
2175 (!defined($conf->{machine
}) || $conf->{machine
} =~ m/^(?:pc|q35|virt)$/) # non-versioned machine
2176 && (!defined($meta->{'creation-qemu'}) || !min_version
($meta->{'creation-qemu'}, 6, 1)) # created before 6.1
2177 && (!$forced_vers || min_version
($forced_vers, 6, 1)) # handle snapshot-rollback/migrations
2178 && min_version
($kvmver, 6, 1) # only need to apply the change since 6.1
2180 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
2181 if ($q35 && $conf->{ostype
} && $conf->{ostype
} eq 'l26') {
2182 # this changed to default-on in Q 6.1 for q35 machines, it will mess with PCI slot view
2183 # and thus with the predictable interface naming of systemd
2184 return ['-global', 'ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off'];
2190 # add JSON properties for create and set function
2191 sub json_config_properties
{
2192 my ($prop, $with_disk_alloc) = @_;
2194 my $skip_json_config_opts = {
2198 runningmachine
=> 1,
2203 foreach my $opt (keys %$confdesc) {
2204 next if $skip_json_config_opts->{$opt};
2206 if ($with_disk_alloc && is_valid_drivename
($opt)) {
2207 $prop->{$opt} = $PVE::QemuServer
::Drive
::drivedesc_hash_with_alloc-
>{$opt};
2209 $prop->{$opt} = $confdesc->{$opt};
2216 # Properties that we can read from an OVF file
2217 sub json_ovf_properties
{
2220 for my $device (PVE
::QemuServer
::Drive
::valid_drive_names
()) {
2221 $prop->{$device} = {
2223 format
=> 'pve-volume-id-or-absolute-path',
2224 description
=> "Disk image that gets imported to $device",
2231 description
=> "The number of CPU cores.",
2236 description
=> "Amount of RAM for the VM in MB.",
2241 description
=> "Name of the VM.",
2248 # return copy of $confdesc_cloudinit to generate documentation
2249 sub cloudinit_config_properties
{
2251 return dclone
($confdesc_cloudinit);
2254 sub cloudinit_pending_properties
{
2256 map { $_ => 1 } keys $confdesc_cloudinit->%*,
2259 $p->{"net$_"} = 1 for 0..($MAX_NETS-1);
2264 my ($key, $value) = @_;
2266 die "unknown setting '$key'\n" if !$confdesc->{$key};
2268 my $type = $confdesc->{$key}->{type
};
2270 if (!defined($value)) {
2271 die "got undefined value\n";
2274 if ($value =~ m/[\n\r]/) {
2275 die "property contains a line feed\n";
2278 if ($type eq 'boolean') {
2279 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2280 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2281 die "type check ('boolean') failed - got '$value'\n";
2282 } elsif ($type eq 'integer') {
2283 return int($1) if $value =~ m/^(\d+)$/;
2284 die "type check ('integer') failed - got '$value'\n";
2285 } elsif ($type eq 'number') {
2286 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2287 die "type check ('number') failed - got '$value'\n";
2288 } elsif ($type eq 'string') {
2289 if (my $fmt = $confdesc->{$key}->{format
}) {
2290 PVE
::JSONSchema
::check_format
($fmt, $value);
2293 $value =~ s/^\"(.*)\"$/$1/;
2296 die "internal error"
2301 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
2303 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2305 if (!$skiplock && !PVE
::QemuConfig-
>has_lock($conf, 'suspended')) {
2306 PVE
::QemuConfig-
>check_lock($conf);
2309 if ($conf->{template
}) {
2310 # check if any base image is still used by a linked clone
2311 PVE
::QemuConfig-
>foreach_volume_full($conf, { include_unused
=> 1 }, sub {
2312 my ($ds, $drive) = @_;
2313 return if drive_is_cdrom
($drive);
2315 my $volid = $drive->{file
};
2316 return if !$volid || $volid =~ m
|^/|;
2318 die "base volume '$volid' is still in use by linked cloned\n"
2319 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2325 my $remove_owned_drive = sub {
2326 my ($ds, $drive) = @_;
2327 return if drive_is_cdrom
($drive, 1);
2329 my $volid = $drive->{file
};
2330 return if !$volid || $volid =~ m
|^/|;
2331 return if $volids->{$volid};
2333 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2334 return if !$path || !$owner || ($owner != $vmid);
2336 $volids->{$volid} = 1;
2337 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2338 warn "Could not remove disk '$volid', check manually: $@" if $@;
2341 # only remove disks owned by this VM (referenced in the config)
2342 my $include_opts = {
2343 include_unused
=> 1,
2344 extra_keys
=> ['vmstate'],
2346 PVE
::QemuConfig-
>foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2348 for my $snap (values %{$conf->{snapshots
}}) {
2349 next if !defined($snap->{vmstate
});
2350 my $drive = PVE
::QemuConfig-
>parse_volume('vmstate', $snap->{vmstate
}, 1);
2351 next if !defined($drive);
2352 $remove_owned_drive->('vmstate', $drive);
2355 PVE
::QemuConfig-
>foreach_volume_full($conf->{pending
}, $include_opts, $remove_owned_drive);
2357 if ($purge_unreferenced) { # also remove unreferenced disk
2358 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid, undef, 'images');
2359 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2360 my ($volid, $sid, $volname, $d) = @_;
2361 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2366 eval { delete_ifaces_ipams_ips
($conf, $vmid)};
2369 if (defined $replacement_conf) {
2370 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2372 PVE
::QemuConfig-
>destroy_config($vmid);
2376 sub parse_vm_config
{
2377 my ($filename, $raw, $strict) = @_;
2379 return if !defined($raw);
2382 digest
=> Digest
::SHA
::sha1_hex
($raw),
2388 my $handle_error = sub {
2398 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2399 || die "got strange filename '$filename'";
2405 my $finish_description = sub {
2406 if (defined($descr)) {
2408 $conf->{description
} = $descr;
2414 my @lines = split(/\n/, $raw);
2415 foreach my $line (@lines) {
2416 next if $line =~ m/^\s*$/;
2418 if ($line =~ m/^\[PENDING\]\s*$/i) {
2419 $section = 'pending';
2420 $finish_description->();
2421 $conf = $res->{$section} = {};
2423 } elsif ($line =~ m/^\[special:cloudinit\]\s*$/i) {
2424 $section = 'cloudinit';
2425 $finish_description->();
2426 $conf = $res->{$section} = {};
2429 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2431 $finish_description->();
2432 $conf = $res->{snapshots
}->{$section} = {};
2436 if ($line =~ m/^\#(.*)$/) {
2437 $descr = '' if !defined($descr);
2438 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2442 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2443 $descr = '' if !defined($descr);
2444 $descr .= PVE
::Tools
::decode_text
($2);
2445 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2446 $conf->{snapstate
} = $1;
2447 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2450 $conf->{$key} = $value;
2451 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2453 if ($section eq 'pending') {
2454 $conf->{delete} = $value; # we parse this later
2456 $handle_error->("vm $vmid - property 'delete' is only allowed in [PENDING]\n");
2458 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2461 if ($section eq 'cloudinit') {
2462 # ignore validation only used for informative purpose
2463 $conf->{$key} = $value;
2466 eval { $value = check_type
($key, $value); };
2468 $handle_error->("vm $vmid - unable to parse value of '$key' - $@");
2470 $key = 'ide2' if $key eq 'cdrom';
2471 my $fmt = $confdesc->{$key}->{format
};
2472 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2473 my $v = parse_drive
($key, $value);
2474 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2475 $v->{file
} = $volid;
2476 $value = print_drive
($v);
2478 $handle_error->("vm $vmid - unable to parse value of '$key'\n");
2483 $conf->{$key} = $value;
2486 $handle_error->("vm $vmid - unable to parse config: $line\n");
2490 $finish_description->();
2491 delete $res->{snapstate
}; # just to be sure
2496 sub write_vm_config
{
2497 my ($filename, $conf) = @_;
2499 delete $conf->{snapstate
}; # just to be sure
2501 if ($conf->{cdrom
}) {
2502 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2503 $conf->{ide2
} = $conf->{cdrom
};
2504 delete $conf->{cdrom
};
2507 # we do not use 'smp' any longer
2508 if ($conf->{sockets
}) {
2509 delete $conf->{smp
};
2510 } elsif ($conf->{smp
}) {
2511 $conf->{sockets
} = $conf->{smp
};
2512 delete $conf->{cores
};
2513 delete $conf->{smp
};
2516 my $used_volids = {};
2518 my $cleanup_config = sub {
2519 my ($cref, $pending, $snapname) = @_;
2521 foreach my $key (keys %$cref) {
2522 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2523 $key eq 'snapstate' || $key eq 'pending' || $key eq 'cloudinit';
2524 my $value = $cref->{$key};
2525 if ($key eq 'delete') {
2526 die "propertry 'delete' is only allowed in [PENDING]\n"
2528 # fixme: check syntax?
2531 eval { $value = check_type
($key, $value); };
2532 die "unable to parse value of '$key' - $@" if $@;
2534 $cref->{$key} = $value;
2536 if (!$snapname && is_valid_drivename
($key)) {
2537 my $drive = parse_drive
($key, $value);
2538 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2543 &$cleanup_config($conf);
2545 &$cleanup_config($conf->{pending
}, 1);
2547 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2548 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2549 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2552 # remove 'unusedX' settings if we re-add a volume
2553 foreach my $key (keys %$conf) {
2554 my $value = $conf->{$key};
2555 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2556 delete $conf->{$key};
2560 my $generate_raw_config = sub {
2561 my ($conf, $pending) = @_;
2565 # add description as comment to top of file
2566 if (defined(my $descr = $conf->{description
})) {
2568 foreach my $cl (split(/\n/, $descr)) {
2569 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2572 $raw .= "#\n" if $pending;
2576 foreach my $key (sort keys %$conf) {
2577 next if $key =~ /^(digest|description|pending|cloudinit|snapshots)$/;
2578 $raw .= "$key: $conf->{$key}\n";
2583 my $raw = &$generate_raw_config($conf);
2585 if (scalar(keys %{$conf->{pending
}})){
2586 $raw .= "\n[PENDING]\n";
2587 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2590 if (scalar(keys %{$conf->{cloudinit
}}) && PVE
::QemuConfig-
>has_cloudinit($conf)){
2591 $raw .= "\n[special:cloudinit]\n";
2592 $raw .= &$generate_raw_config($conf->{cloudinit
});
2595 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2596 $raw .= "\n[$snapname]\n";
2597 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2607 # we use static defaults from our JSON schema configuration
2608 foreach my $key (keys %$confdesc) {
2609 if (defined(my $default = $confdesc->{$key}->{default})) {
2610 $res->{$key} = $default;
2618 my $vmlist = PVE
::Cluster
::get_vmlist
();
2620 return $res if !$vmlist || !$vmlist->{ids
};
2621 my $ids = $vmlist->{ids
};
2622 my $nodename = nodename
();
2624 foreach my $vmid (keys %$ids) {
2625 my $d = $ids->{$vmid};
2626 next if !$d->{node
} || $d->{node
} ne $nodename;
2627 next if !$d->{type
} || $d->{type
} ne 'qemu';
2628 $res->{$vmid}->{exists} = 1;
2633 # test if VM uses local resources (to prevent migration)
2634 sub check_local_resources
{
2635 my ($conf, $noerr) = @_;
2638 my $mapped_res = [];
2640 my $nodelist = PVE
::Cluster
::get_nodelist
();
2641 my $pci_map = PVE
::Mapping
::PCI
::config
();
2642 my $usb_map = PVE
::Mapping
::USB
::config
();
2644 my $missing_mappings_by_node = { map { $_ => [] } @$nodelist };
2646 my $add_missing_mapping = sub {
2647 my ($type, $key, $id) = @_;
2648 for my $node (@$nodelist) {
2650 if ($type eq 'pci') {
2651 $entry = PVE
::Mapping
::PCI
::get_node_mapping
($pci_map, $id, $node);
2652 } elsif ($type eq 'usb') {
2653 $entry = PVE
::Mapping
::USB
::get_node_mapping
($usb_map, $id, $node);
2655 if (!scalar($entry->@*)) {
2656 push @{$missing_mappings_by_node->{$node}}, $key;
2661 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2662 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2664 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2666 foreach my $k (keys %$conf) {
2667 if ($k =~ m/^usb/) {
2668 my $entry = parse_property_string
('pve-qm-usb', $conf->{$k});
2669 next if $entry->{host
} =~ m/^spice$/i;
2670 if ($entry->{mapping
}) {
2671 $add_missing_mapping->('usb', $k, $entry->{mapping
});
2672 push @$mapped_res, $k;
2675 if ($k =~ m/^hostpci/) {
2676 my $entry = parse_property_string
('pve-qm-hostpci', $conf->{$k});
2677 if ($entry->{mapping
}) {
2678 $add_missing_mapping->('pci', $k, $entry->{mapping
});
2679 push @$mapped_res, $k;
2682 # sockets are safe: they will recreated be on the target side post-migrate
2683 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2684 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2687 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2689 return wantarray ?
(\
@loc_res, $mapped_res, $missing_mappings_by_node) : \
@loc_res;
2692 # check if used storages are available on all nodes (use by migrate)
2693 sub check_storage_availability
{
2694 my ($storecfg, $conf, $node) = @_;
2696 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2697 my ($ds, $drive) = @_;
2699 my $volid = $drive->{file
};
2702 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2705 # check if storage is available on both nodes
2706 my $scfg = PVE
::Storage
::storage_check_enabled
($storecfg, $sid);
2707 PVE
::Storage
::storage_check_enabled
($storecfg, $sid, $node);
2709 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $volid);
2711 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2712 if !$scfg->{content
}->{$vtype};
2716 # list nodes where all VM images are available (used by has_feature API)
2718 my ($conf, $storecfg) = @_;
2720 my $nodelist = PVE
::Cluster
::get_nodelist
();
2721 my $nodehash = { map { $_ => 1 } @$nodelist };
2722 my $nodename = nodename
();
2724 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2725 my ($ds, $drive) = @_;
2727 my $volid = $drive->{file
};
2730 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2732 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2733 if ($scfg->{disable
}) {
2735 } elsif (my $avail = $scfg->{nodes
}) {
2736 foreach my $node (keys %$nodehash) {
2737 delete $nodehash->{$node} if !$avail->{$node};
2739 } elsif (!$scfg->{shared
}) {
2740 foreach my $node (keys %$nodehash) {
2741 delete $nodehash->{$node} if $node ne $nodename
2750 sub check_local_storage_availability
{
2751 my ($conf, $storecfg) = @_;
2753 my $nodelist = PVE
::Cluster
::get_nodelist
();
2754 my $nodehash = { map { $_ => {} } @$nodelist };
2756 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2757 my ($ds, $drive) = @_;
2759 my $volid = $drive->{file
};
2762 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2764 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2766 if ($scfg->{disable
}) {
2767 foreach my $node (keys %$nodehash) {
2768 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2770 } elsif (my $avail = $scfg->{nodes
}) {
2771 foreach my $node (keys %$nodehash) {
2772 if (!$avail->{$node}) {
2773 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2780 foreach my $node (values %$nodehash) {
2781 if (my $unavail = $node->{unavailable_storages
}) {
2782 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2789 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2791 my ($vmid, $nocheck, $node) = @_;
2793 # $nocheck is set when called during a migration, in which case the config
2794 # file might still or already reside on the *other* node
2795 # - because rename has already happened, and current node is source
2796 # - because rename hasn't happened yet, and current node is target
2797 # - because rename has happened, current node is target, but hasn't yet
2799 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2800 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2805 my $vzlist = config_list
();
2807 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2809 while (defined(my $de = $fd->read)) {
2810 next if $de !~ m/^(\d+)\.pid$/;
2812 next if !defined($vzlist->{$vmid});
2813 if (my $pid = check_running
($vmid)) {
2814 $vzlist->{$vmid}->{pid
} = $pid;
2821 our $vmstatus_return_properties = {
2822 vmid
=> get_standard_option
('pve-vmid'),
2824 description
=> "QEMU process status.",
2826 enum
=> ['stopped', 'running'],
2829 description
=> "Maximum memory in bytes.",
2832 renderer
=> 'bytes',
2835 description
=> "Root disk size in bytes.",
2838 renderer
=> 'bytes',
2841 description
=> "VM name.",
2846 description
=> "VM run state from the 'query-status' QMP monitor command.",
2851 description
=> "PID of running qemu process.",
2856 description
=> "Uptime.",
2859 renderer
=> 'duration',
2862 description
=> "Maximum usable CPUs.",
2867 description
=> "The current config lock, if any.",
2872 description
=> "The current configured tags, if any",
2876 'running-machine' => {
2877 description
=> "The currently running machine type (if running).",
2882 description
=> "The currently running QEMU version (if running).",
2888 my $last_proc_pid_stat;
2890 # get VM status information
2891 # This must be fast and should not block ($full == false)
2892 # We only query KVM using QMP if $full == true (this can be slow)
2894 my ($opt_vmid, $full) = @_;
2898 my $storecfg = PVE
::Storage
::config
();
2900 my $list = vzlist
();
2901 my $defaults = load_defaults
();
2903 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
(1);
2905 my $cpucount = $cpuinfo->{cpus
} || 1;
2907 foreach my $vmid (keys %$list) {
2908 next if $opt_vmid && ($vmid ne $opt_vmid);
2910 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2912 my $d = { vmid
=> int($vmid) };
2913 $d->{pid
} = int($list->{$vmid}->{pid
}) if $list->{$vmid}->{pid
};
2915 # fixme: better status?
2916 $d->{status
} = $list->{$vmid}->{pid
} ?
'running' : 'stopped';
2918 my $size = PVE
::QemuServer
::Drive
::bootdisk_size
($storecfg, $conf);
2919 if (defined($size)) {
2920 $d->{disk
} = 0; # no info available
2921 $d->{maxdisk
} = $size;
2927 $d->{cpus
} = ($conf->{sockets
} || $defaults->{sockets
})
2928 * ($conf->{cores
} || $defaults->{cores
});
2929 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
2930 $d->{cpus
} = $conf->{vcpus
} if $conf->{vcpus
};
2932 $d->{name
} = $conf->{name
} || "VM $vmid";
2933 $d->{maxmem
} = get_current_memory
($conf->{memory
})*(1024*1024);
2935 if ($conf->{balloon
}) {
2936 $d->{balloon_min
} = $conf->{balloon
}*(1024*1024);
2937 $d->{shares
} = defined($conf->{shares
}) ?
$conf->{shares
}
2938 : $defaults->{shares
};
2949 $d->{diskwrite
} = 0;
2951 $d->{template
} = 1 if PVE
::QemuConfig-
>is_template($conf);
2953 $d->{serial
} = 1 if conf_has_serial
($conf);
2954 $d->{lock} = $conf->{lock} if $conf->{lock};
2955 $d->{tags
} = $conf->{tags
} if defined($conf->{tags
});
2960 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
2961 foreach my $dev (keys %$netdev) {
2962 next if $dev !~ m/^tap([1-9]\d*)i/;
2964 my $d = $res->{$vmid};
2967 $d->{netout
} += $netdev->{$dev}->{receive
};
2968 $d->{netin
} += $netdev->{$dev}->{transmit
};
2971 $d->{nics
}->{$dev}->{netout
} = int($netdev->{$dev}->{receive
});
2972 $d->{nics
}->{$dev}->{netin
} = int($netdev->{$dev}->{transmit
});
2977 my $ctime = gettimeofday
;
2979 foreach my $vmid (keys %$list) {
2981 my $d = $res->{$vmid};
2982 my $pid = $d->{pid
};
2985 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
2986 next if !$pstat; # not running
2988 my $used = $pstat->{utime} + $pstat->{stime
};
2990 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
2992 if ($pstat->{vsize
}) {
2993 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
2996 my $old = $last_proc_pid_stat->{$pid};
2998 $last_proc_pid_stat->{$pid} = {
3006 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
3008 if ($dtime > 1000) {
3009 my $dutime = $used - $old->{used
};
3011 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
3012 $last_proc_pid_stat->{$pid} = {
3018 $d->{cpu
} = $old->{cpu
};
3022 return $res if !$full;
3024 my $qmpclient = PVE
::QMPClient-
>new();
3026 my $ballooncb = sub {
3027 my ($vmid, $resp) = @_;
3029 my $info = $resp->{'return'};
3030 return if !$info->{max_mem
};
3032 my $d = $res->{$vmid};
3034 # use memory assigned to VM
3035 $d->{maxmem
} = $info->{max_mem
};
3036 $d->{balloon
} = $info->{actual
};
3038 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
3039 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
3040 $d->{freemem
} = $info->{free_mem
};
3043 $d->{ballooninfo
} = $info;
3046 my $blockstatscb = sub {
3047 my ($vmid, $resp) = @_;
3048 my $data = $resp->{'return'} || [];
3049 my $totalrdbytes = 0;
3050 my $totalwrbytes = 0;
3052 for my $blockstat (@$data) {
3053 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
3054 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
3056 $blockstat->{device
} =~ s/drive-//;
3057 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
3059 $res->{$vmid}->{diskread
} = $totalrdbytes;
3060 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
3063 my $machinecb = sub {
3064 my ($vmid, $resp) = @_;
3065 my $data = $resp->{'return'} || [];
3067 $res->{$vmid}->{'running-machine'} =
3068 PVE
::QemuServer
::Machine
::current_from_query_machines
($data);
3071 my $versioncb = sub {
3072 my ($vmid, $resp) = @_;
3073 my $data = $resp->{'return'} // {};
3074 my $version = 'unknown';
3076 if (my $v = $data->{qemu
}) {
3077 $version = $v->{major
} . "." . $v->{minor
} . "." . $v->{micro
};
3080 $res->{$vmid}->{'running-qemu'} = $version;
3083 my $statuscb = sub {
3084 my ($vmid, $resp) = @_;
3086 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
3087 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
3088 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
3089 # this fails if ballon driver is not loaded, so this must be
3090 # the last commnand (following command are aborted if this fails).
3091 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
3093 my $status = 'unknown';
3094 if (!defined($status = $resp->{'return'}->{status
})) {
3095 warn "unable to get VM status\n";
3099 $res->{$vmid}->{qmpstatus
} = $resp->{'return'}->{status
};
3102 foreach my $vmid (keys %$list) {
3103 next if $opt_vmid && ($vmid ne $opt_vmid);
3104 next if !$res->{$vmid}->{pid
}; # not running
3105 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
3108 $qmpclient->queue_execute(undef, 2);
3110 foreach my $vmid (keys %$list) {
3111 next if $opt_vmid && ($vmid ne $opt_vmid);
3112 next if !$res->{$vmid}->{pid
}; #not running
3114 # we can't use the $qmpclient since it might have already aborted on
3115 # 'query-balloon', but this might also fail for older versions...
3116 my $qemu_support = eval { mon_cmd
($vmid, "query-proxmox-support") };
3117 $res->{$vmid}->{'proxmox-support'} = $qemu_support // {};
3120 foreach my $vmid (keys %$list) {
3121 next if $opt_vmid && ($vmid ne $opt_vmid);
3122 $res->{$vmid}->{qmpstatus
} = $res->{$vmid}->{status
} if !$res->{$vmid}->{qmpstatus
};
3128 sub conf_has_serial
{
3131 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3132 if ($conf->{"serial$i"}) {
3140 sub conf_has_audio
{
3141 my ($conf, $id) = @_;
3144 my $audio = $conf->{"audio$id"};
3145 return if !defined($audio);
3147 my $audioproperties = parse_property_string
($audio_fmt, $audio);
3148 my $audiodriver = $audioproperties->{driver
} // 'spice';
3151 dev
=> $audioproperties->{device
},
3152 dev_id
=> "audiodev$id",
3153 backend
=> $audiodriver,
3154 backend_id
=> "$audiodriver-backend${id}",
3159 my ($audio, $audiopciaddr, $machine_version) = @_;
3163 my $id = $audio->{dev_id
};
3165 if (min_version
($machine_version, 4, 2)) {
3166 $audiodev = ",audiodev=$audio->{backend_id}";
3169 if ($audio->{dev
} eq 'AC97') {
3170 push @$devs, '-device', "AC97,id=${id}${audiopciaddr}$audiodev";
3171 } elsif ($audio->{dev
} =~ /intel\-hda$/) {
3172 push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
3173 push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0$audiodev";
3174 push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1$audiodev";
3176 die "unkown audio device '$audio->{dev}', implement me!";
3179 push @$devs, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
3187 socket => "/var/run/qemu-server/$vmid.swtpm",
3188 pid
=> "/var/run/qemu-server/$vmid.swtpm.pid",
3192 sub add_tpm_device
{
3193 my ($vmid, $devices, $conf) = @_;
3195 return if !$conf->{tpmstate0
};
3197 my $paths = get_tpm_paths
($vmid);
3199 push @$devices, "-chardev", "socket,id=tpmchar,path=$paths->{socket}";
3200 push @$devices, "-tpmdev", "emulator,id=tpmdev,chardev=tpmchar";
3201 push @$devices, "-device", "tpm-tis,tpmdev=tpmdev";
3205 my ($storecfg, $vmid, $tpmdrive, $migration) = @_;
3207 return if !$tpmdrive;
3210 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
3211 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
3213 $state = PVE
::Storage
::map_volume
($storecfg, $tpm->{file
});
3215 $state = $tpm->{file
};
3218 my $paths = get_tpm_paths
($vmid);
3220 # during migration, we will get state from remote
3223 # run swtpm_setup to create a new TPM state if it doesn't exist yet
3230 "--create-platform-cert",
3233 "/etc/swtpm_setup.conf", # do not use XDG configs
3235 "0", # force creation as root, error if not possible
3236 "--not-overwrite", # ignore existing state, do not modify
3239 push @$setup_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3240 # TPM 2.0 supports ECC crypto, use if possible
3241 push @$setup_cmd, "--ecc" if $tpm->{version
} eq 'v2.0';
3243 run_command
($setup_cmd, outfunc
=> sub {
3244 print "swtpm_setup: $1\n";
3248 # Used to distinguish different invocations in the log.
3249 my $log_prefix = "[id=" . int(time()) . "] ";
3251 my $emulator_cmd = [
3255 "backend-uri=file://$state,mode=0600",
3257 "type=unixio,path=$paths->{socket},mode=0600",
3259 "file=$paths->{pid}",
3260 "--terminate", # terminate on QEMU disconnect
3263 "file=/run/qemu-server/$vmid-swtpm.log,level=1,prefix=$log_prefix",
3265 push @$emulator_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3266 run_command
($emulator_cmd, outfunc
=> sub { print $1; });
3268 my $tries = 100; # swtpm may take a bit to start before daemonizing, wait up to 5s for pid
3269 while (! -e
$paths->{pid
}) {
3270 die "failed to start swtpm: pid file '$paths->{pid}' wasn't created.\n" if --$tries == 0;
3274 # return untainted PID of swtpm daemon so it can be killed on error
3275 file_read_firstline
($paths->{pid
}) =~ m/(\d+)/;
3279 sub vga_conf_has_spice
{
3282 my $vgaconf = parse_vga
($vga);
3283 my $vgatype = $vgaconf->{type
};
3284 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
3291 return get_host_arch
() eq $arch;
3296 return $conf->{arch
} // get_host_arch
();
3299 my $default_machines = {
3304 sub get_installed_machine_version
{
3305 my ($kvmversion) = @_;
3306 $kvmversion = kvm_user_version
() if !defined($kvmversion);
3307 $kvmversion =~ m/^(\d+\.\d+)/;
3311 sub windows_get_pinned_machine_version
{
3312 my ($machine, $base_version, $kvmversion) = @_;
3314 my $pin_version = $base_version;
3315 if (!defined($base_version) ||
3316 !PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($base_version, $kvmversion)
3318 $pin_version = get_installed_machine_version
($kvmversion);
3320 if (!$machine || $machine eq 'pc') {
3321 $machine = "pc-i440fx-$pin_version";
3322 } elsif ($machine eq 'q35') {
3323 $machine = "pc-q35-$pin_version";
3324 } elsif ($machine eq 'virt') {
3325 $machine = "virt-$pin_version";
3327 warn "unknown machine type '$machine', not touching that!\n";
3333 sub get_vm_machine
{
3334 my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
3336 my $machine = $forcemachine || $conf->{machine
};
3338 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
3339 $kvmversion //= kvm_user_version
();
3340 # we must pin Windows VMs without a specific version to 5.1, as 5.2 fixed a bug in ACPI
3341 # layout which confuses windows quite a bit and may result in various regressions..
3342 # see: https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg08484.html
3343 if (windows_version
($conf->{ostype
})) {
3344 $machine = windows_get_pinned_machine_version
($machine, '5.1', $kvmversion);
3347 $machine ||= $default_machines->{$arch};
3348 if ($add_pve_version) {
3349 my $pvever = PVE
::QemuServer
::Machine
::get_pve_version
($kvmversion);
3350 $machine .= "+pve$pvever";
3354 if ($add_pve_version && $machine !~ m/\+pve\d+?(?:\.pxe)?$/) {
3355 my $is_pxe = $machine =~ m/^(.*?)\.pxe$/;
3356 $machine = $1 if $is_pxe;
3358 # for version-pinned machines that do not include a pve-version (e.g.
3359 # pc-q35-4.1), we assume 0 to keep them stable in case we bump
3360 $machine .= '+pve0';
3362 $machine .= '.pxe' if $is_pxe;
3368 sub get_ovmf_files
($$$) {
3369 my ($arch, $efidisk, $smm) = @_;
3371 my $types = $OVMF->{$arch}
3372 or die "no OVMF images known for architecture '$arch'\n";
3374 my $type = 'default';
3375 if ($arch eq 'x86_64') {
3376 if (defined($efidisk->{efitype
}) && $efidisk->{efitype
} eq '4m') {
3377 $type = $smm ?
"4m" : "4m-no-smm";
3378 $type .= '-ms' if $efidisk->{'pre-enrolled-keys'};
3380 # TODO: log_warn about use of legacy images for x86_64 with Promxox VE 9
3384 my ($ovmf_code, $ovmf_vars) = $types->{$type}->@*;
3385 die "EFI base image '$ovmf_code' not found\n" if ! -f
$ovmf_code;
3386 die "EFI vars image '$ovmf_vars' not found\n" if ! -f
$ovmf_vars;
3388 return ($ovmf_code, $ovmf_vars);
3392 aarch64
=> '/usr/bin/qemu-system-aarch64',
3393 x86_64
=> '/usr/bin/qemu-system-x86_64',
3395 sub get_command_for_arch
($) {
3397 return '/usr/bin/kvm' if is_native
($arch);
3399 my $cmd = $Arch2Qemu->{$arch}
3400 or die "don't know how to emulate architecture '$arch'\n";
3404 # To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
3405 # to use in a QEMU command line (-cpu element), first array_intersect the result
3406 # of query_supported_ with query_understood_. This is necessary because:
3408 # a) query_understood_ returns flags the host cannot use and
3409 # b) query_supported_ (rather the QMP call) doesn't actually return CPU
3410 # flags, but CPU settings - with most of them being flags. Those settings
3411 # (and some flags, curiously) cannot be specified as a "-cpu" argument.
3413 # query_supported_ needs to start up to 2 temporary VMs and is therefore rather
3414 # expensive. If you need the value returned from this, you can get it much
3415 # cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags-$accel') with
3416 # $accel being 'kvm' or 'tcg'.
3418 # pvestatd calls this function on startup and whenever the QEMU/KVM version
3419 # changes, automatically populating pmxcfs.
3421 # Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
3422 # since kvm and tcg machines support different flags
3424 sub query_supported_cpu_flags
{
3427 $arch //= get_host_arch
();
3428 my $default_machine = $default_machines->{$arch};
3432 # FIXME: Once this is merged, the code below should work for ARM as well:
3433 # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
3434 die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
3437 my $kvm_supported = defined(kvm_version
());
3438 my $qemu_cmd = get_command_for_arch
($arch);
3440 my $pidfile = PVE
::QemuServer
::Helpers
::pidfile_name
($fakevmid);
3442 # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
3443 my $query_supported_run_qemu = sub {
3449 '-machine', $default_machine,
3451 '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
3452 '-mon', 'chardev=qmp,mode=control',
3453 '-pidfile', $pidfile,
3458 push @$cmd, '-accel', 'tcg';
3461 my $rc = run_command
($cmd, noerr
=> 1, quiet
=> 0);
3462 die "QEMU flag querying VM exited with code " . $rc if $rc;
3465 my $cmd_result = mon_cmd
(
3467 'query-cpu-model-expansion',
3469 model
=> { name
=> 'host' }
3472 my $props = $cmd_result->{model
}->{props
};
3473 foreach my $prop (keys %$props) {
3474 next if $props->{$prop} ne '1';
3475 # QEMU returns some flags multiple times, with '_', '.' or '-'
3476 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
3477 # We only keep those with underscores, to match /proc/cpuinfo
3478 $prop =~ s/\.|-/_/g;
3479 $flags->{$prop} = 1;
3484 # force stop with 10 sec timeout and 'nocheck', always stop, even if QMP failed
3485 vm_stop
(undef, $fakevmid, 1, 1, 10, 0, 1);
3489 return [ sort keys %$flags ];
3492 # We need to query QEMU twice, since KVM and TCG have different supported flags
3493 PVE
::QemuConfig-
>lock_config($fakevmid, sub {
3494 $flags->{tcg
} = eval { $query_supported_run_qemu->(0) };
3495 warn "warning: failed querying supported tcg flags: $@\n" if $@;
3497 if ($kvm_supported) {
3498 $flags->{kvm
} = eval { $query_supported_run_qemu->(1) };
3499 warn "warning: failed querying supported kvm flags: $@\n" if $@;
3506 # Understood CPU flags are written to a file at 'pve-qemu' compile time
3507 my $understood_cpu_flag_dir = "/usr/share/kvm";
3508 sub query_understood_cpu_flags
{
3509 my $arch = get_host_arch
();
3510 my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
3512 die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
3515 my $raw = file_get_contents
($filepath);
3516 $raw =~ s/^\s+|\s+$//g;
3517 my @flags = split(/\s+/, $raw);
3522 # Since commit 277d33454f77ec1d1e0bc04e37621e4dd2424b67 in pve-qemu, smm is not off by default
3523 # anymore. But smm=off seems to be required when using SeaBIOS and serial display.
3524 my sub should_disable_smm
{
3525 my ($conf, $vga, $machine) = @_;
3527 return if $machine =~ m/^virt/; # there is no smm flag that could be disabled
3529 return (!defined($conf->{bios
}) || $conf->{bios
} eq 'seabios') &&
3530 $vga->{type
} && $vga->{type
} =~ m/^(serial\d+|none)$/;
3533 my sub print_ovmf_drive_commandlines
{
3534 my ($conf, $storecfg, $vmid, $arch, $q35, $version_guard) = @_;
3536 my $d = $conf->{efidisk0
} ? parse_drive
('efidisk0', $conf->{efidisk0
}) : undef;
3538 my ($ovmf_code, $ovmf_vars) = get_ovmf_files
($arch, $d, $q35);
3540 my $var_drive_str = "if=pflash,unit=1,id=drive-efidisk0";
3542 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($d->{file
}, 1);
3543 my ($path, $format) = $d->@{'file', 'format'};
3545 $path = PVE
::Storage
::path
($storecfg, $d->{file
});
3546 if (!defined($format)) {
3547 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
3548 $format = qemu_img_format
($scfg, $volname);
3550 } elsif (!defined($format)) {
3551 die "efidisk format must be specified\n";
3553 # SPI flash does lots of read-modify-write OPs, without writeback this gets really slow #3329
3554 if ($path =~ m/^rbd:/) {
3555 $var_drive_str .= ',cache=writeback';
3556 $path .= ':rbd_cache_policy=writeback'; # avoid write-around, we *need* to cache writes too
3558 $var_drive_str .= ",format=$format,file=$path";
3560 $var_drive_str .= ",size=" . (-s
$ovmf_vars) if $format eq 'raw' && $version_guard->(4, 1, 2);
3561 $var_drive_str .= ',readonly=on' if drive_is_read_only
($conf, $d);
3563 log_warn
("no efidisk configured! Using temporary efivars disk.");
3564 my $path = "/tmp/$vmid-ovmf.fd";
3565 PVE
::Tools
::file_copy
($ovmf_vars, $path, -s
$ovmf_vars);
3566 $var_drive_str .= ",format=raw,file=$path";
3567 $var_drive_str .= ",size=" . (-s
$ovmf_vars) if $version_guard->(4, 1, 2);
3570 return ("if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code", $var_drive_str);
3573 sub config_to_command
{
3574 my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
3577 my ($globalFlags, $machineFlags, $rtcFlags) = ([], [], []);
3580 my $ostype = $conf->{ostype
};
3581 my $winversion = windows_version
($ostype);
3582 my $kvm = $conf->{kvm
};
3583 my $nodename = nodename
();
3585 my $arch = get_vm_arch
($conf);
3586 my $kvm_binary = get_command_for_arch
($arch);
3587 my $kvmver = kvm_user_version
($kvm_binary);
3589 if (!$kvmver || $kvmver !~ m/^(\d+)\.(\d+)/ || $1 < 3) {
3590 $kvmver //= "undefined";
3591 die "Detected old QEMU binary ('$kvmver', at least 3.0 is required)\n";
3594 my $add_pve_version = min_version
($kvmver, 4, 1);
3596 my $machine_type = get_vm_machine
($conf, $forcemachine, $arch, $add_pve_version);
3597 my $machine_version = extract_version
($machine_type, $kvmver);
3598 $kvm //= 1 if is_native
($arch);
3600 $machine_version =~ m/(\d+)\.(\d+)/;
3601 my ($machine_major, $machine_minor) = ($1, $2);
3603 if ($kvmver =~ m/^\d+\.\d+\.(\d+)/ && $1 >= 90) {
3604 warn "warning: Installed QEMU version ($kvmver) is a release candidate, ignoring version checks\n";
3605 } elsif (!min_version
($kvmver, $machine_major, $machine_minor)) {
3606 die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type',"
3607 ." please upgrade node '$nodename'\n"
3608 } elsif (!PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($machine_version, $kvmver)) {
3609 my $max_pve_version = PVE
::QemuServer
::Machine
::get_pve_version
($machine_version);
3610 die "Installed qemu-server (max feature level for $machine_major.$machine_minor is"
3611 ." pve$max_pve_version) is too old to run machine type '$machine_type', please upgrade"
3612 ." node '$nodename'\n";
3615 # if a specific +pve version is required for a feature, use $version_guard
3616 # instead of min_version to allow machines to be run with the minimum
3618 my $required_pve_version = 0;
3619 my $version_guard = sub {
3620 my ($major, $minor, $pve) = @_;
3621 return 0 if !min_version
($machine_version, $major, $minor, $pve);
3622 my $max_pve = PVE
::QemuServer
::Machine
::get_pve_version
("$major.$minor");
3623 return 1 if min_version
($machine_version, $major, $minor, $max_pve+1);
3624 $required_pve_version = $pve if $pve && $pve > $required_pve_version;
3628 if ($kvm && !defined kvm_version
()) {
3629 die "KVM virtualisation configured, but not available. Either disable in VM configuration"
3630 ." or enable in BIOS.\n";
3633 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3634 my $hotplug_features = parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
3635 my $use_old_bios_files = undef;
3636 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3639 if ($conf->{affinity
}) {
3640 push @$cmd, '/usr/bin/taskset', '--cpu-list', '--all-tasks', $conf->{affinity
};
3643 push @$cmd, $kvm_binary;
3645 push @$cmd, '-id', $vmid;
3647 my $vmname = $conf->{name
} || "vm$vmid";
3649 push @$cmd, '-name', "$vmname,debug-threads=on";
3651 push @$cmd, '-no-shutdown';
3655 my $qmpsocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid);
3656 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server=on,wait=off";
3657 push @$cmd, '-mon', "chardev=qmp,mode=control";
3659 if (min_version
($machine_version, 2, 12)) {
3660 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3661 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3664 push @$cmd, '-pidfile' , PVE
::QemuServer
::Helpers
::pidfile_name
($vmid);
3666 push @$cmd, '-daemonize';
3668 if ($conf->{smbios1
}) {
3669 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
3670 if ($smbios_conf->{base64
}) {
3671 # Do not pass base64 flag to qemu
3672 delete $smbios_conf->{base64
};
3673 my $smbios_string = "";
3674 foreach my $key (keys %$smbios_conf) {
3676 if ($key eq "uuid") {
3677 $value = $smbios_conf->{uuid
}
3679 $value = decode_base64
($smbios_conf->{$key});
3681 # qemu accepts any binary data, only commas need escaping by double comma
3683 $smbios_string .= "," . $key . "=" . $value if $value;
3685 push @$cmd, '-smbios', "type=1" . $smbios_string;
3687 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3691 if ($conf->{bios
} && $conf->{bios
} eq 'ovmf') {
3692 my ($code_drive_str, $var_drive_str) =
3693 print_ovmf_drive_commandlines
($conf, $storecfg, $vmid, $arch, $q35, $version_guard);
3694 push $cmd->@*, '-drive', $code_drive_str;
3695 push $cmd->@*, '-drive', $var_drive_str;
3698 if ($q35) { # tell QEMU to load q35 config early
3699 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3700 if (min_version
($machine_version, 4, 0)) {
3701 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3703 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3707 if (defined(my $fixups = qemu_created_version_fixups
($conf, $forcemachine, $kvmver))) {
3708 push @$cmd, $fixups->@*;
3711 if ($conf->{vmgenid
}) {
3712 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid
};
3715 # add usb controllers
3716 my @usbcontrollers = PVE
::QemuServer
::USB
::get_usb_controllers
(
3717 $conf, $bridges, $arch, $machine_type, $machine_version);
3718 push @$devices, @usbcontrollers if @usbcontrollers;
3719 my $vga = parse_vga
($conf->{vga
});
3721 my $qxlnum = vga_conf_has_spice
($conf->{vga
});
3722 $vga->{type
} = 'qxl' if $qxlnum;
3724 if (!$vga->{type
}) {
3725 if ($arch eq 'aarch64') {
3726 $vga->{type
} = 'virtio';
3727 } elsif (min_version
($machine_version, 2, 9)) {
3728 $vga->{type
} = (!$winversion || $winversion >= 6) ?
'std' : 'cirrus';
3730 $vga->{type
} = ($winversion >= 6) ?
'std' : 'cirrus';
3734 # enable absolute mouse coordinates (needed by vnc)
3735 my $tablet = $conf->{tablet
};
3736 if (!defined($tablet)) {
3737 $tablet = $defaults->{tablet
};
3738 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3739 $tablet = 0 if $vga->{type
} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3743 push @$devices, '-device', print_tabletdevice_full
($conf, $arch) if $tablet;
3744 my $kbd = print_keyboarddevice_full
($conf, $arch);
3745 push @$devices, '-device', $kbd if defined($kbd);
3748 my $bootorder = device_bootorder
($conf);
3750 # host pci device passthrough
3751 my ($kvm_off, $gpu_passthrough, $legacy_igd, $pci_devices) = PVE
::QemuServer
::PCI
::print_hostpci_devices
(
3752 $vmid, $conf, $devices, $vga, $winversion, $bridges, $arch, $machine_type, $bootorder);
3755 my $usb_dev_features = {};
3756 $usb_dev_features->{spice_usb3
} = 1 if min_version
($machine_version, 4, 0);
3758 my @usbdevices = PVE
::QemuServer
::USB
::get_usb_devices
(
3759 $conf, $usb_dev_features, $bootorder, $machine_version);
3760 push @$devices, @usbdevices if @usbdevices;
3763 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3764 my $path = $conf->{"serial$i"} or next;
3765 if ($path eq 'socket') {
3766 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3767 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
3768 # On aarch64, serial0 is the UART device. QEMU only allows
3769 # connecting UART devices via the '-serial' command line, as
3770 # the device has a fixed slot on the hardware...
3771 if ($arch eq 'aarch64' && $i == 0) {
3772 push @$devices, '-serial', "chardev:serial$i";
3774 push @$devices, '-device', "isa-serial,chardev=serial$i";
3777 die "no such serial device\n" if ! -c
$path;
3778 push @$devices, '-chardev', "serial,id=serial$i,path=$path";
3779 push @$devices, '-device', "isa-serial,chardev=serial$i";
3784 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3785 if (my $path = $conf->{"parallel$i"}) {
3786 die "no such parallel device\n" if ! -c
$path;
3787 my $devtype = $path =~ m!^/dev/usb/lp! ?
'serial' : 'parallel';
3788 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3789 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3793 if (min_version
($machine_version, 4, 0) && (my $audio = conf_has_audio
($conf))) {
3794 my $audiopciaddr = print_pci_addr
("audio0", $bridges, $arch, $machine_type);
3795 my $audio_devs = audio_devs
($audio, $audiopciaddr, $machine_version);
3796 push @$devices, @$audio_devs;
3799 # Add a TPM only if the VM is not a template,
3800 # to support backing up template VMs even if the TPM disk is write-protected.
3801 add_tpm_device
($vmid, $devices, $conf) if (!PVE
::QemuConfig-
>is_template($conf));
3804 $sockets = $conf->{smp
} if $conf->{smp
}; # old style - no longer iused
3805 $sockets = $conf->{sockets
} if $conf->{sockets
};
3807 my $cores = $conf->{cores
} || 1;
3809 my $maxcpus = $sockets * $cores;
3811 my $vcpus = $conf->{vcpus
} ?
$conf->{vcpus
} : $maxcpus;
3813 my $allowed_vcpus = $cpuinfo->{cpus
};
3815 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n" if ($allowed_vcpus < $maxcpus);
3817 if ($hotplug_features->{cpu
} && min_version
($machine_version, 2, 7)) {
3818 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3819 for (my $i = 2; $i <= $vcpus; $i++) {
3820 my $cpustr = print_cpu_device
($conf,$i);
3821 push @$cmd, '-device', $cpustr;
3826 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3828 push @$cmd, '-nodefaults';
3830 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3832 push $machineFlags->@*, 'acpi=off' if defined($conf->{acpi
}) && $conf->{acpi
} == 0;
3834 push @$cmd, '-no-reboot' if defined($conf->{reboot
}) && $conf->{reboot
} == 0;
3836 if ($vga->{type
} && $vga->{type
} !~ m/^serial\d+$/ && $vga->{type
} ne 'none'){
3837 push @$devices, '-device', print_vga_device
(
3838 $conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
3840 push @$cmd, '-display', 'egl-headless,gl=core' if $vga->{type
} eq 'virtio-gl'; # VIRGL
3842 my $socket = PVE
::QemuServer
::Helpers
::vnc_socket
($vmid);
3843 push @$cmd, '-vnc', "unix:$socket,password=on";
3845 push @$cmd, '-vga', 'none' if $vga->{type
} eq 'none';
3846 push @$cmd, '-nographic';
3850 my $tdf = defined($conf->{tdf
}) ?
$conf->{tdf
} : $defaults->{tdf
};
3851 my $useLocaltime = $conf->{localtime};
3853 if ($winversion >= 5) { # windows
3854 $useLocaltime = 1 if !defined($conf->{localtime});
3856 # use time drift fix when acpi is enabled
3857 if (!(defined($conf->{acpi
}) && $conf->{acpi
} == 0)) {
3858 $tdf = 1 if !defined($conf->{tdf
});
3862 if ($winversion >= 6) {
3863 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3864 push @$machineFlags, 'hpet=off';
3867 push @$rtcFlags, 'driftfix=slew' if $tdf;
3869 if ($conf->{startdate
} && $conf->{startdate
} ne 'now') {
3870 push @$rtcFlags, "base=$conf->{startdate}";
3871 } elsif ($useLocaltime) {
3872 push @$rtcFlags, 'base=localtime';
3876 push @$cmd, '-cpu', $forcecpu;
3878 push @$cmd, get_cpu_options
($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
3881 PVE
::QemuServer
::Memory
::config
(
3882 $conf, $vmid, $sockets, $cores, $hotplug_features->{memory
}, $cmd);
3884 push @$cmd, '-S' if $conf->{freeze
};
3886 push @$cmd, '-k', $conf->{keyboard
} if defined($conf->{keyboard
});
3888 my $guest_agent = parse_guest_agent
($conf);
3890 if ($guest_agent->{enabled
}) {
3891 my $qgasocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid, 1);
3892 push @$devices, '-chardev', "socket,path=$qgasocket,server=on,wait=off,id=qga0";
3894 if (!$guest_agent->{type
} || $guest_agent->{type
} eq 'virtio') {
3895 my $pciaddr = print_pci_addr
("qga0", $bridges, $arch, $machine_type);
3896 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3897 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3898 } elsif ($guest_agent->{type
} eq 'isa') {
3899 push @$devices, '-device', "isa-serial,chardev=qga0";
3903 my $rng = $conf->{rng0
} ? parse_rng
($conf->{rng0
}) : undef;
3904 if ($rng && $version_guard->(4, 1, 2)) {
3905 check_rng_source
($rng->{source
});
3907 my $max_bytes = $rng->{max_bytes
} // $rng_fmt->{max_bytes
}->{default};
3908 my $period = $rng->{period
} // $rng_fmt->{period
}->{default};
3909 my $limiter_str = "";
3911 $limiter_str = ",max-bytes=$max_bytes,period=$period";
3914 my $rng_addr = print_pci_addr
("rng0", $bridges, $arch, $machine_type);
3915 push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
3916 push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
3921 assert_clipboard_config
($vga);
3922 my $is_spice = $qxlnum || $vga->{type
} =~ /^virtio/;
3924 if ($is_spice || ($vga->{'clipboard'} && $vga->{'clipboard'} eq 'vnc')) {
3927 for (my $i = 1; $i < $qxlnum; $i++){
3928 push @$devices, '-device', print_vga_device
(
3929 $conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
3932 # assume other OS works like Linux
3933 my ($ram, $vram) = ("134217728", "67108864");
3934 if ($vga->{memory
}) {
3935 $ram = PVE
::Tools
::convert_size
($qxlnum*4*$vga->{memory
}, 'mb' => 'b');
3936 $vram = PVE
::Tools
::convert_size
($qxlnum*2*$vga->{memory
}, 'mb' => 'b');
3938 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3939 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3943 my $pciaddr = print_pci_addr
("spice", $bridges, $arch, $machine_type);
3945 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3946 if ($vga->{'clipboard'} && $vga->{'clipboard'} eq 'vnc') {
3947 push @$devices, '-chardev', 'qemu-vdagent,id=vdagent,name=vdagent,clipboard=on';
3949 push @$devices, '-chardev', 'spicevmc,id=vdagent,name=vdagent';
3951 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3954 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
3955 my @nodeaddrs = PVE
::Tools
::getaddrinfo_all
('localhost', family
=> $pfamily);
3956 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3958 my $localhost = PVE
::Network
::addr_to_ip
($nodeaddrs[0]->{addr
});
3959 $spice_port = PVE
::Tools
::next_spice_port
($pfamily, $localhost);
3961 my $spice_enhancement_str = $conf->{spice_enhancements
} // '';
3962 my $spice_enhancement = parse_property_string
($spice_enhancements_fmt, $spice_enhancement_str);
3963 if ($spice_enhancement->{foldersharing
}) {
3964 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3965 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3968 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3969 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}"
3970 if $spice_enhancement->{videostreaming
};
3971 push @$devices, '-spice', "$spice_opts";
3975 # enable balloon by default, unless explicitly disabled
3976 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
3977 my $pciaddr = print_pci_addr
("balloon0", $bridges, $arch, $machine_type);
3978 my $ballooncmd = "virtio-balloon-pci,id=balloon0$pciaddr";
3979 $ballooncmd .= ",free-page-reporting=on" if min_version
($machine_version, 6, 2);
3980 push @$devices, '-device', $ballooncmd;
3983 if ($conf->{watchdog
}) {
3984 my $wdopts = parse_watchdog
($conf->{watchdog
});
3985 my $pciaddr = print_pci_addr
("watchdog", $bridges, $arch, $machine_type);
3986 my $watchdog = $wdopts->{model
} || 'i6300esb';
3987 push @$devices, '-device', "$watchdog$pciaddr";
3988 push @$devices, '-watchdog-action', $wdopts->{action
} if $wdopts->{action
};
3992 my $scsicontroller = {};
3993 my $ahcicontroller = {};
3994 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : $defaults->{scsihw
};
3996 # Add iscsi initiator name if available
3997 if (my $initiator = get_initiator_name
()) {
3998 push @$devices, '-iscsi', "initiator-name=$initiator";
4001 PVE
::QemuConfig-
>foreach_volume($conf, sub {
4002 my ($ds, $drive) = @_;
4004 if (PVE
::Storage
::parse_volume_id
($drive->{file
}, 1)) {
4005 check_volume_storage_type
($storecfg, $drive->{file
});
4006 push @$vollist, $drive->{file
};
4009 # ignore efidisk here, already added in bios/fw handling code above
4010 return if $drive->{interface
} eq 'efidisk';
4012 return if $drive->{interface
} eq 'tpmstate';
4014 $use_virtio = 1 if $ds =~ m/^virtio/;
4016 $drive->{bootindex
} = $bootorder->{$ds} if $bootorder->{$ds};
4018 if ($drive->{interface
} eq 'virtio'){
4019 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread
};
4022 if ($drive->{interface
} eq 'scsi') {
4024 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
4026 die "scsi$drive->{index}: machine version 4.1~pve2 or higher is required to use more than 14 SCSI disks\n"
4027 if $drive->{index} > 13 && !&$version_guard(4, 1, 2);
4029 my $pciaddr = print_pci_addr
("$controller_prefix$controller", $bridges, $arch, $machine_type);
4030 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ?
"virtio-scsi-pci" : $scsihw;
4033 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{iothread
}){
4034 $iothread .= ",iothread=iothread-$controller_prefix$controller";
4035 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
4036 } elsif ($drive->{iothread
}) {
4038 "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n"
4043 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{queues
}){
4044 $queues = ",num_queues=$drive->{queues}";
4047 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues"
4048 if !$scsicontroller->{$controller};
4049 $scsicontroller->{$controller}=1;
4052 if ($drive->{interface
} eq 'sata') {
4053 my $controller = int($drive->{index} / $PVE::QemuServer
::Drive
::MAX_SATA_DISKS
);
4054 my $pciaddr = print_pci_addr
("ahci$controller", $bridges, $arch, $machine_type);
4055 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr"
4056 if !$ahcicontroller->{$controller};
4057 $ahcicontroller->{$controller}=1;
4060 my $pbs_conf = $pbs_backing->{$ds};
4061 my $pbs_name = undef;
4063 $pbs_name = "drive-$ds-pbs";
4064 push @$devices, '-blockdev', print_pbs_blockdev
($pbs_conf, $pbs_name);
4067 my $drive_cmd = print_drive_commandline_full
(
4068 $storecfg, $vmid, $drive, $pbs_name, min_version
($kvmver, 6, 0));
4070 # extra protection for templates, but SATA and IDE don't support it..
4071 $drive_cmd .= ',readonly=on' if drive_is_read_only
($conf, $drive);
4073 push @$devices, '-drive',$drive_cmd;
4074 push @$devices, '-device', print_drivedevice_full
(
4075 $storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
4078 for (my $i = 0; $i < $MAX_NETS; $i++) {
4079 my $netname = "net$i";
4081 next if !$conf->{$netname};
4082 my $d = parse_net
($conf->{$netname});
4084 # save the MAC addr here (could be auto-gen. in some odd setups) for FDB registering later?
4086 $use_virtio = 1 if $d->{model
} eq 'virtio';
4088 $d->{bootindex
} = $bootorder->{$netname} if $bootorder->{$netname};
4090 my $netdevfull = print_netdev_full
($vmid, $conf, $arch, $d, $netname);
4091 push @$devices, '-netdev', $netdevfull;
4093 my $netdevicefull = print_netdevice_full
(
4094 $vmid, $conf, $d, $netname, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version);
4096 push @$devices, '-device', $netdevicefull;
4099 if ($conf->{ivshmem
}) {
4100 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
4104 $bus = print_pcie_addr
("ivshmem");
4106 $bus = print_pci_addr
("ivshmem", $bridges, $arch, $machine_type);
4109 my $ivshmem_name = $ivshmem->{name
} // $vmid;
4110 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
4112 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
4113 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path"
4114 .",size=$ivshmem->{size}M";
4117 # pci.4 is nested in pci.1
4118 $bridges->{1} = 1 if $bridges->{4};
4120 if (!$q35) { # add pci bridges
4121 if (min_version
($machine_version, 2, 3)) {
4125 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
4128 for my $k (sort {$b cmp $a} keys %$bridges) {
4129 next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
4132 if ($k == 2 && $legacy_igd) {
4135 my $pciaddr = print_pci_addr
("pci.$k_name", undef, $arch, $machine_type);
4136 my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
4138 if ($q35) { # add after -readconfig pve-q35.cfg
4139 splice @$devices, 2, 0, '-device', $devstr;
4141 unshift @$devices, '-device', $devstr if $k > 0;
4146 push @$machineFlags, 'accel=tcg';
4149 push @$machineFlags, 'smm=off' if should_disable_smm
($conf, $vga, $machine_type);
4151 my $machine_type_min = $machine_type;
4152 if ($add_pve_version) {
4153 $machine_type_min =~ s/\+pve\d+$//;
4154 $machine_type_min .= "+pve$required_pve_version";
4156 push @$machineFlags, "type=${machine_type_min}";
4158 push @$cmd, @$devices;
4159 push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
4160 push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
4161 push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags);
4163 if (my $vmstate = $conf->{vmstate
}) {
4164 my $statepath = PVE
::Storage
::path
($storecfg, $vmstate);
4165 push @$vollist, $vmstate;
4166 push @$cmd, '-loadstate', $statepath;
4167 print "activating and using '$vmstate' as vmstate\n";
4170 if (PVE
::QemuConfig-
>is_template($conf)) {
4171 # needed to workaround base volumes being read-only
4172 push @$cmd, '-snapshot';
4176 if ($conf->{args
}) {
4177 my $aa = PVE
::Tools
::split_args
($conf->{args
});
4181 return wantarray ?
($cmd, $vollist, $spice_port, $pci_devices) : $cmd;
4184 sub check_rng_source
{
4187 # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
4188 die "cannot create VirtIO RNG device: source file '$source' doesn't exist\n"
4191 my $rng_current = '/sys/devices/virtual/misc/hw_random/rng_current';
4192 if ($source eq '/dev/hwrng' && file_read_firstline
($rng_current) eq 'none') {
4193 # Needs to abort, otherwise QEMU crashes on first rng access. Note that rng_current cannot
4194 # be changed to 'none' manually, so once the VM is past this point, it's no longer an issue.
4195 die "Cannot start VM with passed-through RNG device: '/dev/hwrng' exists, but"
4196 ." '$rng_current' is set to 'none'. Ensure that a compatible hardware-RNG is attached"
4204 my $res = mon_cmd
($vmid, 'query-spice');
4206 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
4209 sub vm_devices_list
{
4212 my $res = mon_cmd
($vmid, 'query-pci');
4213 my $devices_to_check = [];
4215 foreach my $pcibus (@$res) {
4216 push @$devices_to_check, @{$pcibus->{devices
}},
4219 while (@$devices_to_check) {
4221 for my $d (@$devices_to_check) {
4222 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
4223 next if !$d->{'pci_bridge'} || !$d->{'pci_bridge'}->{devices
};
4225 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices
}});
4226 push @$to_check, @{$d->{'pci_bridge'}->{devices
}};
4228 $devices_to_check = $to_check;
4231 my $resblock = mon_cmd
($vmid, 'query-block');
4232 foreach my $block (@$resblock) {
4233 if($block->{device
} =~ m/^drive-(\S+)/){
4238 my $resmice = mon_cmd
($vmid, 'query-mice');
4239 foreach my $mice (@$resmice) {
4240 if ($mice->{name
} eq 'QEMU HID Tablet') {
4241 $devices->{tablet
} = 1;
4246 # for usb devices there is no query-usb
4247 # but we can iterate over the entries in
4248 # qom-list path=/machine/peripheral
4249 my $resperipheral = mon_cmd
($vmid, 'qom-list', path
=> '/machine/peripheral');
4250 foreach my $per (@$resperipheral) {
4251 if ($per->{name
} =~ m/^usb(?:redirdev)?\d+$/) {
4252 $devices->{$per->{name
}} = 1;
4260 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4262 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
4264 my $devices_list = vm_devices_list
($vmid);
4265 return 1 if defined($devices_list->{$deviceid});
4267 # add PCI bridge if we need it for the device
4268 qemu_add_pci_bridge
($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type);
4270 if ($deviceid eq 'tablet') {
4271 qemu_deviceadd
($vmid, print_tabletdevice_full
($conf, $arch));
4272 } elsif ($deviceid eq 'keyboard') {
4273 qemu_deviceadd
($vmid, print_keyboarddevice_full
($conf, $arch));
4274 } elsif ($deviceid =~ m/^usbredirdev(\d+)$/) {
4276 qemu_spice_usbredir_chardev_add
($vmid, "usbredirchardev$id");
4277 qemu_deviceadd
($vmid, PVE
::QemuServer
::USB
::print_spice_usbdevice
($id, "xhci", $id + 1));
4278 } elsif ($deviceid =~ m/^usb(\d+)$/) {
4279 qemu_deviceadd
($vmid, PVE
::QemuServer
::USB
::print_usbdevice_full
($conf, $deviceid, $device, {}, $1 + 1));
4280 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4281 qemu_iothread_add
($vmid, $deviceid, $device);
4283 qemu_driveadd
($storecfg, $vmid, $device);
4284 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4286 qemu_deviceadd
($vmid, $devicefull);
4287 eval { qemu_deviceaddverify
($vmid, $deviceid); };
4289 eval { qemu_drivedel
($vmid, $deviceid); };
4293 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4294 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : "lsi";
4295 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4296 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ?
"virtio-scsi-pci" : $scsihw;
4298 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
4300 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread
}) {
4301 qemu_iothread_add
($vmid, $deviceid, $device);
4302 $devicefull .= ",iothread=iothread-$deviceid";
4305 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues
}) {
4306 $devicefull .= ",num_queues=$device->{queues}";
4309 qemu_deviceadd
($vmid, $devicefull);
4310 qemu_deviceaddverify
($vmid, $deviceid);
4311 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4312 qemu_findorcreatescsihw
($storecfg,$conf, $vmid, $device, $arch, $machine_type);
4313 qemu_driveadd
($storecfg, $vmid, $device);
4315 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4316 eval { qemu_deviceadd
($vmid, $devicefull); };
4318 eval { qemu_drivedel
($vmid, $deviceid); };
4322 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4323 return if !qemu_netdevadd
($vmid, $conf, $arch, $device, $deviceid);
4325 my $machine_type = PVE
::QemuServer
::Machine
::qemu_machine_pxe
($vmid, $conf);
4326 my $machine_version = PVE
::QemuServer
::Machine
::extract_version
($machine_type);
4327 my $use_old_bios_files = undef;
4328 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
4330 my $netdevicefull = print_netdevice_full
(
4331 $vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type, $machine_version);
4332 qemu_deviceadd
($vmid, $netdevicefull);
4334 qemu_deviceaddverify
($vmid, $deviceid);
4335 qemu_set_link_status
($vmid, $deviceid, !$device->{link_down
});
4338 eval { qemu_netdevdel
($vmid, $deviceid); };
4342 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
4344 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4345 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
4347 qemu_deviceadd
($vmid, $devicefull);
4348 qemu_deviceaddverify
($vmid, $deviceid);
4350 die "can't hotplug device '$deviceid'\n";
4356 # fixme: this should raise exceptions on error!
4357 sub vm_deviceunplug
{
4358 my ($vmid, $conf, $deviceid) = @_;
4360 my $devices_list = vm_devices_list
($vmid);
4361 return 1 if !defined($devices_list->{$deviceid});
4363 my $bootdisks = PVE
::QemuServer
::Drive
::get_bootdisks
($conf);
4364 die "can't unplug bootdisk '$deviceid'\n" if grep {$_ eq $deviceid} @$bootdisks;
4366 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard' || $deviceid eq 'xhci') {
4367 qemu_devicedel
($vmid, $deviceid);
4368 } elsif ($deviceid =~ m/^usbredirdev\d+$/) {
4369 qemu_devicedel
($vmid, $deviceid);
4370 qemu_devicedelverify
($vmid, $deviceid);
4371 } elsif ($deviceid =~ m/^usb\d+$/) {
4372 qemu_devicedel
($vmid, $deviceid);
4373 qemu_devicedelverify
($vmid, $deviceid);
4374 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4375 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4377 qemu_devicedel
($vmid, $deviceid);
4378 qemu_devicedelverify
($vmid, $deviceid);
4379 qemu_drivedel
($vmid, $deviceid);
4380 qemu_iothread_del
($vmid, $deviceid, $device);
4381 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4382 qemu_devicedel
($vmid, $deviceid);
4383 qemu_devicedelverify
($vmid, $deviceid);
4384 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4385 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4387 qemu_devicedel
($vmid, $deviceid);
4388 qemu_devicedelverify
($vmid, $deviceid);
4389 qemu_drivedel
($vmid, $deviceid);
4390 qemu_deletescsihw
($conf, $vmid, $deviceid);
4392 qemu_iothread_del
($vmid, "virtioscsi$device->{index}", $device)
4393 if $conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single');
4394 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4395 qemu_devicedel
($vmid, $deviceid);
4396 qemu_devicedelverify
($vmid, $deviceid);
4397 qemu_netdevdel
($vmid, $deviceid);
4399 die "can't unplug device '$deviceid'\n";
4405 sub qemu_spice_usbredir_chardev_add
{
4406 my ($vmid, $id) = @_;
4408 mon_cmd
($vmid, "chardev-add" , (
4419 sub qemu_iothread_add
{
4420 my ($vmid, $deviceid, $device) = @_;
4422 if ($device->{iothread
}) {
4423 my $iothreads = vm_iothreads_list
($vmid);
4424 qemu_objectadd
($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4428 sub qemu_iothread_del
{
4429 my ($vmid, $deviceid, $device) = @_;
4431 if ($device->{iothread
}) {
4432 my $iothreads = vm_iothreads_list
($vmid);
4433 qemu_objectdel
($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4438 my ($storecfg, $vmid, $device) = @_;
4440 my $kvmver = get_running_qemu_version
($vmid);
4441 my $io_uring = min_version
($kvmver, 6, 0);
4442 my $drive = print_drive_commandline_full
($storecfg, $vmid, $device, undef, $io_uring);
4443 $drive =~ s/\\/\\\\/g;
4444 my $ret = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "drive_add auto \"$drive\"");
4446 # If the command succeeds qemu prints: "OK
"
4447 return 1 if $ret =~ m/OK/s;
4449 die "adding drive failed
: $ret\n";
4453 my ($vmid, $deviceid) = @_;
4455 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-
$deviceid");
4458 return 1 if $ret eq "";
4460 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4461 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4463 die "deleting drive
$deviceid failed
: $ret\n";
4466 sub qemu_deviceaddverify {
4467 my ($vmid, $deviceid) = @_;
4469 for (my $i = 0; $i <= 5; $i++) {
4470 my $devices_list = vm_devices_list($vmid);
4471 return 1 if defined($devices_list->{$deviceid});
4475 die "error on hotplug device
'$deviceid'\n";
4479 sub qemu_devicedelverify {
4480 my ($vmid, $deviceid) = @_;
4482 # need to verify that the device is correctly removed as device_del
4483 # is async and empty return is not reliable
4485 for (my $i = 0; $i <= 5; $i++) {
4486 my $devices_list = vm_devices_list($vmid);
4487 return 1 if !defined($devices_list->{$deviceid});
4491 die "error on hot-unplugging device
'$deviceid'\n";
4494 sub qemu_findorcreatescsihw {
4495 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4497 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4499 my $scsihwid="$controller_prefix$controller";
4500 my $devices_list = vm_devices_list($vmid);
4502 if (!defined($devices_list->{$scsihwid})) {
4503 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4509 sub qemu_deletescsihw {
4510 my ($conf, $vmid, $opt) = @_;
4512 my $device = parse_drive($opt, $conf->{$opt});
4514 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4515 vm_deviceunplug($vmid, $conf, "virtioscsi
$device->{index}");
4519 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4521 my $devices_list = vm_devices_list($vmid);
4522 foreach my $opt (keys %{$devices_list}) {
4523 if (is_valid_drivename($opt)) {
4524 my $drive = parse_drive($opt, $conf->{$opt});
4525 if ($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4531 my $scsihwid="scsihw
$controller";
4533 vm_deviceunplug($vmid, $conf, $scsihwid);
4538 sub qemu_add_pci_bridge {
4539 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4545 print_pci_addr($device, $bridges, $arch, $machine_type);
4547 while (my ($k, $v) = each %$bridges) {
4550 return 1 if !defined($bridgeid) || $bridgeid < 1;
4552 my $bridge = "pci
.$bridgeid";
4553 my $devices_list = vm_devices_list($vmid);
4555 if (!defined($devices_list->{$bridge})) {
4556 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4562 sub qemu_set_link_status {
4563 my ($vmid, $device, $up) = @_;
4565 mon_cmd($vmid, "set_link
", name => $device,
4566 up => $up ? JSON::true : JSON::false);
4569 sub qemu_netdevadd {
4570 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4572 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4573 my %options = split(/[=,]/, $netdev);
4575 if (defined(my $vhost = $options{vhost})) {
4576 $options{vhost} = JSON::boolean(PVE::JSONSchema::parse_boolean($vhost));
4579 if (defined(my $queues = $options{queues})) {
4580 $options{queues} = $queues + 0;
4583 mon_cmd($vmid, "netdev_add
", %options);
4587 sub qemu_netdevdel {
4588 my ($vmid, $deviceid) = @_;
4590 mon_cmd($vmid, "netdev_del
", id => $deviceid);
4593 sub qemu_usb_hotplug {
4594 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4598 # remove the old one first
4599 vm_deviceunplug($vmid, $conf, $deviceid);
4601 # check if xhci controller is necessary and available
4602 my $devicelist = vm_devices_list($vmid);
4604 if (!$devicelist->{xhci}) {
4605 my $pciaddr = print_pci_addr("xhci
", undef, $arch, $machine_type);
4606 qemu_deviceadd($vmid, PVE::QemuServer::USB::print_qemu_xhci_controller($pciaddr));
4610 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type);
4613 sub qemu_cpu_hotplug {
4614 my ($vmid, $conf, $vcpus) = @_;
4616 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
4619 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4620 $sockets = $conf->{sockets} if $conf->{sockets};
4621 my $cores = $conf->{cores} || 1;
4622 my $maxcpus = $sockets * $cores;
4624 $vcpus = $maxcpus if !$vcpus;
4626 die "you can
't add more vcpus than maxcpus\n"
4627 if $vcpus > $maxcpus;
4629 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4631 if ($vcpus < $currentvcpus) {
4633 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4635 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4636 qemu_devicedel($vmid, "cpu$i");
4638 my $currentrunningvcpus = undef;
4640 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4641 last if scalar(@{$currentrunningvcpus}) == $i-1;
4642 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4646 #update conf after each succesfull cpu unplug
4647 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4648 PVE::QemuConfig->write_config($vmid, $conf);
4651 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4657 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4658 die "vcpus in running vm does not match its configuration\n"
4659 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4661 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4663 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4664 my $cpustr = print_cpu_device($conf, $i);
4665 qemu_deviceadd($vmid, $cpustr);
4668 my $currentrunningvcpus = undef;
4670 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4671 last if scalar(@{$currentrunningvcpus}) == $i;
4672 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4676 #update conf after each succesfull cpu hotplug
4677 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4678 PVE::QemuConfig->write_config($vmid, $conf);
4682 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4683 mon_cmd($vmid, "cpu-add", id => int($i));
4688 sub qemu_block_set_io_throttle {
4689 my ($vmid, $deviceid,
4690 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4691 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4692 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4693 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4695 return if !check_running($vmid) ;
4697 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4699 bps_rd => int($bps_rd),
4700 bps_wr => int($bps_wr),
4702 iops_rd => int($iops_rd),
4703 iops_wr => int($iops_wr),
4704 bps_max => int($bps_max),
4705 bps_rd_max => int($bps_rd_max),
4706 bps_wr_max => int($bps_wr_max),
4707 iops_max => int($iops_max),
4708 iops_rd_max => int($iops_rd_max),
4709 iops_wr_max => int($iops_wr_max),
4710 bps_max_length => int($bps_max_length),
4711 bps_rd_max_length => int($bps_rd_max_length),
4712 bps_wr_max_length => int($bps_wr_max_length),
4713 iops_max_length => int($iops_max_length),
4714 iops_rd_max_length => int($iops_rd_max_length),
4715 iops_wr_max_length => int($iops_wr_max_length),
4720 sub qemu_block_resize {
4721 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4723 my $running = check_running($vmid);
4725 PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4727 return if !$running;
4729 my $padding = (1024 - $size % 1024) % 1024;
4730 $size = $size + $padding;
4735 device => $deviceid,
4741 sub qemu_volume_snapshot {
4742 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4744 my $running = check_running($vmid);
4746 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4747 mon_cmd($vmid, 'blockdev-snapshot-internal-sync
', device => $deviceid, name => $snap);
4749 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4753 sub qemu_volume_snapshot_delete {
4754 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4756 my $running = check_running($vmid);
4761 my $conf = PVE::QemuConfig->load_config($vmid);
4762 PVE::QemuConfig->foreach_volume($conf, sub {
4763 my ($ds, $drive) = @_;
4764 $running = 1 if $drive->{file} eq $volid;
4768 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4769 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync
', device => $deviceid, name => $snap);
4771 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4775 sub set_migration_caps {
4776 my ($vmid, $savevm) = @_;
4778 my $qemu_support = eval { mon_cmd($vmid, "query-proxmox-support") };
4780 my $bitmap_prop = $savevm ? 'pbs-dirty-bitmap-savevm
' : 'pbs-dirty-bitmap-migration
';
4781 my $dirty_bitmaps = $qemu_support->{$bitmap_prop} ? 1 : 0;
4786 "auto-converge" => 1,
4788 "x-rdma-pin-all" => 0,
4791 "dirty-bitmaps" => $dirty_bitmaps,
4794 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4796 for my $supported_capability (@$supported_capabilities) {
4798 capability => $supported_capability->{capability},
4799 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4803 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4807 my ($conf, $func, @param) = @_;
4811 my $test_volid = sub {
4812 my ($key, $drive, $snapname, $pending) = @_;
4814 my $volid = $drive->{file};
4817 $volhash->{$volid}->{cdrom} //= 1;
4818 $volhash->{$volid}->{cdrom} = 0 if !drive_is_cdrom($drive);
4820 my $replicate = $drive->{replicate} // 1;
4821 $volhash->{$volid}->{replicate} //= 0;
4822 $volhash->{$volid}->{replicate} = 1 if $replicate;
4824 $volhash->{$volid}->{shared} //= 0;
4825 $volhash->{$volid}->{shared} = 1 if $drive->{shared};
4827 $volhash->{$volid}->{is_unused} //= 0;
4828 $volhash->{$volid}->{is_unused} = 1 if $key =~ /^unused\d+$/;
4830 $volhash->{$volid}->{is_attached} //= 0;
4831 $volhash->{$volid}->{is_attached} = 1
4832 if !$volhash->{$volid}->{is_unused} && !defined($snapname) && !$pending;
4834 $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
4835 if defined($snapname);
4837 $volhash->{$volid}->{referenced_in_pending} = 1 if $pending;
4839 my $size = $drive->{size};
4840 $volhash->{$volid}->{size} //= $size if $size;
4842 $volhash->{$volid}->{is_vmstate} //= 0;
4843 $volhash->{$volid}->{is_vmstate} = 1 if $key eq 'vmstate
';
4845 $volhash->{$volid}->{is_tpmstate} //= 0;
4846 $volhash->{$volid}->{is_tpmstate} = 1 if $key eq 'tpmstate0
';
4848 $volhash->{$volid}->{drivename} = $key if is_valid_drivename($key);
4851 my $include_opts = {
4852 extra_keys => ['vmstate
'],
4853 include_unused => 1,
4856 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $test_volid);
4858 PVE::QemuConfig->foreach_volume_full($conf->{pending}, $include_opts, $test_volid, undef, 1)
4859 if defined($conf->{pending}) && $conf->{pending}->%*;
4861 foreach my $snapname (keys %{$conf->{snapshots}}) {
4862 my $snap = $conf->{snapshots}->{$snapname};
4863 PVE::QemuConfig->foreach_volume_full($snap, $include_opts, $test_volid, $snapname);
4866 foreach my $volid (keys %$volhash) {
4867 &$func($volid, $volhash->{$volid}, @param);
4871 my $fast_plug_option = {
4875 'migrate_downtime
' => 1,
4876 'migrate_speed
' => 1,
4883 'vmstatestorage
' => 1,
4886 for my $opt (keys %$confdesc_cloudinit) {
4887 $fast_plug_option->{$opt} = 1;
4890 # hotplug changes in [PENDING]
4891 # $selection hash can be used to only apply specified options, for
4892 # example: { cores => 1 } (only apply changed 'cores
')
4893 # $errors ref is used to return error messages
4894 sub vmconfig_hotplug_pending {
4895 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4897 my $defaults = load_defaults();
4898 my $arch = get_vm_arch($conf);
4899 my $machine_type = get_vm_machine($conf, undef, $arch);
4901 # commit values which do not have any impact on running VM first
4902 # Note: those option cannot raise errors, we we do not care about
4903 # $selection and always apply them.
4905 my $add_error = sub {
4906 my ($opt, $msg) = @_;
4907 $errors->{$opt} = "hotplug problem - $msg";
4910 my $cloudinit_pending_properties = PVE::QemuServer::cloudinit_pending_properties();
4912 my $cloudinit_record_changed = sub {
4913 my ($conf, $opt, $old, $new) = @_;
4914 return if !$cloudinit_pending_properties->{$opt};
4916 my $ci = ($conf->{cloudinit} //= {});
4918 my $recorded = $ci->{$opt};
4919 my %added = map { $_ => 1 } PVE::Tools::split_list(delete($ci->{added}) // '');
4921 if (defined($new)) {
4922 if (defined($old)) {
4923 # an existing value is being modified
4924 if (defined($recorded)) {
4925 # the value was already not in sync
4926 if ($new eq $recorded) {
4927 # a value is being reverted to the cloud-init state:
4929 delete $added{$opt};
4931 # the value was changed multiple times, do nothing
4933 } elsif ($added{$opt}) {
4934 # the value had been marked as added and is being changed, do nothing
4936 # the value is new, record it:
4940 # a new value is being added
4941 if (defined($recorded)) {
4942 # it was already not in sync
4943 if ($new eq $recorded) {
4944 # a value is being reverted to the cloud-init state:
4946 delete $added{$opt};
4948 # the value had temporarily been removed, do nothing
4950 } elsif ($added{$opt}) {
4951 # the value had been marked as added already, do nothing
4953 # the value is new, add it
4957 } elsif (!defined($old)) {
4958 # a non-existent value is being removed? ignore...
4960 # a value is being deleted
4961 if (defined($recorded)) {
4962 # a value was already recorded, just keep it
4963 } elsif ($added{$opt}) {
4964 # the value was marked as added, remove it
4965 delete $added{$opt};
4967 # a previously unrecorded value is being removed, record the old value:
4972 my $added = join(',', sort keys %added);
4973 $ci->{added} = $added if length($added);
4977 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4978 if ($fast_plug_option->{$opt}) {
4979 my $new = delete $conf->{pending}->{$opt};
4980 $cloudinit_record_changed->($conf, $opt, $conf->{$opt}, $new);
4981 $conf->{$opt} = $new;
4987 PVE::QemuConfig->write_config($vmid, $conf);
4990 my $ostype = $conf->{ostype};
4991 my $version = extract_version($machine_type, get_running_qemu_version($vmid));
4992 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
4993 my $usb_hotplug = $hotplug_features->{usb}
4994 && min_version($version, 7, 1)
4995 && defined($ostype) && ($ostype eq 'l26
' || windows_version($ostype) > 7);
4997 my $cgroup = PVE::QemuServer::CGroup->new($vmid);
4998 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
5000 foreach my $opt (sort keys %$pending_delete_hash) {
5001 next if $selection && !$selection->{$opt};
5002 my $force = $pending_delete_hash->{$opt}->{force};
5004 if ($opt eq 'hotplug
') {
5005 die "skip\n" if ($conf->{hotplug} =~ /(cpu|memory)/);
5006 } elsif ($opt eq 'tablet
') {
5007 die "skip\n" if !$hotplug_features->{usb};
5008 if ($defaults->{tablet}) {
5009 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
5010 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
5011 if $arch eq 'aarch64
';
5013 vm_deviceunplug($vmid, $conf, 'tablet
');
5014 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
5016 } elsif ($opt =~ m/^usb(\d+)$/) {
5018 die "skip\n" if !$usb_hotplug;
5019 vm_deviceunplug($vmid, $conf, "usbredirdev$index"); # if it's a spice port
5020 vm_deviceunplug
($vmid, $conf, $opt);
5021 } elsif ($opt eq 'vcpus') {
5022 die "skip\n" if !$hotplug_features->{cpu
};
5023 qemu_cpu_hotplug
($vmid, $conf, undef);
5024 } elsif ($opt eq 'balloon') {
5025 # enable balloon device is not hotpluggable
5026 die "skip\n" if defined($conf->{balloon
}) && $conf->{balloon
} == 0;
5027 # here we reset the ballooning value to memory
5028 my $balloon = get_current_memory
($conf->{memory
});
5029 mon_cmd
($vmid, "balloon", value
=> $balloon*1024*1024);
5030 } elsif ($fast_plug_option->{$opt}) {
5032 } elsif ($opt =~ m/^net(\d+)$/) {
5033 die "skip\n" if !$hotplug_features->{network
};
5034 vm_deviceunplug
($vmid, $conf, $opt);
5036 my $net = PVE
::QemuServer
::parse_net
($conf->{$opt});
5037 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($net->{bridge
}, $net->{macaddr
}, $conf->{name
});
5039 } elsif (is_valid_drivename
($opt)) {
5040 die "skip\n" if !$hotplug_features->{disk
} || $opt =~ m/(ide|sata)(\d+)/;
5041 vm_deviceunplug
($vmid, $conf, $opt);
5042 vmconfig_delete_or_detach_drive
($vmid, $storecfg, $conf, $opt, $force);
5043 } elsif ($opt =~ m/^memory$/) {
5044 die "skip\n" if !$hotplug_features->{memory
};
5045 PVE
::QemuServer
::Memory
::qemu_memory_hotplug
($vmid, $conf);
5046 } elsif ($opt eq 'cpuunits') {
5047 $cgroup->change_cpu_shares(undef);
5048 } elsif ($opt eq 'cpulimit') {
5049 $cgroup->change_cpu_quota(undef, undef); # reset, cgroup module can better decide values
5055 &$add_error($opt, $err) if $err ne "skip\n";
5057 my $old = delete $conf->{$opt};
5058 $cloudinit_record_changed->($conf, $opt, $old, undef);
5059 PVE
::QemuConfig-
>remove_from_pending_delete($conf, $opt);
5064 foreach my $opt (keys %{$conf->{pending
}}) {
5065 next if $selection && !$selection->{$opt};
5066 my $value = $conf->{pending
}->{$opt};
5068 if ($opt eq 'hotplug') {
5069 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug
} =~ /memory/);
5070 die "skip\n" if ($value =~ /cpu/) || ($value !~ /cpu/ && $conf->{hotplug
} =~ /cpu/);
5071 } elsif ($opt eq 'tablet') {
5072 die "skip\n" if !$hotplug_features->{usb
};
5074 vm_deviceplug
($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
5075 vm_deviceplug
($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
5076 if $arch eq 'aarch64';
5077 } elsif ($value == 0) {
5078 vm_deviceunplug
($vmid, $conf, 'tablet');
5079 vm_deviceunplug
($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
5081 } elsif ($opt =~ m/^usb(\d+)$/) {
5083 die "skip\n" if !$usb_hotplug;
5084 my $d = eval { parse_property_string
('pve-qm-usb', $value) };
5086 if ($d->{host
} =~ m/^spice$/i) {
5087 $id = "usbredirdev$index";
5089 qemu_usb_hotplug
($storecfg, $conf, $vmid, $id, $d, $arch, $machine_type);
5090 } elsif ($opt eq 'vcpus') {
5091 die "skip\n" if !$hotplug_features->{cpu
};
5092 qemu_cpu_hotplug
($vmid, $conf, $value);
5093 } elsif ($opt eq 'balloon') {
5094 # enable/disable balloning device is not hotpluggable
5095 my $old_balloon_enabled = !!(!defined($conf->{balloon
}) || $conf->{balloon
});
5096 my $new_balloon_enabled = !!(!defined($conf->{pending
}->{balloon
}) || $conf->{pending
}->{balloon
});
5097 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
5099 # allow manual ballooning if shares is set to zero
5100 if ((defined($conf->{shares
}) && ($conf->{shares
} == 0))) {
5101 my $memory = get_current_memory
($conf->{memory
});
5102 my $balloon = $conf->{pending
}->{balloon
} || $memory;
5103 mon_cmd
($vmid, "balloon", value
=> $balloon*1024*1024);
5105 } elsif ($opt =~ m/^net(\d+)$/) {
5106 # some changes can be done without hotplug
5107 vmconfig_update_net
($storecfg, $conf, $hotplug_features->{network
},
5108 $vmid, $opt, $value, $arch, $machine_type);
5109 } elsif (is_valid_drivename
($opt)) {
5110 die "skip\n" if $opt eq 'efidisk0' || $opt eq 'tpmstate0';
5111 # some changes can be done without hotplug
5112 my $drive = parse_drive
($opt, $value);
5113 if (drive_is_cloudinit
($drive)) {
5114 $cloudinit_opt = [$opt, $drive];
5115 # apply all the other changes first, then generate the cloudinit disk
5118 vmconfig_update_disk
($storecfg, $conf, $hotplug_features->{disk
},
5119 $vmid, $opt, $value, $arch, $machine_type);
5120 } elsif ($opt =~ m/^memory$/) { #dimms
5121 die "skip\n" if !$hotplug_features->{memory
};
5122 $value = PVE
::QemuServer
::Memory
::qemu_memory_hotplug
($vmid, $conf, $value);
5123 } elsif ($opt eq 'cpuunits') {
5124 my $new_cpuunits = PVE
::CGroup
::clamp_cpu_shares
($conf->{pending
}->{$opt}); #clamp
5125 $cgroup->change_cpu_shares($new_cpuunits);
5126 } elsif ($opt eq 'cpulimit') {
5127 my $cpulimit = $conf->{pending
}->{$opt} == 0 ?
-1 : int($conf->{pending
}->{$opt} * 100000);
5128 $cgroup->change_cpu_quota($cpulimit, 100000);
5129 } elsif ($opt eq 'agent') {
5130 vmconfig_update_agent
($conf, $opt, $value);
5132 die "skip\n"; # skip non-hot-pluggable options
5136 &$add_error($opt, $err) if $err ne "skip\n";
5138 $cloudinit_record_changed->($conf, $opt, $conf->{$opt}, $value);
5139 $conf->{$opt} = $value;
5140 delete $conf->{pending
}->{$opt};
5144 if (defined($cloudinit_opt)) {
5145 my ($opt, $drive) = @$cloudinit_opt;
5146 my $value = $conf->{pending
}->{$opt};
5148 my $temp = {%$conf, $opt => $value};
5149 PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($temp, $vmid);
5150 vmconfig_update_disk
($storecfg, $conf, $hotplug_features->{disk
},
5151 $vmid, $opt, $value, $arch, $machine_type);
5154 &$add_error($opt, $err) if $err ne "skip\n";
5156 $conf->{$opt} = $value;
5157 delete $conf->{pending
}->{$opt};
5161 # unplug xhci controller if no usb device is left
5164 for (my $i = 0; $i < $PVE::QemuServer
::USB
::MAX_USB_DEVICES
; $i++) {
5165 next if !defined($conf->{"usb$i"});
5170 vm_deviceunplug
($vmid, $conf, 'xhci');
5174 PVE
::QemuConfig-
>write_config($vmid, $conf);
5176 if ($hotplug_features->{cloudinit
} && PVE
::QemuServer
::Cloudinit
::has_changes
($conf)) {
5177 PVE
::QemuServer
::vmconfig_update_cloudinit_drive
($storecfg, $conf, $vmid);
5181 sub try_deallocate_drive
{
5182 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
5184 if (($force || $key =~ /^unused/) && !drive_is_cdrom
($drive, 1)) {
5185 my $volid = $drive->{file
};
5186 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
5187 my $sid = PVE
::Storage
::parse_volume_id
($volid);
5188 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
5190 # check if the disk is really unused
5191 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
5192 if PVE
::QemuServer
::Drive
::is_volume_in_use
($storecfg, $conf, $key, $volid);
5193 PVE
::Storage
::vdisk_free
($storecfg, $volid);
5196 # If vm is not owner of this disk remove from config
5204 sub vmconfig_delete_or_detach_drive
{
5205 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
5207 my $drive = parse_drive
($opt, $conf->{$opt});
5209 my $rpcenv = PVE
::RPCEnvironment
::get
();
5210 my $authuser = $rpcenv->get_user();
5213 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
5214 try_deallocate_drive
($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
5216 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $drive);
5222 sub vmconfig_apply_pending
{
5223 my ($vmid, $conf, $storecfg, $errors, $skip_cloud_init) = @_;
5225 return if !scalar(keys %{$conf->{pending
}});
5227 my $add_apply_error = sub {
5228 my ($opt, $msg) = @_;
5229 my $err_msg = "unable to apply pending change $opt : $msg";
5230 $errors->{$opt} = $err_msg;
5236 my $pending_delete_hash = PVE
::QemuConfig-
>parse_pending_delete($conf->{pending
}->{delete});
5237 foreach my $opt (sort keys %$pending_delete_hash) {
5238 my $force = $pending_delete_hash->{$opt}->{force
};
5240 if ($opt =~ m/^unused/) {
5241 die "internal error";
5242 } elsif (defined($conf->{$opt}) && is_valid_drivename
($opt)) {
5243 vmconfig_delete_or_detach_drive
($vmid, $storecfg, $conf, $opt, $force);
5244 } elsif (defined($conf->{$opt}) && $opt =~ m/^net\d+$/) {
5246 my $net = PVE
::QemuServer
::parse_net
($conf->{$opt});
5247 eval { PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($net->{bridge
}, $net->{macaddr
}, $conf->{name
}) };
5253 $add_apply_error->($opt, $err);
5255 PVE
::QemuConfig-
>remove_from_pending_delete($conf, $opt);
5256 delete $conf->{$opt};
5260 PVE
::QemuConfig-
>cleanup_pending($conf);
5262 my $generate_cloudinit = $skip_cloud_init ?
0 : undef;
5264 foreach my $opt (keys %{$conf->{pending
}}) { # add/change
5265 next if $opt eq 'delete'; # just to be sure
5267 if (defined($conf->{$opt}) && is_valid_drivename
($opt)) {
5268 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, parse_drive
($opt, $conf->{$opt}))
5269 } elsif (defined($conf->{pending
}->{$opt}) && $opt =~ m/^net\d+$/) {
5271 my $new_net = PVE
::QemuServer
::parse_net
($conf->{pending
}->{$opt});
5273 my $old_net = PVE
::QemuServer
::parse_net
($conf->{$opt});
5275 if ($old_net->{bridge
} ne $new_net->{bridge
} ||
5276 $old_net->{macaddr
} ne $new_net->{macaddr
}) {
5277 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($old_net->{bridge
}, $old_net->{macaddr
}, $conf->{name
});
5280 #fixme: reuse ip if mac change && same bridge
5281 PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($new_net->{bridge
}, $conf->{name
}, $new_net->{macaddr
}, $vmid, undef, 1);
5286 $add_apply_error->($opt, $err);
5289 if (is_valid_drivename
($opt)) {
5290 my $drive = parse_drive
($opt, $conf->{pending
}->{$opt});
5291 $generate_cloudinit //= 1 if drive_is_cloudinit
($drive);
5294 $conf->{$opt} = delete $conf->{pending
}->{$opt};
5298 # write all changes at once to avoid unnecessary i/o
5299 PVE
::QemuConfig-
>write_config($vmid, $conf);
5300 if ($generate_cloudinit) {
5301 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5302 # After successful generation and if there were changes to be applied, update the
5303 # config to drop the {cloudinit} entry.
5304 PVE
::QemuConfig-
>write_config($vmid, $conf);
5309 sub vmconfig_update_net
{
5310 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5312 my $newnet = parse_net
($value);
5314 if ($conf->{$opt}) {
5315 my $oldnet = parse_net
($conf->{$opt});
5317 if (safe_string_ne
($oldnet->{model
}, $newnet->{model
}) ||
5318 safe_string_ne
($oldnet->{macaddr
}, $newnet->{macaddr
}) ||
5319 safe_num_ne
($oldnet->{queues
}, $newnet->{queues
}) ||
5320 safe_num_ne
($oldnet->{mtu
}, $newnet->{mtu
}) ||
5321 !($newnet->{bridge
} && $oldnet->{bridge
})) { # bridge/nat mode change
5323 # for non online change, we try to hot-unplug
5324 die "skip\n" if !$hotplug;
5325 vm_deviceunplug
($vmid, $conf, $opt);
5328 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($oldnet->{bridge
}, $oldnet->{macaddr
}, $conf->{name
});
5333 die "internal error" if $opt !~ m/net(\d+)/;
5334 my $iface = "tap${vmid}i$1";
5336 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
}) ||
5337 safe_num_ne
($oldnet->{tag
}, $newnet->{tag
}) ||
5338 safe_string_ne
($oldnet->{trunks
}, $newnet->{trunks
}) ||
5339 safe_num_ne
($oldnet->{firewall
}, $newnet->{firewall
})) {
5340 PVE
::Network
::tap_unplug
($iface);
5342 #set link_down in guest if bridge or vlan change to notify guest (dhcp renew for example)
5343 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
}) ||
5344 safe_num_ne
($oldnet->{tag
}, $newnet->{tag
})) {
5345 qemu_set_link_status
($vmid, $opt, 0);
5348 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
})) {
5350 PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($oldnet->{bridge
}, $oldnet->{macaddr
}, $conf->{name
});
5351 PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($newnet->{bridge
}, $conf->{name
}, $newnet->{macaddr
}, $vmid, undef, 1);
5356 PVE
::Network
::SDN
::Zones
::tap_plug
($iface, $newnet->{bridge
}, $newnet->{tag
}, $newnet->{firewall
}, $newnet->{trunks
}, $newnet->{rate
});
5358 PVE
::Network
::tap_plug
($iface, $newnet->{bridge
}, $newnet->{tag
}, $newnet->{firewall
}, $newnet->{trunks
}, $newnet->{rate
});
5361 #set link_up in guest if bridge or vlan change to notify guest (dhcp renew for example)
5362 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
}) ||
5363 safe_num_ne
($oldnet->{tag
}, $newnet->{tag
})) {
5364 qemu_set_link_status
($vmid, $opt, 1);
5367 } elsif (safe_num_ne
($oldnet->{rate
}, $newnet->{rate
})) {
5368 # Rate can be applied on its own but any change above needs to
5369 # include the rate in tap_plug since OVS resets everything.
5370 PVE
::Network
::tap_rate_limit
($iface, $newnet->{rate
});
5373 if (safe_string_ne
($oldnet->{link_down
}, $newnet->{link_down
})) {
5374 qemu_set_link_status
($vmid, $opt, !$newnet->{link_down
});
5383 PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($newnet->{bridge
}, $conf->{name
}, $newnet->{macaddr
}, $vmid, undef, 1);
5384 PVE
::Network
::SDN
::Vnets
::add_dhcp_mapping
($newnet->{bridge
}, $newnet->{macaddr
}, $vmid, $conf->{name
});
5386 vm_deviceplug
($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
5392 sub vmconfig_update_agent
{
5393 my ($conf, $opt, $value) = @_;
5395 die "skip\n" if !$conf->{$opt};
5397 my $hotplug_options = { fstrim_cloned_disks
=> 1 };
5399 my $old_agent = parse_guest_agent
($conf);
5400 my $agent = parse_guest_agent
({$opt => $value});
5402 for my $option (keys %$agent) { # added/changed options
5403 next if defined($hotplug_options->{$option});
5404 die "skip\n" if safe_string_ne
($agent->{$option}, $old_agent->{$option});
5407 for my $option (keys %$old_agent) { # removed options
5408 next if defined($hotplug_options->{$option});
5409 die "skip\n" if safe_string_ne
($old_agent->{$option}, $agent->{$option});
5412 return; # either no actual change (e.g., format string reordered) or just hotpluggable changes
5415 sub vmconfig_update_disk
{
5416 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5418 my $drive = parse_drive
($opt, $value);
5420 if ($conf->{$opt} && (my $old_drive = parse_drive
($opt, $conf->{$opt}))) {
5421 my $media = $drive->{media
} || 'disk';
5422 my $oldmedia = $old_drive->{media
} || 'disk';
5423 die "unable to change media type\n" if $media ne $oldmedia;
5425 if (!drive_is_cdrom
($old_drive)) {
5427 if ($drive->{file
} ne $old_drive->{file
}) {
5429 die "skip\n" if !$hotplug;
5431 # unplug and register as unused
5432 vm_deviceunplug
($vmid, $conf, $opt);
5433 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $old_drive)
5436 # update existing disk
5438 # skip non hotpluggable value
5439 if (safe_string_ne
($drive->{aio
}, $old_drive->{aio
}) ||
5440 safe_string_ne
($drive->{discard
}, $old_drive->{discard
}) ||
5441 safe_string_ne
($drive->{iothread
}, $old_drive->{iothread
}) ||
5442 safe_string_ne
($drive->{queues
}, $old_drive->{queues
}) ||
5443 safe_string_ne
($drive->{cache
}, $old_drive->{cache
}) ||
5444 safe_string_ne
($drive->{ssd
}, $old_drive->{ssd
}) ||
5445 safe_string_ne
($drive->{ro
}, $old_drive->{ro
})) {
5450 if (safe_num_ne
($drive->{mbps
}, $old_drive->{mbps
}) ||
5451 safe_num_ne
($drive->{mbps_rd
}, $old_drive->{mbps_rd
}) ||
5452 safe_num_ne
($drive->{mbps_wr
}, $old_drive->{mbps_wr
}) ||
5453 safe_num_ne
($drive->{iops
}, $old_drive->{iops
}) ||
5454 safe_num_ne
($drive->{iops_rd
}, $old_drive->{iops_rd
}) ||
5455 safe_num_ne
($drive->{iops_wr
}, $old_drive->{iops_wr
}) ||
5456 safe_num_ne
($drive->{mbps_max
}, $old_drive->{mbps_max
}) ||
5457 safe_num_ne
($drive->{mbps_rd_max
}, $old_drive->{mbps_rd_max
}) ||
5458 safe_num_ne
($drive->{mbps_wr_max
}, $old_drive->{mbps_wr_max
}) ||
5459 safe_num_ne
($drive->{iops_max
}, $old_drive->{iops_max
}) ||
5460 safe_num_ne
($drive->{iops_rd_max
}, $old_drive->{iops_rd_max
}) ||
5461 safe_num_ne
($drive->{iops_wr_max
}, $old_drive->{iops_wr_max
}) ||
5462 safe_num_ne
($drive->{bps_max_length
}, $old_drive->{bps_max_length
}) ||
5463 safe_num_ne
($drive->{bps_rd_max_length
}, $old_drive->{bps_rd_max_length
}) ||
5464 safe_num_ne
($drive->{bps_wr_max_length
}, $old_drive->{bps_wr_max_length
}) ||
5465 safe_num_ne
($drive->{iops_max_length
}, $old_drive->{iops_max_length
}) ||
5466 safe_num_ne
($drive->{iops_rd_max_length
}, $old_drive->{iops_rd_max_length
}) ||
5467 safe_num_ne
($drive->{iops_wr_max_length
}, $old_drive->{iops_wr_max_length
})) {
5469 qemu_block_set_io_throttle
(
5471 ($drive->{mbps
} || 0)*1024*1024,
5472 ($drive->{mbps_rd
} || 0)*1024*1024,
5473 ($drive->{mbps_wr
} || 0)*1024*1024,
5474 $drive->{iops
} || 0,
5475 $drive->{iops_rd
} || 0,
5476 $drive->{iops_wr
} || 0,
5477 ($drive->{mbps_max
} || 0)*1024*1024,
5478 ($drive->{mbps_rd_max
} || 0)*1024*1024,
5479 ($drive->{mbps_wr_max
} || 0)*1024*1024,
5480 $drive->{iops_max
} || 0,
5481 $drive->{iops_rd_max
} || 0,
5482 $drive->{iops_wr_max
} || 0,
5483 $drive->{bps_max_length
} || 1,
5484 $drive->{bps_rd_max_length
} || 1,
5485 $drive->{bps_wr_max_length
} || 1,
5486 $drive->{iops_max_length
} || 1,
5487 $drive->{iops_rd_max_length
} || 1,
5488 $drive->{iops_wr_max_length
} || 1,
5498 if ($drive->{file
} eq 'none') {
5499 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$opt");
5500 if (drive_is_cloudinit
($old_drive)) {
5501 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $old_drive);
5504 my $path = get_iso_path
($storecfg, $vmid, $drive->{file
});
5506 # force eject if locked
5507 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$opt");
5510 mon_cmd
($vmid, "blockdev-change-medium",
5511 id
=> "$opt", filename
=> "$path");
5519 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
5521 PVE
::Storage
::activate_volumes
($storecfg, [$drive->{file
}]) if $drive->{file
} !~ m
|^/dev/.+|;
5522 vm_deviceplug
($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
5525 sub vmconfig_update_cloudinit_drive
{
5526 my ($storecfg, $conf, $vmid) = @_;
5528 my $cloudinit_ds = undef;
5529 my $cloudinit_drive = undef;
5531 PVE
::QemuConfig-
>foreach_volume($conf, sub {
5532 my ($ds, $drive) = @_;
5533 if (PVE
::QemuServer
::drive_is_cloudinit
($drive)) {
5534 $cloudinit_ds = $ds;
5535 $cloudinit_drive = $drive;
5539 return if !$cloudinit_drive;
5541 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5542 PVE
::QemuConfig-
>write_config($vmid, $conf);
5545 my $running = PVE
::QemuServer
::check_running
($vmid);
5548 my $path = PVE
::Storage
::path
($storecfg, $cloudinit_drive->{file
});
5550 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$cloudinit_ds");
5551 mon_cmd
($vmid, "blockdev-change-medium", id
=> "$cloudinit_ds", filename
=> "$path");
5556 # called in locked context by incoming migration
5557 sub vm_migrate_get_nbd_disks
{
5558 my ($storecfg, $conf, $replicated_volumes) = @_;
5560 my $local_volumes = {};
5561 PVE
::QemuConfig-
>foreach_volume($conf, sub {
5562 my ($ds, $drive) = @_;
5564 return if drive_is_cdrom
($drive);
5565 return if $ds eq 'tpmstate0';
5567 my $volid = $drive->{file
};
5571 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
5573 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
5574 return if $scfg->{shared
};
5576 my $format = qemu_img_format
($scfg, $volname);
5578 # replicated disks re-use existing state via bitmap
5579 my $use_existing = $replicated_volumes->{$volid} ?
1 : 0;
5580 $local_volumes->{$ds} = [$volid, $storeid, $drive, $use_existing, $format];
5582 return $local_volumes;
5585 # called in locked context by incoming migration
5586 sub vm_migrate_alloc_nbd_disks
{
5587 my ($storecfg, $vmid, $source_volumes, $storagemap) = @_;
5590 foreach my $opt (sort keys %$source_volumes) {
5591 my ($volid, $storeid, $drive, $use_existing, $format) = @{$source_volumes->{$opt}};
5593 if ($use_existing) {
5594 $nbd->{$opt}->{drivestr
} = print_drive
($drive);
5595 $nbd->{$opt}->{volid
} = $volid;
5596 $nbd->{$opt}->{replicated
} = 1;
5600 $storeid = PVE
::JSONSchema
::map_id
($storagemap, $storeid);
5602 # order of precedence, filtered by whether storage supports it:
5603 # 1. explicit requested format
5604 # 2. default format of storage
5605 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
5606 $format = $defFormat if !$format || !grep { $format eq $_ } $validFormats->@*;
5608 my $size = $drive->{size
} / 1024;
5609 my $newvolid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $format, undef, $size);
5610 my $newdrive = $drive;
5611 $newdrive->{format
} = $format;
5612 $newdrive->{file
} = $newvolid;
5613 my $drivestr = print_drive
($newdrive);
5614 $nbd->{$opt}->{drivestr
} = $drivestr;
5615 $nbd->{$opt}->{volid
} = $newvolid;
5621 # see vm_start_nolock for parameters, additionally:
5623 # storagemap = parsed storage map for allocating NBD disks
5625 my ($storecfg, $vmid, $params, $migrate_opts) = @_;
5627 return PVE
::QemuConfig-
>lock_config($vmid, sub {
5628 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migrate_opts->{migratedfrom
});
5630 die "you can't start a vm if it's a template\n"
5631 if !$params->{skiptemplate
} && PVE
::QemuConfig-
>is_template($conf);
5633 my $has_suspended_lock = PVE
::QemuConfig-
>has_lock($conf, 'suspended');
5634 my $has_backup_lock = PVE
::QemuConfig-
>has_lock($conf, 'backup');
5636 my $running = check_running
($vmid, undef, $migrate_opts->{migratedfrom
});
5638 if ($has_backup_lock && $running) {
5639 # a backup is currently running, attempt to start the guest in the
5640 # existing QEMU instance
5641 return vm_resume
($vmid);
5644 PVE
::QemuConfig-
>check_lock($conf)
5645 if !($params->{skiplock
} || $has_suspended_lock);
5647 $params->{resume
} = $has_suspended_lock || defined($conf->{vmstate
});
5649 die "VM $vmid already running\n" if $running;
5651 if (my $storagemap = $migrate_opts->{storagemap
}) {
5652 my $replicated = $migrate_opts->{replicated_volumes
};
5653 my $disks = vm_migrate_get_nbd_disks
($storecfg, $conf, $replicated);
5654 $migrate_opts->{nbd
} = vm_migrate_alloc_nbd_disks
($storecfg, $vmid, $disks, $storagemap);
5656 foreach my $opt (keys %{$migrate_opts->{nbd
}}) {
5657 $conf->{$opt} = $migrate_opts->{nbd
}->{$opt}->{drivestr
};
5661 return vm_start_nolock
($storecfg, $vmid, $conf, $params, $migrate_opts);
5667 # statefile => 'tcp', 'unix' for migration or path/volid for RAM state
5668 # skiplock => 0/1, skip checking for config lock
5669 # skiptemplate => 0/1, skip checking whether VM is template
5670 # forcemachine => to force QEMU machine (rollback/migration)
5671 # forcecpu => a QEMU '-cpu' argument string to override get_cpu_options
5672 # timeout => in seconds
5673 # paused => start VM in paused state (backup)
5674 # resume => resume from hibernation
5685 # nbd => volumes for NBD exports (vm_migrate_alloc_nbd_disks)
5686 # migratedfrom => source node
5687 # spice_ticket => used for spice migration, passed via tunnel/stdin
5688 # network => CIDR of migration network
5689 # type => secure/insecure - tunnel over encrypted connection or plain-text
5690 # nbd_proto_version => int, 0 for TCP, 1 for UNIX
5691 # replicated_volumes => which volids should be re-used with bitmaps for nbd migration
5692 # offline_volumes => new volids of offline migrated disks like tpmstate and cloudinit, not yet
5693 # contained in config
5694 sub vm_start_nolock
{
5695 my ($storecfg, $vmid, $conf, $params, $migrate_opts) = @_;
5697 my $statefile = $params->{statefile
};
5698 my $resume = $params->{resume
};
5700 my $migratedfrom = $migrate_opts->{migratedfrom
};
5701 my $migration_type = $migrate_opts->{type
};
5705 # clean up leftover reboot request files
5706 eval { clear_reboot_request
($vmid); };
5709 if (!$statefile && scalar(keys %{$conf->{pending
}})) {
5710 vmconfig_apply_pending
($vmid, $conf, $storecfg);
5711 $conf = PVE
::QemuConfig-
>load_config($vmid); # update/reload
5714 # don't regenerate the ISO if the VM is started as part of a live migration
5715 # this way we can reuse the old ISO with the correct config
5716 if (!$migratedfrom) {
5717 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5718 # FIXME: apply_cloudinit_config updates $conf in this case, and it would only drop
5719 # $conf->{cloudinit}, so we could just not do this?
5720 # But we do it above, so for now let's be consistent.
5721 $conf = PVE
::QemuConfig-
>load_config($vmid); # update/reload
5725 # override offline migrated volumes, conf is out of date still
5726 if (my $offline_volumes = $migrate_opts->{offline_volumes
}) {
5727 for my $key (sort keys $offline_volumes->%*) {
5728 my $parsed = parse_drive
($key, $conf->{$key});
5729 $parsed->{file
} = $offline_volumes->{$key};
5730 $conf->{$key} = print_drive
($parsed);
5734 my $defaults = load_defaults
();
5736 # set environment variable useful inside network script
5737 # for remote migration the config is available on the target node!
5738 if (!$migrate_opts->{remote_node
}) {
5739 $ENV{PVE_MIGRATED_FROM
} = $migratedfrom;
5742 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-start', 1);
5744 my $forcemachine = $params->{forcemachine
};
5745 my $forcecpu = $params->{forcecpu
};
5747 # enforce machine and CPU type on suspended vm to ensure HW compatibility
5748 $forcemachine = $conf->{runningmachine
};
5749 $forcecpu = $conf->{runningcpu
};
5750 print "Resuming suspended VM\n";
5753 my ($cmd, $vollist, $spice_port, $pci_devices) = config_to_command
($storecfg, $vmid,
5754 $conf, $defaults, $forcemachine, $forcecpu, $params->{'pbs-backing'});
5757 my $get_migration_ip = sub {
5758 my ($nodename) = @_;
5760 return $migration_ip if defined($migration_ip);
5762 my $cidr = $migrate_opts->{network
};
5764 if (!defined($cidr)) {
5765 my $dc_conf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5766 $cidr = $dc_conf->{migration
}->{network
};
5769 if (defined($cidr)) {
5770 my $ips = PVE
::Network
::get_local_ip_from_cidr
($cidr);
5772 die "could not get IP: no address configured on local " .
5773 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5775 die "could not get IP: multiple addresses configured on local " .
5776 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5778 $migration_ip = @$ips[0];
5781 $migration_ip = PVE
::Cluster
::remote_node_ip
($nodename, 1)
5782 if !defined($migration_ip);
5784 return $migration_ip;
5788 if ($statefile eq 'tcp') {
5789 my $migrate = $res->{migrate
} = { proto
=> 'tcp' };
5790 $migrate->{addr
} = "localhost";
5791 my $datacenterconf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5792 my $nodename = nodename
();
5794 if (!defined($migration_type)) {
5795 if (defined($datacenterconf->{migration
}->{type
})) {
5796 $migration_type = $datacenterconf->{migration
}->{type
};
5798 $migration_type = 'secure';
5802 if ($migration_type eq 'insecure') {
5803 $migrate->{addr
} = $get_migration_ip->($nodename);
5804 $migrate->{addr
} = "[$migrate->{addr}]" if Net
::IP
::ip_is_ipv6
($migrate->{addr
});
5807 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
5808 $migrate->{port
} = PVE
::Tools
::next_migrate_port
($pfamily);
5809 $migrate->{uri
} = "tcp:$migrate->{addr}:$migrate->{port}";
5810 push @$cmd, '-incoming', $migrate->{uri
};
5813 } elsif ($statefile eq 'unix') {
5814 # should be default for secure migrations as a ssh TCP forward
5815 # tunnel is not deterministic reliable ready and fails regurarly
5816 # to set up in time, so use UNIX socket forwards
5817 my $migrate = $res->{migrate
} = { proto
=> 'unix' };
5818 $migrate->{addr
} = "/run/qemu-server/$vmid.migrate";
5819 unlink $migrate->{addr
};
5821 $migrate->{uri
} = "unix:$migrate->{addr}";
5822 push @$cmd, '-incoming', $migrate->{uri
};
5825 } elsif (-e
$statefile) {
5826 push @$cmd, '-loadstate', $statefile;
5828 my $statepath = PVE
::Storage
::path
($storecfg, $statefile);
5829 push @$vollist, $statefile;
5830 push @$cmd, '-loadstate', $statepath;
5832 } elsif ($params->{paused
}) {
5836 my $memory = get_current_memory
($conf->{memory
});
5837 my $start_timeout = $params->{timeout
} // config_aware_timeout
($conf, $memory, $resume);
5839 my $pci_reserve_list = [];
5840 for my $device (values $pci_devices->%*) {
5841 next if $device->{mdev
}; # we don't reserve for mdev devices
5842 push $pci_reserve_list->@*, map { $_->{id
} } $device->{ids
}->@*;
5845 # reserve all PCI IDs before actually doing anything with them
5846 PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_reserve_list, $vmid, $start_timeout);
5850 for my $id (sort keys %$pci_devices) {
5851 my $d = $pci_devices->{$id};
5852 my ($index) = ($id =~ m/^hostpci(\d+)$/);
5855 for my $dev ($d->{ids
}->@*) {
5856 my $info = eval { PVE
::QemuServer
::PCI
::prepare_pci_device
($vmid, $dev->{id
}, $index, $d->{mdev
}) };
5859 $chosen_mdev = $info;
5860 last if $chosen_mdev; # if successful, we're done
5866 next if !$d->{mdev
};
5867 die "could not create mediated device\n" if !defined($chosen_mdev);
5869 # nvidia grid needs the uuid of the mdev as qemu parameter
5870 if (!defined($uuid) && $chosen_mdev->{vendor
} =~ m/^(0x)?10de$/) {
5871 if (defined($conf->{smbios1
})) {
5872 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
5873 $uuid = $smbios_conf->{uuid
} if defined($smbios_conf->{uuid
});
5875 $uuid = PVE
::QemuServer
::PCI
::generate_mdev_uuid
($vmid, $index) if !defined($uuid);
5878 push @$cmd, '-uuid', $uuid if defined($uuid);
5881 eval { cleanup_pci_devices
($vmid, $conf) };
5886 PVE
::Storage
::activate_volumes
($storecfg, $vollist);
5889 my %silence_std_outs = (outfunc
=> sub {}, errfunc
=> sub {});
5890 eval { run_command
(['/bin/systemctl', 'reset-failed', "$vmid.scope"], %silence_std_outs) };
5891 eval { run_command
(['/bin/systemctl', 'stop', "$vmid.scope"], %silence_std_outs) };
5892 # Issues with the above 'stop' not being fully completed are extremely rare, a very low
5893 # timeout should be more than enough here...
5894 PVE
::Systemd
::wait_for_unit_removed
("$vmid.scope", 20);
5896 my $cpuunits = PVE
::CGroup
::clamp_cpu_shares
($conf->{cpuunits
});
5899 timeout
=> $statefile ?
undef : $start_timeout,
5904 # when migrating, prefix QEMU output so other side can pick up any
5905 # errors that might occur and show the user
5906 if ($migratedfrom) {
5907 $run_params{quiet
} = 1;
5908 $run_params{logfunc
} = sub { print "QEMU: $_[0]\n" };
5911 my %systemd_properties = (
5912 Slice
=> 'qemu.slice',
5913 KillMode
=> 'process',
5915 TimeoutStopUSec
=> ULONG_MAX
, # infinity
5918 if (PVE
::CGroup
::cgroup_mode
() == 2) {
5919 $systemd_properties{CPUWeight
} = $cpuunits;
5921 $systemd_properties{CPUShares
} = $cpuunits;
5924 if (my $cpulimit = $conf->{cpulimit
}) {
5925 $systemd_properties{CPUQuota
} = int($cpulimit * 100);
5927 $systemd_properties{timeout
} = 10 if $statefile; # setting up the scope shoul be quick
5929 my $run_qemu = sub {
5930 PVE
::Tools
::run_fork
sub {
5931 PVE
::Systemd
::enter_systemd_scope
($vmid, "Proxmox VE VM $vmid", %systemd_properties);
5934 if ((my $tpm = $conf->{tpmstate0
}) && !PVE
::QemuConfig-
>is_template($conf)) {
5935 # start the TPM emulator so QEMU can connect on start
5936 $tpmpid = start_swtpm
($storecfg, $vmid, $tpm, $migratedfrom);
5939 my $exitcode = run_command
($cmd, %run_params);
5942 warn "stopping swtpm instance (pid $tpmpid) due to QEMU startup error\n";
5943 kill 'TERM', $tpmpid;
5945 die "QEMU exited with code $exitcode\n";
5950 if ($conf->{hugepages
}) {
5953 my $hotplug_features =
5954 parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
5955 my $hugepages_topology =
5956 PVE
::QemuServer
::Memory
::hugepages_topology
($conf, $hotplug_features->{memory
});
5958 my $hugepages_host_topology = PVE
::QemuServer
::Memory
::hugepages_host_topology
();
5960 PVE
::QemuServer
::Memory
::hugepages_mount
();
5961 PVE
::QemuServer
::Memory
::hugepages_allocate
($hugepages_topology, $hugepages_host_topology);
5963 eval { $run_qemu->() };
5965 PVE
::QemuServer
::Memory
::hugepages_reset
($hugepages_host_topology)
5966 if !$conf->{keephugepages
};
5970 PVE
::QemuServer
::Memory
::hugepages_pre_deallocate
($hugepages_topology)
5971 if !$conf->{keephugepages
};
5973 eval { PVE
::QemuServer
::Memory
::hugepages_update_locked
($code); };
5976 eval { $run_qemu->() };
5980 # deactivate volumes if start fails
5981 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
5983 eval { cleanup_pci_devices
($vmid, $conf) };
5986 die "start failed: $err";
5989 # re-reserve all PCI IDs now that we can know the actual VM PID
5990 my $pid = PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
5991 eval { PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_reserve_list, $vmid, undef, $pid) };
5994 if (defined($res->{migrate
})) {
5995 print "migration listens on $res->{migrate}->{uri}\n";
5996 } elsif ($statefile) {
5997 eval { mon_cmd
($vmid, "cont"); };
6001 #start nbd server for storage migration
6002 if (my $nbd = $migrate_opts->{nbd
}) {
6003 my $nbd_protocol_version = $migrate_opts->{nbd_proto_version
} // 0;
6005 my $migrate_storage_uri;
6006 # nbd_protocol_version > 0 for unix socket support
6007 if ($nbd_protocol_version > 0 && ($migration_type eq 'secure' || $migration_type eq 'websocket')) {
6008 my $socket_path = "/run/qemu-server/$vmid\_nbd.migrate";
6009 mon_cmd
($vmid, "nbd-server-start", addr
=> { type
=> 'unix', data
=> { path
=> $socket_path } } );
6010 $migrate_storage_uri = "nbd:unix:$socket_path";
6011 $res->{migrate
}->{unix_sockets
} = [$socket_path];
6013 my $nodename = nodename
();
6014 my $localip = $get_migration_ip->($nodename);
6015 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
6016 my $storage_migrate_port = PVE
::Tools
::next_migrate_port
($pfamily);
6018 mon_cmd
($vmid, "nbd-server-start", addr
=> {
6021 host
=> "${localip}",
6022 port
=> "${storage_migrate_port}",
6025 $localip = "[$localip]" if Net
::IP
::ip_is_ipv6
($localip);
6026 $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}";
6029 my $block_info = mon_cmd
($vmid, "query-block");
6030 $block_info = { map { $_->{device
} => $_ } $block_info->@* };
6032 foreach my $opt (sort keys %$nbd) {
6033 my $drivestr = $nbd->{$opt}->{drivestr
};
6034 my $volid = $nbd->{$opt}->{volid
};
6036 my $block_node = $block_info->{"drive-$opt"}->{inserted
}->{'node-name'};
6042 'node-name' => $block_node,
6043 writable
=> JSON
::true
,
6045 name
=> "drive-$opt", # NBD export name
6048 my $nbd_uri = "$migrate_storage_uri:exportname=drive-$opt";
6049 print "storage migration listens on $nbd_uri volume:$drivestr\n";
6050 print "re-using replicated volume: $opt - $volid\n"
6051 if $nbd->{$opt}->{replicated
};
6053 $res->{drives
}->{$opt} = $nbd->{$opt};
6054 $res->{drives
}->{$opt}->{nbd_uri
} = $nbd_uri;
6058 if ($migratedfrom) {
6060 set_migration_caps
($vmid);
6065 print "spice listens on port $spice_port\n";
6066 $res->{spice_port
} = $spice_port;
6067 if ($migrate_opts->{spice_ticket
}) {
6068 mon_cmd
($vmid, "set_password", protocol
=> 'spice', password
=>
6069 $migrate_opts->{spice_ticket
});
6070 mon_cmd
($vmid, "expire_password", protocol
=> 'spice', time => "+30");
6075 mon_cmd
($vmid, "balloon", value
=> $conf->{balloon
}*1024*1024)
6076 if !$statefile && $conf->{balloon
};
6078 foreach my $opt (keys %$conf) {
6079 next if $opt !~ m/^net\d+$/;
6080 my $nicconf = parse_net
($conf->{$opt});
6081 qemu_set_link_status
($vmid, $opt, 0) if $nicconf->{link_down
};
6083 add_nets_bridge_fdb
($conf, $vmid);
6086 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
6091 path
=> "machine/peripheral/balloon0",
6092 property
=> "guest-stats-polling-interval",
6096 log_warn
("could not set polling interval for ballooning - $@") if $@;
6100 print "Resumed VM, removing state\n";
6101 if (my $vmstate = $conf->{vmstate
}) {
6102 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
6103 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
6105 delete $conf->@{qw(lock vmstate runningmachine runningcpu)};
6106 PVE
::QemuConfig-
>write_config($vmid, $conf);
6109 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'post-start');
6111 my ($current_machine, $is_deprecated) =
6112 PVE
::QemuServer
::Machine
::get_current_qemu_machine
($vmid);
6113 if ($is_deprecated) {
6115 "current machine version '$current_machine' is deprecated - see the documentation and ".
6116 "change to a newer one",
6123 sub vm_commandline
{
6124 my ($storecfg, $vmid, $snapname) = @_;
6126 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6128 my ($forcemachine, $forcecpu);
6130 my $snapshot = $conf->{snapshots
}->{$snapname};
6131 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
6133 # check for machine or CPU overrides in snapshot
6134 $forcemachine = $snapshot->{runningmachine
};
6135 $forcecpu = $snapshot->{runningcpu
};
6137 $snapshot->{digest
} = $conf->{digest
}; # keep file digest for API
6142 my $defaults = load_defaults
();
6144 my $cmd = config_to_command
($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu);
6146 return PVE
::Tools
::cmd2string
($cmd);
6150 my ($vmid, $skiplock) = @_;
6152 PVE
::QemuConfig-
>lock_config($vmid, sub {
6154 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6156 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
6158 mon_cmd
($vmid, "system_reset");
6162 sub get_vm_volumes
{
6166 foreach_volid
($conf, sub {
6167 my ($volid, $attr) = @_;
6169 return if $volid =~ m
|^/|;
6171 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
6174 push @$vollist, $volid;
6180 sub cleanup_pci_devices
{
6181 my ($vmid, $conf) = @_;
6183 foreach my $key (keys %$conf) {
6184 next if $key !~ m/^hostpci(\d+)$/;
6185 my $hostpciindex = $1;
6186 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $hostpciindex);
6187 my $d = parse_hostpci
($conf->{$key});
6189 # NOTE: avoid PVE::SysFSTools::pci_cleanup_mdev_device as it requires PCI ID and we
6190 # don't want to break ABI just for this two liner
6191 my $dev_sysfs_dir = "/sys/bus/mdev/devices/$uuid";
6193 # some nvidia vgpu driver versions want to clean the mdevs up themselves, and error
6194 # out when we do it first. so wait for 10 seconds and then try it
6195 if ($d->{ids
}->[0]->[0]->{vendor
} =~ m/^(0x)?10de$/) {
6199 PVE
::SysFSTools
::file_write
("$dev_sysfs_dir/remove", "1") if -e
$dev_sysfs_dir;
6202 PVE
::QemuServer
::PCI
::remove_pci_reservation
($vmid);
6205 sub vm_stop_cleanup
{
6206 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
6211 my $vollist = get_vm_volumes
($conf);
6212 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
6214 if (my $tpmdrive = $conf->{tpmstate0
}) {
6215 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
6216 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
6218 PVE
::Storage
::unmap_volume
($storecfg, $tpm->{file
});
6223 foreach my $ext (qw(mon qmp pid vnc qga)) {
6224 unlink "/var/run/qemu-server/${vmid}.$ext";
6227 if ($conf->{ivshmem
}) {
6228 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
6229 # just delete it for now, VMs which have this already open do not
6230 # are affected, but new VMs will get a separated one. If this
6231 # becomes an issue we either add some sort of ref-counting or just
6232 # add a "don't delete on stop" flag to the ivshmem format.
6233 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name
} // $vmid);
6236 cleanup_pci_devices
($vmid, $conf);
6238 vmconfig_apply_pending
($vmid, $conf, $storecfg) if $apply_pending_changes;
6240 warn $@ if $@; # avoid errors - just warn
6243 # call only in locked context
6245 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
6247 my $pid = check_running
($vmid, $nocheck);
6252 $conf = PVE
::QemuConfig-
>load_config($vmid);
6253 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
6254 if (!defined($timeout) && $shutdown && $conf->{startup
}) {
6255 my $opts = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
6256 $timeout = $opts->{down
} if $opts->{down
};
6258 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-stop');
6263 if (defined($conf) && get_qga_key
($conf, 'enabled')) {
6264 mon_cmd
($vmid, "guest-shutdown", timeout
=> $timeout);
6266 mon_cmd
($vmid, "system_powerdown");
6269 mon_cmd
($vmid, "quit");
6275 $timeout = 60 if !defined($timeout);
6278 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
6283 if ($count >= $timeout) {
6285 warn "VM still running - terminating now with SIGTERM\n";
6288 die "VM quit/powerdown failed - got timeout\n";
6291 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6295 if (!check_running
($vmid, $nocheck)) {
6296 warn "Unexpected: VM shutdown command failed, but VM not running anymore..\n";
6300 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
6303 die "VM quit/powerdown failed\n";
6311 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
6316 if ($count >= $timeout) {
6317 warn "VM still running - terminating now with SIGKILL\n";
6322 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6325 # Note: use $nocheck to skip tests if VM configuration file exists.
6326 # We need that when migration VMs to other nodes (files already moved)
6327 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
6329 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
6331 $force = 1 if !defined($force) && !$shutdown;
6334 my $pid = check_running
($vmid, $nocheck, $migratedfrom);
6335 kill 15, $pid if $pid;
6336 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migratedfrom);
6337 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 0);
6341 PVE
::QemuConfig-
>lock_config($vmid, sub {
6342 _do_vm_stop
($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
6347 my ($vmid, $timeout) = @_;
6349 PVE
::QemuConfig-
>lock_config($vmid, sub {
6352 # only reboot if running, as qmeventd starts it again on a stop event
6353 return if !check_running
($vmid);
6355 create_reboot_request
($vmid);
6357 my $storecfg = PVE
::Storage
::config
();
6358 _do_vm_stop
($storecfg, $vmid, undef, undef, $timeout, 1);
6362 # avoid that the next normal shutdown will be confused for a reboot
6363 clear_reboot_request
($vmid);
6369 # note: if using the statestorage parameter, the caller has to check privileges
6371 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
6378 PVE
::QemuConfig-
>lock_config($vmid, sub {
6380 $conf = PVE
::QemuConfig-
>load_config($vmid);
6382 my $is_backing_up = PVE
::QemuConfig-
>has_lock($conf, 'backup');
6383 PVE
::QemuConfig-
>check_lock($conf)
6384 if !($skiplock || $is_backing_up);
6386 die "cannot suspend to disk during backup\n"
6387 if $is_backing_up && $includestate;
6389 if ($includestate) {
6390 $conf->{lock} = 'suspending';
6391 my $date = strftime
("%Y-%m-%d", localtime(time()));
6392 $storecfg = PVE
::Storage
::config
();
6393 if (!$statestorage) {
6394 $statestorage = find_vmstate_storage
($conf, $storecfg);
6395 # check permissions for the storage
6396 my $rpcenv = PVE
::RPCEnvironment
::get
();
6397 if ($rpcenv->{type
} ne 'cli') {
6398 my $authuser = $rpcenv->get_user();
6399 $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
6404 $vmstate = PVE
::QemuConfig-
>__snapshot_save_vmstate(
6405 $vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
6406 $path = PVE
::Storage
::path
($storecfg, $vmstate);
6407 PVE
::QemuConfig-
>write_config($vmid, $conf);
6409 mon_cmd
($vmid, "stop");
6413 if ($includestate) {
6415 PVE
::Storage
::activate_volumes
($storecfg, [$vmstate]);
6418 set_migration_caps
($vmid, 1);
6419 mon_cmd
($vmid, "savevm-start", statefile
=> $path);
6421 my $state = mon_cmd
($vmid, "query-savevm");
6422 if (!$state->{status
}) {
6423 die "savevm not active\n";
6424 } elsif ($state->{status
} eq 'active') {
6427 } elsif ($state->{status
} eq 'completed') {
6428 print "State saved, quitting\n";
6430 } elsif ($state->{status
} eq 'failed' && $state->{error
}) {
6431 die "query-savevm failed with error '$state->{error}'\n"
6433 die "query-savevm returned status '$state->{status}'\n";
6439 PVE
::QemuConfig-
>lock_config($vmid, sub {
6440 $conf = PVE
::QemuConfig-
>load_config($vmid);
6442 # cleanup, but leave suspending lock, to indicate something went wrong
6444 mon_cmd
($vmid, "savevm-end");
6445 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
6446 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
6447 delete $conf->@{qw(vmstate runningmachine runningcpu)};
6448 PVE
::QemuConfig-
>write_config($vmid, $conf);
6454 die "lock changed unexpectedly\n"
6455 if !PVE
::QemuConfig-
>has_lock($conf, 'suspending');
6457 mon_cmd
($vmid, "quit");
6458 $conf->{lock} = 'suspended';
6459 PVE
::QemuConfig-
>write_config($vmid, $conf);
6464 # $nocheck is set when called as part of a migration - in this context the
6465 # location of the config file (source or target node) is not deterministic,
6466 # since migration cannot wait for pmxcfs to process the rename
6468 my ($vmid, $skiplock, $nocheck) = @_;
6470 PVE
::QemuConfig-
>lock_config($vmid, sub {
6471 my $res = mon_cmd
($vmid, 'query-status');
6472 my $resume_cmd = 'cont';
6476 $conf = eval { PVE
::QemuConfig-
>load_config($vmid) }; # try on target node
6478 my $vmlist = PVE
::Cluster
::get_vmlist
();
6479 if (exists($vmlist->{ids
}->{$vmid})) {
6480 my $node = $vmlist->{ids
}->{$vmid}->{node
};
6481 $conf = eval { PVE
::QemuConfig-
>load_config($vmid, $node) }; # try on source node
6484 PVE
::Cluster
::cfs_update
(); # vmlist was wrong, invalidate cache
6485 $conf = PVE
::QemuConfig-
>load_config($vmid); # last try on target node again
6489 $conf = PVE
::QemuConfig-
>load_config($vmid);
6492 if ($res->{status
}) {
6493 return if $res->{status
} eq 'running'; # job done, go home
6494 $resume_cmd = 'system_wakeup' if $res->{status
} eq 'suspended';
6495 $reset = 1 if $res->{status
} eq 'shutdown';
6499 PVE
::QemuConfig-
>check_lock($conf)
6500 if !($skiplock || PVE
::QemuConfig-
>has_lock($conf, 'backup'));
6504 # required if a VM shuts down during a backup and we get a resume
6505 # request before the backup finishes for example
6506 mon_cmd
($vmid, "system_reset");
6509 add_nets_bridge_fdb
($conf, $vmid) if $resume_cmd eq 'cont';
6511 mon_cmd
($vmid, $resume_cmd);
6516 my ($vmid, $skiplock, $key) = @_;
6518 PVE
::QemuConfig-
>lock_config($vmid, sub {
6520 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6522 # there is no qmp command, so we use the human monitor command
6523 my $res = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "sendkey $key");
6524 die $res if $res ne '';
6528 sub check_bridge_access
{
6529 my ($rpcenv, $authuser, $conf) = @_;
6531 return 1 if $authuser eq 'root@pam';
6533 for my $opt (sort keys $conf->%*) {
6534 next if $opt !~ m/^net\d+$/;
6535 my $net = parse_net
($conf->{$opt});
6536 my ($bridge, $tag, $trunks) = $net->@{'bridge', 'tag', 'trunks'};
6537 PVE
::GuestHelpers
::check_vnet_access
($rpcenv, $authuser, $bridge, $tag, $trunks);
6542 sub check_mapping_access
{
6543 my ($rpcenv, $user, $conf) = @_;
6545 for my $opt (keys $conf->%*) {
6546 if ($opt =~ m/^usb\d+$/) {
6547 my $device = PVE
::JSONSchema
::parse_property_string
('pve-qm-usb', $conf->{$opt});
6548 if (my $host = $device->{host
}) {
6549 die "only root can set '$opt' config for real devices\n"
6550 if $host !~ m/^spice$/i && $user ne 'root@pam';
6551 } elsif ($device->{mapping
}) {
6552 $rpcenv->check_full($user, "/mapping/usb/$device->{mapping}", ['Mapping.Use']);
6554 die "either 'host' or 'mapping' must be set.\n";
6556 } elsif ($opt =~ m/^hostpci\d+$/) {
6557 my $device = PVE
::JSONSchema
::parse_property_string
('pve-qm-hostpci', $conf->{$opt});
6558 if ($device->{host
}) {
6559 die "only root can set '$opt' config for non-mapped devices\n" if $user ne 'root@pam';
6560 } elsif ($device->{mapping
}) {
6561 $rpcenv->check_full($user, "/mapping/pci/$device->{mapping}", ['Mapping.Use']);
6563 die "either 'host' or 'mapping' must be set.\n";
6569 sub check_restore_permissions
{
6570 my ($rpcenv, $user, $conf) = @_;
6572 check_bridge_access
($rpcenv, $user, $conf);
6573 check_mapping_access
($rpcenv, $user, $conf);
6575 # vzdump restore implementaion
6577 sub tar_archive_read_firstfile
{
6578 my $archive = shift;
6580 die "ERROR: file '$archive' does not exist\n" if ! -f
$archive;
6582 # try to detect archive type first
6583 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
6584 die "unable to open file '$archive'\n";
6585 my $firstfile = <$fh>;
6589 die "ERROR: archive contaions no data\n" if !$firstfile;
6595 sub tar_restore_cleanup
{
6596 my ($storecfg, $statfile) = @_;
6598 print STDERR
"starting cleanup\n";
6600 if (my $fd = IO
::File-
>new($statfile, "r")) {
6601 while (defined(my $line = <$fd>)) {
6602 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6605 if ($volid =~ m
|^/|) {
6606 unlink $volid || die 'unlink failed\n';
6608 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6610 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6612 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6614 print STDERR
"unable to parse line in statfile - $line";
6621 sub restore_file_archive
{
6622 my ($archive, $vmid, $user, $opts) = @_;
6624 return restore_vma_archive
($archive, $vmid, $user, $opts)
6627 my $info = PVE
::Storage
::archive_info
($archive);
6628 my $format = $opts->{format
} // $info->{format
};
6629 my $comp = $info->{compression
};
6631 # try to detect archive format
6632 if ($format eq 'tar') {
6633 return restore_tar_archive
($archive, $vmid, $user, $opts);
6635 return restore_vma_archive
($archive, $vmid, $user, $opts, $comp);
6639 # hepler to remove disks that will not be used after restore
6640 my $restore_cleanup_oldconf = sub {
6641 my ($storecfg, $vmid, $oldconf, $virtdev_hash) = @_;
6643 my $kept_disks = {};
6645 PVE
::QemuConfig-
>foreach_volume($oldconf, sub {
6646 my ($ds, $drive) = @_;
6648 return if drive_is_cdrom
($drive, 1);
6650 my $volid = $drive->{file
};
6651 return if !$volid || $volid =~ m
|^/|;
6653 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
6654 return if !$path || !$owner || ($owner != $vmid);
6656 # Note: only delete disk we want to restore
6657 # other volumes will become unused
6658 if ($virtdev_hash->{$ds}) {
6659 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
6664 $kept_disks->{$volid} = 1;
6668 # after the restore we have no snapshots anymore
6669 for my $snapname (keys $oldconf->{snapshots
}->%*) {
6670 my $snap = $oldconf->{snapshots
}->{$snapname};
6671 if ($snap->{vmstate
}) {
6672 eval { PVE
::Storage
::vdisk_free
($storecfg, $snap->{vmstate
}); };
6678 for my $volid (keys $kept_disks->%*) {
6679 eval { PVE
::Storage
::volume_snapshot_delete
($storecfg, $volid, $snapname); };
6685 # Helper to parse vzdump backup device hints
6687 # $rpcenv: Environment, used to ckeck storage permissions
6688 # $user: User ID, to check storage permissions
6689 # $storecfg: Storage configuration
6690 # $fh: the file handle for reading the configuration
6691 # $devinfo: should contain device sizes for all backu-up'ed devices
6692 # $options: backup options (pool, default storage)
6694 # Return: $virtdev_hash, updates $devinfo (add devname, virtdev, format, storeid)
6695 my $parse_backup_hints = sub {
6696 my ($rpcenv, $user, $storecfg, $fh, $devinfo, $options) = @_;
6698 my $check_storage = sub { # assert if an image can be allocate
6699 my ($storeid, $scfg) = @_;
6700 die "Content type 'images' is not available on storage '$storeid'\n"
6701 if !$scfg->{content
}->{images
};
6702 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace'])
6703 if $user ne 'root@pam';
6706 my $virtdev_hash = {};
6707 while (defined(my $line = <$fh>)) {
6708 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
6709 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
6710 die "archive does not contain data for drive '$virtdev'\n"
6711 if !$devinfo->{$devname};
6713 if (defined($options->{storage
})) {
6714 $storeid = $options->{storage
} || 'local';
6715 } elsif (!$storeid) {
6718 $format = 'raw' if !$format;
6719 $devinfo->{$devname}->{devname
} = $devname;
6720 $devinfo->{$devname}->{virtdev
} = $virtdev;
6721 $devinfo->{$devname}->{format
} = $format;
6722 $devinfo->{$devname}->{storeid
} = $storeid;
6724 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6725 $check_storage->($storeid, $scfg); # permission and content type check
6727 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
6728 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
6730 my $drive = parse_drive
($virtdev, $2);
6732 if (drive_is_cloudinit
($drive)) {
6733 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
6734 $storeid = $options->{storage
} if defined ($options->{storage
});
6735 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6736 my $format = qemu_img_format
($scfg, $volname); # has 'raw' fallback
6738 $check_storage->($storeid, $scfg); # permission and content type check
6740 $virtdev_hash->{$virtdev} = {
6742 storeid
=> $storeid,
6743 size
=> PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
,
6750 return $virtdev_hash;
6753 # Helper to allocate and activate all volumes required for a restore
6755 # $storecfg: Storage configuration
6756 # $virtdev_hash: as returned by parse_backup_hints()
6758 # Returns: { $virtdev => $volid }
6759 my $restore_allocate_devices = sub {
6760 my ($storecfg, $virtdev_hash, $vmid) = @_;
6763 foreach my $virtdev (sort keys %$virtdev_hash) {
6764 my $d = $virtdev_hash->{$virtdev};
6765 my $alloc_size = int(($d->{size
} + 1024 - 1)/1024);
6766 my $storeid = $d->{storeid
};
6767 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6769 # test if requested format is supported
6770 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
6771 my $supported = grep { $_ eq $d->{format
} } @$validFormats;
6772 $d->{format
} = $defFormat if !$supported;
6775 if ($d->{is_cloudinit
}) {
6776 $name = "vm-$vmid-cloudinit";
6777 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6778 if ($scfg->{path
}) {
6779 $name .= ".$d->{format}";
6783 my $volid = PVE
::Storage
::vdisk_alloc
(
6784 $storecfg, $storeid, $vmid, $d->{format
}, $name, $alloc_size);
6786 print STDERR
"new volume ID is '$volid'\n";
6787 $d->{volid
} = $volid;
6789 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
6791 $map->{$virtdev} = $volid;
6797 sub restore_update_config_line
{
6798 my ($cookie, $map, $line, $unique) = @_;
6800 return '' if $line =~ m/^\#qmdump\#/;
6801 return '' if $line =~ m/^\#vzdump\#/;
6802 return '' if $line =~ m/^lock:/;
6803 return '' if $line =~ m/^unused\d+:/;
6804 return '' if $line =~ m/^parent:/;
6808 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
6809 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
6810 # try to convert old 1.X settings
6811 my ($id, $ind, $ethcfg) = ($1, $2, $3);
6812 foreach my $devconfig (PVE
::Tools
::split_list
($ethcfg)) {
6813 my ($model, $macaddr) = split(/\=/, $devconfig);
6814 $macaddr = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if !$macaddr || $unique;
6817 bridge
=> "vmbr$ind",
6818 macaddr
=> $macaddr,
6820 my $netstr = print_net
($net);
6822 $res .= "net$cookie->{netcount}: $netstr\n";
6823 $cookie->{netcount
}++;
6825 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
6826 my ($id, $netstr) = ($1, $2);
6827 my $net = parse_net
($netstr);
6828 $net->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if $net->{macaddr
};
6829 $netstr = print_net
($net);
6830 $res .= "$id: $netstr\n";
6831 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk|tpmstate)\d+):\s*(\S+)\s*$/) {
6834 my $di = parse_drive
($virtdev, $value);
6835 if (defined($di->{backup
}) && !$di->{backup
}) {
6837 } elsif ($map->{$virtdev}) {
6838 delete $di->{format
}; # format can change on restore
6839 $di->{file
} = $map->{$virtdev};
6840 $value = print_drive
($di);
6841 $res .= "$virtdev: $value\n";
6845 } elsif (($line =~ m/^vmgenid: (.*)/)) {
6847 if ($vmgenid ne '0') {
6848 # always generate a new vmgenid if there was a valid one setup
6849 $vmgenid = generate_uuid
();
6851 $res .= "vmgenid: $vmgenid\n";
6852 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
6853 my ($uuid, $uuid_str);
6854 UUID
::generate
($uuid);
6855 UUID
::unparse
($uuid, $uuid_str);
6856 my $smbios1 = parse_smbios1
($2);
6857 $smbios1->{uuid
} = $uuid_str;
6858 $res .= $1.print_smbios1
($smbios1)."\n";
6866 my $restore_deactivate_volumes = sub {
6867 my ($storecfg, $virtdev_hash) = @_;
6870 for my $dev (values $virtdev_hash->%*) {
6871 push $vollist->@*, $dev->{volid
} if $dev->{volid
};
6874 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
6875 print STDERR
$@ if $@;
6878 my $restore_destroy_volumes = sub {
6879 my ($storecfg, $virtdev_hash) = @_;
6881 for my $dev (values $virtdev_hash->%*) {
6882 my $volid = $dev->{volid
} or next;
6884 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6885 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6887 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6891 sub restore_merge_config
{
6892 my ($filename, $backup_conf_raw, $override_conf) = @_;
6894 my $backup_conf = parse_vm_config
($filename, $backup_conf_raw);
6895 for my $key (keys $override_conf->%*) {
6896 $backup_conf->{$key} = $override_conf->{$key};
6899 return $backup_conf;
6903 my ($cfg, $vmid) = @_;
6905 my $info = PVE
::Storage
::vdisk_list
($cfg, undef, $vmid, undef, 'images');
6907 my $volid_hash = {};
6908 foreach my $storeid (keys %$info) {
6909 foreach my $item (@{$info->{$storeid}}) {
6910 next if !($item->{volid
} && $item->{size
});
6911 $item->{path
} = PVE
::Storage
::path
($cfg, $item->{volid
});
6912 $volid_hash->{$item->{volid
}} = $item;
6919 sub update_disk_config
{
6920 my ($vmid, $conf, $volid_hash) = @_;
6923 my $prefix = "VM $vmid";
6925 # used and unused disks
6926 my $referenced = {};
6928 # Note: it is allowed to define multiple storages with same path (alias), so
6929 # we need to check both 'volid' and real 'path' (two different volid can point
6930 # to the same path).
6932 my $referencedpath = {};
6935 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6936 my ($opt, $drive) = @_;
6938 my $volid = $drive->{file
};
6940 my $volume = $volid_hash->{$volid};
6942 # mark volid as "in-use" for next step
6943 $referenced->{$volid} = 1;
6944 if ($volume && (my $path = $volume->{path
})) {
6945 $referencedpath->{$path} = 1;
6948 return if drive_is_cdrom
($drive);
6951 my ($updated, $msg) = PVE
::QemuServer
::Drive
::update_disksize
($drive, $volume->{size
});
6952 if (defined($updated)) {
6954 $conf->{$opt} = print_drive
($updated);
6955 print "$prefix ($opt): $msg\n";
6959 # remove 'unusedX' entry if volume is used
6960 PVE
::QemuConfig-
>foreach_unused_volume($conf, sub {
6961 my ($opt, $drive) = @_;
6963 my $volid = $drive->{file
};
6967 $path = $volid_hash->{$volid}->{path
} if $volid_hash->{$volid};
6968 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6969 print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
6971 delete $conf->{$opt};
6974 $referenced->{$volid} = 1;
6975 $referencedpath->{$path} = 1 if $path;
6978 foreach my $volid (sort keys %$volid_hash) {
6979 next if $volid =~ m/vm-$vmid-state-/;
6980 next if $referenced->{$volid};
6981 my $path = $volid_hash->{$volid}->{path
};
6982 next if !$path; # just to be sure
6983 next if $referencedpath->{$path};
6985 my $key = PVE
::QemuConfig-
>add_unused_volume($conf, $volid);
6986 print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
6987 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6994 my ($vmid, $nolock, $dryrun) = @_;
6996 my $cfg = PVE
::Storage
::config
();
6998 print "rescan volumes...\n";
6999 my $volid_hash = scan_volids
($cfg, $vmid);
7001 my $updatefn = sub {
7004 my $conf = PVE
::QemuConfig-
>load_config($vmid);
7006 PVE
::QemuConfig-
>check_lock($conf);
7009 foreach my $volid (keys %$volid_hash) {
7010 my $info = $volid_hash->{$volid};
7011 $vm_volids->{$volid} = $info if $info->{vmid
} && $info->{vmid
} == $vmid;
7014 my $changes = update_disk_config
($vmid, $conf, $vm_volids);
7016 PVE
::QemuConfig-
>write_config($vmid, $conf) if $changes && !$dryrun;
7019 if (defined($vmid)) {
7023 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
7026 my $vmlist = config_list
();
7027 foreach my $vmid (keys %$vmlist) {
7031 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
7037 sub restore_proxmox_backup_archive
{
7038 my ($archive, $vmid, $user, $options) = @_;
7040 my $storecfg = PVE
::Storage
::config
();
7042 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($archive);
7043 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7045 my $fingerprint = $scfg->{fingerprint
};
7046 my $keyfile = PVE
::Storage
::PBSPlugin
::pbs_encryption_key_file_name
($storecfg, $storeid);
7048 my $repo = PVE
::PBSClient
::get_repository
($scfg);
7049 my $namespace = $scfg->{namespace
};
7051 # This is only used for `pbs-restore` and the QEMU PBS driver (live-restore)
7052 my $password = PVE
::Storage
::PBSPlugin
::pbs_get_password
($scfg, $storeid);
7053 local $ENV{PBS_PASSWORD
} = $password;
7054 local $ENV{PBS_FINGERPRINT
} = $fingerprint if defined($fingerprint);
7056 my ($vtype, $pbs_backup_name, undef, undef, undef, undef, $format) =
7057 PVE
::Storage
::parse_volname
($storecfg, $archive);
7059 die "got unexpected vtype '$vtype'\n" if $vtype ne 'backup';
7061 die "got unexpected backup format '$format'\n" if $format ne 'pbs-vm';
7063 my $tmpdir = "/var/tmp/vzdumptmp$$";
7067 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7068 # disable interrupts (always do cleanups)
7072 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
7074 # Note: $oldconf is undef if VM does not exists
7075 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
7076 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
7077 my $new_conf_raw = '';
7079 my $rpcenv = PVE
::RPCEnvironment
::get
();
7080 my $devinfo = {}; # info about drives included in backup
7081 my $virtdev_hash = {}; # info about allocated drives
7089 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7091 my $cfgfn = "$tmpdir/qemu-server.conf";
7092 my $firewall_config_fn = "$tmpdir/fw.conf";
7093 my $index_fn = "$tmpdir/index.json";
7095 my $cmd = "restore";
7097 my $param = [$pbs_backup_name, "index.json", $index_fn];
7098 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7099 my $index = PVE
::Tools
::file_get_contents
($index_fn);
7100 $index = decode_json
($index);
7102 foreach my $info (@{$index->{files
}}) {
7103 if ($info->{filename
} =~ m/^(drive-\S+).img.fidx$/) {
7105 if ($info->{size
} =~ m/^(\d+)$/) { # untaint size
7106 $devinfo->{$devname}->{size
} = $1;
7108 die "unable to parse file size in 'index.json' - got '$info->{size}'\n";
7113 my $is_qemu_server_backup = scalar(
7114 grep { $_->{filename
} eq 'qemu-server.conf.blob' } @{$index->{files
}}
7116 if (!$is_qemu_server_backup) {
7117 die "backup does not look like a qemu-server backup (missing 'qemu-server.conf' file)\n";
7119 my $has_firewall_config = scalar(grep { $_->{filename
} eq 'fw.conf.blob' } @{$index->{files
}});
7121 $param = [$pbs_backup_name, "qemu-server.conf", $cfgfn];
7122 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7124 if ($has_firewall_config) {
7125 $param = [$pbs_backup_name, "fw.conf", $firewall_config_fn];
7126 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7128 my $pve_firewall_dir = '/etc/pve/firewall';
7129 mkdir $pve_firewall_dir; # make sure the dir exists
7130 PVE
::Tools
::file_copy
($firewall_config_fn, "${pve_firewall_dir}/$vmid.fw");
7133 my $fh = IO
::File-
>new($cfgfn, "r") ||
7134 die "unable to read qemu-server.conf - $!\n";
7136 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $storecfg, $fh, $devinfo, $options);
7138 # fixme: rate limit?
7140 # create empty/temp config
7141 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\nlock: create");
7143 $restore_cleanup_oldconf->($storecfg, $vmid, $oldconf, $virtdev_hash) if $oldconf;
7146 my $map = $restore_allocate_devices->($storecfg, $virtdev_hash, $vmid);
7148 foreach my $virtdev (sort keys %$virtdev_hash) {
7149 my $d = $virtdev_hash->{$virtdev};
7150 next if $d->{is_cloudinit
}; # no need to restore cloudinit
7152 # this fails if storage is unavailable
7153 my $volid = $d->{volid
};
7154 my $path = PVE
::Storage
::path
($storecfg, $volid);
7156 # for live-restore we only want to preload the efidisk and TPM state
7157 next if $options->{live
} && $virtdev ne 'efidisk0' && $virtdev ne 'tpmstate0';
7160 if (defined(my $ns = $scfg->{namespace
})) {
7161 @ns_arg = ('--ns', $ns);
7164 my $pbs_restore_cmd = [
7165 '/usr/bin/pbs-restore',
7166 '--repository', $repo,
7169 "$d->{devname}.img.fidx",
7174 push @$pbs_restore_cmd, '--format', $d->{format
} if $d->{format
};
7175 push @$pbs_restore_cmd, '--keyfile', $keyfile if -e
$keyfile;
7177 if (PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $volid)) {
7178 push @$pbs_restore_cmd, '--skip-zero';
7181 my $dbg_cmdstring = PVE
::Tools
::cmd2string
($pbs_restore_cmd);
7182 print "restore proxmox backup image: $dbg_cmdstring\n";
7183 run_command
($pbs_restore_cmd);
7186 $fh->seek(0, 0) || die "seek failed - $!\n";
7188 my $cookie = { netcount
=> 0 };
7189 while (defined(my $line = <$fh>)) {
7190 $new_conf_raw .= restore_update_config_line
(
7202 if ($err || !$options->{live
}) {
7203 $restore_deactivate_volumes->($storecfg, $virtdev_hash);
7209 $restore_destroy_volumes->($storecfg, $virtdev_hash);
7213 if ($options->{live
}) {
7214 # keep lock during live-restore
7215 $new_conf_raw .= "\nlock: create";
7218 my $new_conf = restore_merge_config
($conffile, $new_conf_raw, $options->{override_conf
});
7219 check_restore_permissions
($rpcenv, $user, $new_conf);
7220 PVE
::QemuConfig-
>write_config($vmid, $new_conf);
7222 eval { rescan
($vmid, 1); };
7225 PVE
::AccessControl
::add_vm_to_pool
($vmid, $options->{pool
}) if $options->{pool
};
7227 if ($options->{live
}) {
7233 local $SIG{PIPE
} = sub { die "got signal ($!) - abort\n"; };
7235 my $conf = PVE
::QemuConfig-
>load_config($vmid);
7236 die "cannot do live-restore for template\n" if PVE
::QemuConfig-
>is_template($conf);
7238 # these special drives are already restored before start
7239 delete $devinfo->{'drive-efidisk0'};
7240 delete $devinfo->{'drive-tpmstate0-backup'};
7244 keyfile
=> $keyfile,
7245 snapshot
=> $pbs_backup_name,
7246 namespace
=> $namespace,
7248 pbs_live_restore
($vmid, $conf, $storecfg, $devinfo, $pbs_opts);
7250 PVE
::QemuConfig-
>remove_lock($vmid, "create");
7254 sub pbs_live_restore
{
7255 my ($vmid, $conf, $storecfg, $restored_disks, $opts) = @_;
7257 print "starting VM for live-restore\n";
7258 print "repository: '$opts->{repo}', snapshot: '$opts->{snapshot}'\n";
7260 my $pbs_backing = {};
7261 for my $ds (keys %$restored_disks) {
7262 $ds =~ m/^drive-(.*)$/;
7264 $pbs_backing->{$confname} = {
7265 repository
=> $opts->{repo
},
7266 snapshot
=> $opts->{snapshot
},
7267 archive
=> "$ds.img.fidx",
7269 $pbs_backing->{$confname}->{keyfile
} = $opts->{keyfile
} if -e
$opts->{keyfile
};
7270 $pbs_backing->{$confname}->{namespace
} = $opts->{namespace
} if defined($opts->{namespace
});
7272 my $drive = parse_drive
($confname, $conf->{$confname});
7273 print "restoring '$ds' to '$drive->{file}'\n";
7276 my $drives_streamed = 0;
7278 # make sure HA doesn't interrupt our restore by stopping the VM
7279 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid)) {
7280 run_command
(['ha-manager', 'set', "vm:$vmid", '--state', 'started']);
7283 # start VM with backing chain pointing to PBS backup, environment vars for PBS driver
7284 # in QEMU (PBS_PASSWORD and PBS_FINGERPRINT) are already set by our caller
7285 vm_start_nolock
($storecfg, $vmid, $conf, {paused
=> 1, 'pbs-backing' => $pbs_backing}, {});
7287 my $qmeventd_fd = register_qmeventd_handle
($vmid);
7289 # begin streaming, i.e. data copy from PBS to target disk for every vol,
7290 # this will effectively collapse the backing image chain consisting of
7291 # [target <- alloc-track -> PBS snapshot] to just [target] (alloc-track
7292 # removes itself once all backing images vanish with 'auto-remove=on')
7294 for my $ds (sort keys %$restored_disks) {
7295 my $job_id = "restore-$ds";
7296 mon_cmd
($vmid, 'block-stream',
7297 'job-id' => $job_id,
7300 $jobs->{$job_id} = {};
7303 mon_cmd
($vmid, 'cont');
7304 qemu_drive_mirror_monitor
($vmid, undef, $jobs, 'auto', 0, 'stream');
7306 print "restore-drive jobs finished successfully, removing all tracking block devices"
7307 ." to disconnect from Proxmox Backup Server\n";
7309 for my $ds (sort keys %$restored_disks) {
7310 mon_cmd
($vmid, 'blockdev-del', 'node-name' => "$ds-pbs");
7313 close($qmeventd_fd);
7319 warn "An error occurred during live-restore: $err\n";
7320 _do_vm_stop
($storecfg, $vmid, 1, 1, 10, 0, 1);
7321 die "live-restore failed\n";
7325 sub restore_vma_archive
{
7326 my ($archive, $vmid, $user, $opts, $comp) = @_;
7328 my $readfrom = $archive;
7330 my $cfg = PVE
::Storage
::config
();
7332 my $bwlimit = $opts->{bwlimit
};
7334 my $dbg_cmdstring = '';
7335 my $add_pipe = sub {
7337 push @$commands, $cmd;
7338 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
7339 $dbg_cmdstring .= PVE
::Tools
::cmd2string
($cmd);
7344 if ($archive eq '-') {
7347 # If we use a backup from a PVE defined storage we also consider that
7348 # storage's rate limit:
7349 my (undef, $volid) = PVE
::Storage
::path_to_volume_id
($cfg, $archive);
7350 if (defined($volid)) {
7351 my ($sid, undef) = PVE
::Storage
::parse_volume_id
($volid);
7352 my $readlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [$sid], $bwlimit);
7354 print STDERR
"applying read rate limit: $readlimit\n";
7355 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
7356 $add_pipe->($cstream);
7362 my $info = PVE
::Storage
::decompressor_info
('vma', $comp);
7363 my $cmd = $info->{decompressor
};
7364 push @$cmd, $readfrom;
7368 my $tmpdir = "/var/tmp/vzdumptmp$$";
7371 # disable interrupts (always do cleanups)
7375 local $SIG{HUP
} = sub { warn "got interrupt - ignored\n"; };
7377 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
7378 POSIX
::mkfifo
($mapfifo, 0600);
7380 my $openfifo = sub { open($fifofh, '>', $mapfifo) or die $! };
7382 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
7384 my $devinfo = {}; # info about drives included in backup
7385 my $virtdev_hash = {}; # info about allocated drives
7387 my $rpcenv = PVE
::RPCEnvironment
::get
();
7389 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7391 # Note: $oldconf is undef if VM does not exist
7392 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
7393 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
7394 my $new_conf_raw = '';
7398 my $print_devmap = sub {
7399 my $cfgfn = "$tmpdir/qemu-server.conf";
7401 # we can read the config - that is already extracted
7402 my $fh = IO
::File-
>new($cfgfn, "r") ||
7403 die "unable to read qemu-server.conf - $!\n";
7405 my $fwcfgfn = "$tmpdir/qemu-server.fw";
7407 my $pve_firewall_dir = '/etc/pve/firewall';
7408 mkdir $pve_firewall_dir; # make sure the dir exists
7409 PVE
::Tools
::file_copy
($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
7412 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $cfg, $fh, $devinfo, $opts);
7414 foreach my $info (values %{$virtdev_hash}) {
7415 my $storeid = $info->{storeid
};
7416 next if defined($storage_limits{$storeid});
7418 my $limit = PVE
::Storage
::get_bandwidth_limit
('restore', [$storeid], $bwlimit) // 0;
7419 print STDERR
"rate limit for storage $storeid: $limit KiB/s\n" if $limit;
7420 $storage_limits{$storeid} = $limit * 1024;
7423 foreach my $devname (keys %$devinfo) {
7424 die "found no device mapping information for device '$devname'\n"
7425 if !$devinfo->{$devname}->{virtdev
};
7428 # create empty/temp config
7430 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\n");
7431 $restore_cleanup_oldconf->($cfg, $vmid, $oldconf, $virtdev_hash);
7435 my $map = $restore_allocate_devices->($cfg, $virtdev_hash, $vmid);
7437 # print restore information to $fifofh
7438 foreach my $virtdev (sort keys %$virtdev_hash) {
7439 my $d = $virtdev_hash->{$virtdev};
7440 next if $d->{is_cloudinit
}; # no need to restore cloudinit
7442 my $storeid = $d->{storeid
};
7443 my $volid = $d->{volid
};
7446 if (my $limit = $storage_limits{$storeid}) {
7447 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
7450 my $write_zeros = 1;
7451 if (PVE
::Storage
::volume_has_feature
($cfg, 'sparseinit', $volid)) {
7455 my $path = PVE
::Storage
::path
($cfg, $volid);
7457 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
7459 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
7462 $fh->seek(0, 0) || die "seek failed - $!\n";
7464 my $cookie = { netcount
=> 0 };
7465 while (defined(my $line = <$fh>)) {
7466 $new_conf_raw .= restore_update_config_line
(
7485 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7486 local $SIG{ALRM
} = sub { die "got timeout\n"; };
7488 $oldtimeout = alarm(5); # for reading the VMA header - might hang with a corrupted one
7495 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
7496 my ($dev_id, $size, $devname) = ($1, $2, $3);
7497 $devinfo->{$devname} = { size
=> $size, dev_id
=> $dev_id };
7498 } elsif ($line =~ m/^CTIME: /) {
7499 # we correctly received the vma config, so we can disable
7500 # the timeout now for disk allocation
7501 alarm($oldtimeout || 0);
7502 $oldtimeout = undef;
7504 print $fifofh "done\n";
7510 print "restore vma archive: $dbg_cmdstring\n";
7511 run_command
($commands, input
=> $input, outfunc
=> $parser, afterfork
=> $openfifo);
7515 alarm($oldtimeout) if $oldtimeout;
7517 $restore_deactivate_volumes->($cfg, $virtdev_hash);
7519 close($fifofh) if $fifofh;
7524 $restore_destroy_volumes->($cfg, $virtdev_hash);
7528 my $new_conf = restore_merge_config
($conffile, $new_conf_raw, $opts->{override_conf
});
7529 check_restore_permissions
($rpcenv, $user, $new_conf);
7530 PVE
::QemuConfig-
>write_config($vmid, $new_conf);
7532 eval { rescan
($vmid, 1); };
7535 PVE
::AccessControl
::add_vm_to_pool
($vmid, $opts->{pool
}) if $opts->{pool
};
7538 sub restore_tar_archive
{
7539 my ($archive, $vmid, $user, $opts) = @_;
7541 if (scalar(keys $opts->{override_conf
}->%*) > 0) {
7542 my $keystring = join(' ', keys $opts->{override_conf
}->%*);
7543 die "cannot pass along options ($keystring) when restoring from tar archive\n";
7546 if ($archive ne '-') {
7547 my $firstfile = tar_archive_read_firstfile
($archive);
7548 die "ERROR: file '$archive' does not look like a QemuServer vzdump backup\n"
7549 if $firstfile ne 'qemu-server.conf';
7552 my $storecfg = PVE
::Storage
::config
();
7554 # avoid zombie disks when restoring over an existing VM -> cleanup first
7555 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
7556 # skiplock=1 because qmrestore has set the 'create' lock itself already
7557 my $vmcfgfn = PVE
::QemuConfig-
>config_file($vmid);
7558 destroy_vm
($storecfg, $vmid, 1, { lock => 'restore' }) if -f
$vmcfgfn;
7560 my $tocmd = "/usr/lib/qemu-server/qmextract";
7562 $tocmd .= " --storage " . PVE
::Tools
::shellquote
($opts->{storage
}) if $opts->{storage
};
7563 $tocmd .= " --pool " . PVE
::Tools
::shellquote
($opts->{pool
}) if $opts->{pool
};
7564 $tocmd .= ' --prealloc' if $opts->{prealloc
};
7565 $tocmd .= ' --info' if $opts->{info
};
7567 # tar option "xf" does not autodetect compression when read from STDIN,
7568 # so we pipe to zcat
7569 my $cmd = "zcat -f|tar xf " . PVE
::Tools
::shellquote
($archive) . " " .
7570 PVE
::Tools
::shellquote
("--to-command=$tocmd");
7572 my $tmpdir = "/var/tmp/vzdumptmp$$";
7575 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
7576 local $ENV{VZDUMP_VMID
} = $vmid;
7577 local $ENV{VZDUMP_USER
} = $user;
7579 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7580 my $new_conf_raw = '';
7582 # disable interrupts (always do cleanups)
7586 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
7594 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7596 if ($archive eq '-') {
7597 print "extracting archive from STDIN\n";
7598 run_command
($cmd, input
=> "<&STDIN");
7600 print "extracting archive '$archive'\n";
7604 return if $opts->{info
};
7608 my $statfile = "$tmpdir/qmrestore.stat";
7609 if (my $fd = IO
::File-
>new($statfile, "r")) {
7610 while (defined (my $line = <$fd>)) {
7611 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
7612 $map->{$1} = $2 if $1;
7614 print STDERR
"unable to parse line in statfile - $line\n";
7620 my $confsrc = "$tmpdir/qemu-server.conf";
7622 my $srcfd = IO
::File-
>new($confsrc, "r") || die "unable to open file '$confsrc'\n";
7624 my $cookie = { netcount
=> 0 };
7625 while (defined (my $line = <$srcfd>)) {
7626 $new_conf_raw .= restore_update_config_line
(
7637 tar_restore_cleanup
($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info
};
7643 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
7645 PVE
::Cluster
::cfs_update
(); # make sure we read new file
7647 eval { rescan
($vmid, 1); };
7651 sub foreach_storage_used_by_vm
{
7652 my ($conf, $func) = @_;
7656 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7657 my ($ds, $drive) = @_;
7658 return if drive_is_cdrom
($drive);
7660 my $volid = $drive->{file
};
7662 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
7663 $sidhash->{$sid} = $sid if $sid;
7666 foreach my $sid (sort keys %$sidhash) {
7671 my $qemu_snap_storage = {
7674 sub do_snapshots_with_qemu
{
7675 my ($storecfg, $volid, $deviceid) = @_;
7677 return if $deviceid =~ m/tpmstate0/;
7679 my $storage_name = PVE
::Storage
::parse_volume_id
($volid);
7680 my $scfg = $storecfg->{ids
}->{$storage_name};
7681 die "could not find storage '$storage_name'\n" if !defined($scfg);
7683 if ($qemu_snap_storage->{$scfg->{type
}} && !$scfg->{krbd
}){
7687 if ($volid =~ m/\.(qcow2|qed)$/){
7694 sub qga_check_running
{
7695 my ($vmid, $nowarn) = @_;
7697 eval { mon_cmd
($vmid, "guest-ping", timeout
=> 3); };
7699 warn "QEMU Guest Agent is not running - $@" if !$nowarn;
7705 sub template_create
{
7706 my ($vmid, $conf, $disk) = @_;
7708 my $storecfg = PVE
::Storage
::config
();
7710 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7711 my ($ds, $drive) = @_;
7713 return if drive_is_cdrom
($drive);
7714 return if $disk && $ds ne $disk;
7716 my $volid = $drive->{file
};
7717 return if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
7719 my $voliddst = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
7720 $drive->{file
} = $voliddst;
7721 $conf->{$ds} = print_drive
($drive);
7722 PVE
::QemuConfig-
>write_config($vmid, $conf);
7726 sub convert_iscsi_path
{
7729 if ($path =~ m
|^iscsi
://([^/]+)/([^/]+)/(.+)$|) {
7734 my $initiator_name = get_initiator_name
();
7736 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
7737 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
7740 die "cannot convert iscsi path '$path', unkown format\n";
7743 sub qemu_img_convert
{
7744 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized, $bwlimit) = @_;
7746 my $storecfg = PVE
::Storage
::config
();
7747 my ($src_storeid, $src_volname) = PVE
::Storage
::parse_volume_id
($src_volid, 1);
7748 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid, 1);
7750 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
7754 my $src_is_iscsi = 0;
7758 PVE
::Storage
::activate_volumes
($storecfg, [$src_volid], $snapname);
7759 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
7760 $src_format = qemu_img_format
($src_scfg, $src_volname);
7761 $src_path = PVE
::Storage
::path
($storecfg, $src_volid, $snapname);
7762 $src_is_iscsi = ($src_path =~ m
|^iscsi
://|);
7763 $cachemode = 'none' if $src_scfg->{type
} eq 'zfspool';
7764 } elsif (-f
$src_volid || -b
$src_volid) {
7765 $src_path = $src_volid;
7766 if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7771 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
7773 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7774 my $dst_format = qemu_img_format
($dst_scfg, $dst_volname);
7775 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7776 my $dst_is_iscsi = ($dst_path =~ m
|^iscsi
://|);
7779 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
7780 push @$cmd, '-l', "snapshot.name=$snapname"
7781 if $snapname && $src_format && $src_format eq "qcow2";
7782 push @$cmd, '-t', 'none' if $dst_scfg->{type
} eq 'zfspool';
7783 push @$cmd, '-T', $cachemode if defined($cachemode);
7784 push @$cmd, '-r', "${bwlimit}K" if defined($bwlimit);
7786 if ($src_is_iscsi) {
7787 push @$cmd, '--image-opts';
7788 $src_path = convert_iscsi_path
($src_path);
7789 } elsif ($src_format) {
7790 push @$cmd, '-f', $src_format;
7793 if ($dst_is_iscsi) {
7794 push @$cmd, '--target-image-opts';
7795 $dst_path = convert_iscsi_path
($dst_path);
7797 push @$cmd, '-O', $dst_format;
7800 push @$cmd, $src_path;
7802 if (!$dst_is_iscsi && $is_zero_initialized) {
7803 push @$cmd, "zeroinit:$dst_path";
7805 push @$cmd, $dst_path;
7810 if($line =~ m/\((\S+)\/100\
%\)/){
7812 my $transferred = int($size * $percent / 100);
7813 my $total_h = render_bytes
($size, 1);
7814 my $transferred_h = render_bytes
($transferred, 1);
7816 print "transferred $transferred_h of $total_h ($percent%)\n";
7821 eval { run_command
($cmd, timeout
=> undef, outfunc
=> $parser); };
7823 die "copy failed: $err" if $err;
7826 sub qemu_img_format
{
7827 my ($scfg, $volname) = @_;
7829 if ($scfg->{path
} && $volname =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7836 sub qemu_drive_mirror
{
7837 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
7839 $jobs = {} if !$jobs;
7843 $jobs->{"drive-$drive"} = {};
7845 if ($dst_volid =~ /^nbd:/) {
7846 $qemu_target = $dst_volid;
7849 my $storecfg = PVE
::Storage
::config
();
7850 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid);
7852 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7854 $format = qemu_img_format
($dst_scfg, $dst_volname);
7856 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7858 $qemu_target = $is_zero_initialized ?
"zeroinit:$dst_path" : $dst_path;
7861 my $opts = { timeout
=> 10, device
=> "drive-$drive", mode
=> "existing", sync
=> "full", target
=> $qemu_target };
7862 $opts->{format
} = $format if $format;
7864 if (defined($src_bitmap)) {
7865 $opts->{sync
} = 'incremental';
7866 $opts->{bitmap
} = $src_bitmap;
7867 print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
7870 if (defined($bwlimit)) {
7871 $opts->{speed
} = $bwlimit * 1024;
7872 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
7874 print "drive mirror is starting for drive-$drive\n";
7877 # if a job already runs for this device we get an error, catch it for cleanup
7878 eval { mon_cmd
($vmid, "drive-mirror", %$opts); };
7880 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7882 die "mirroring error: $err\n";
7885 qemu_drive_mirror_monitor
($vmid, $vmiddst, $jobs, $completion, $qga);
7888 # $completion can be either
7889 # 'complete': wait until all jobs are ready, block-job-complete them (default)
7890 # 'cancel': wait until all jobs are ready, block-job-cancel them
7891 # 'skip': wait until all jobs are ready, return with block jobs in ready state
7892 # 'auto': wait until all jobs disappear, only use for jobs which complete automatically
7893 sub qemu_drive_mirror_monitor
{
7894 my ($vmid, $vmiddst, $jobs, $completion, $qga, $op) = @_;
7896 $completion //= 'complete';
7900 my $err_complete = 0;
7902 my $starttime = time ();
7904 die "block job ('$op') timed out\n" if $err_complete > 300;
7906 my $stats = mon_cmd
($vmid, "query-block-jobs");
7909 my $running_jobs = {};
7910 for my $stat (@$stats) {
7911 next if $stat->{type
} ne $op;
7912 $running_jobs->{$stat->{device
}} = $stat;
7915 my $readycounter = 0;
7917 for my $job_id (sort keys %$jobs) {
7918 my $job = $running_jobs->{$job_id};
7920 my $vanished = !defined($job);
7921 my $complete = defined($jobs->{$job_id}->{complete
}) && $vanished;
7922 if($complete || ($vanished && $completion eq 'auto')) {
7923 print "$job_id: $op-job finished\n";
7924 delete $jobs->{$job_id};
7928 die "$job_id: '$op' has been cancelled\n" if !defined($job);
7930 my $busy = $job->{busy
};
7931 my $ready = $job->{ready
};
7932 if (my $total = $job->{len
}) {
7933 my $transferred = $job->{offset
} || 0;
7934 my $remaining = $total - $transferred;
7935 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
7937 my $duration = $ctime - $starttime;
7938 my $total_h = render_bytes
($total, 1);
7939 my $transferred_h = render_bytes
($transferred, 1);
7941 my $status = sprintf(
7942 "transferred $transferred_h of $total_h ($percent%%) in %s",
7943 render_duration
($duration),
7948 $status .= ", still busy"; # shouldn't even happen? but mirror is weird
7950 $status .= ", ready";
7953 print "$job_id: $status\n" if !$jobs->{$job_id}->{ready
};
7954 $jobs->{$job_id}->{ready
} = $ready;
7957 $readycounter++ if $job->{ready
};
7960 last if scalar(keys %$jobs) == 0;
7962 if ($readycounter == scalar(keys %$jobs)) {
7963 print "all '$op' jobs are ready\n";
7965 # do the complete later (or has already been done)
7966 last if $completion eq 'skip' || $completion eq 'auto';
7968 if ($vmiddst && $vmiddst != $vmid) {
7969 my $agent_running = $qga && qga_check_running
($vmid);
7970 if ($agent_running) {
7971 print "freeze filesystem\n";
7972 eval { mon_cmd
($vmid, "guest-fsfreeze-freeze"); };
7975 print "suspend vm\n";
7976 eval { PVE
::QemuServer
::vm_suspend
($vmid, 1); };
7980 # if we clone a disk for a new target vm, we don't switch the disk
7981 PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs);
7983 if ($agent_running) {
7984 print "unfreeze filesystem\n";
7985 eval { mon_cmd
($vmid, "guest-fsfreeze-thaw"); };
7988 print "resume vm\n";
7989 eval { PVE
::QemuServer
::vm_resume
($vmid, 1, 1); };
7996 for my $job_id (sort keys %$jobs) {
7997 # try to switch the disk if source and destination are on the same guest
7998 print "$job_id: Completing block job_id...\n";
8001 if ($completion eq 'complete') {
8002 $op = 'block-job-complete';
8003 } elsif ($completion eq 'cancel') {
8004 $op = 'block-job-cancel';
8006 die "invalid completion value: $completion\n";
8008 eval { mon_cmd
($vmid, $op, device
=> $job_id) };
8009 if ($@ =~ m/cannot be completed/) {
8010 print "$job_id: block job cannot be completed, trying again.\n";
8013 print "$job_id: Completed successfully.\n";
8014 $jobs->{$job_id}->{complete
} = 1;
8025 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
8026 die "block job ($op) error: $err";
8030 sub qemu_blockjobs_cancel
{
8031 my ($vmid, $jobs) = @_;
8033 foreach my $job (keys %$jobs) {
8034 print "$job: Cancelling block job\n";
8035 eval { mon_cmd
($vmid, "block-job-cancel", device
=> $job); };
8036 $jobs->{$job}->{cancel
} = 1;
8040 my $stats = mon_cmd
($vmid, "query-block-jobs");
8042 my $running_jobs = {};
8043 foreach my $stat (@$stats) {
8044 $running_jobs->{$stat->{device
}} = $stat;
8047 foreach my $job (keys %$jobs) {
8049 if (defined($jobs->{$job}->{cancel
}) && !defined($running_jobs->{$job})) {
8050 print "$job: Done.\n";
8051 delete $jobs->{$job};
8055 last if scalar(keys %$jobs) == 0;
8061 # Check for bug #4525: drive-mirror will open the target drive with the same aio setting as the
8062 # source, but some storages have problems with io_uring, sometimes even leading to crashes.
8063 my sub clone_disk_check_io_uring
{
8064 my ($src_drive, $storecfg, $src_storeid, $dst_storeid, $use_drive_mirror) = @_;
8066 return if !$use_drive_mirror;
8068 # Don't complain when not changing storage.
8069 # Assume if it works for the source, it'll work for the target too.
8070 return if $src_storeid eq $dst_storeid;
8072 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
8073 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
8075 my $cache_direct = drive_uses_cache_direct
($src_drive);
8077 my $src_uses_io_uring;
8078 if ($src_drive->{aio
}) {
8079 $src_uses_io_uring = $src_drive->{aio
} eq 'io_uring';
8081 $src_uses_io_uring = storage_allows_io_uring_default
($src_scfg, $cache_direct);
8084 die "target storage is known to cause issues with aio=io_uring (used by current drive)\n"
8085 if $src_uses_io_uring && !storage_allows_io_uring_default
($dst_scfg, $cache_direct);
8089 my ($storecfg, $source, $dest, $full, $newvollist, $jobs, $completion, $qga, $bwlimit) = @_;
8091 my ($vmid, $running) = $source->@{qw(vmid running)};
8092 my ($src_drivename, $drive, $snapname) = $source->@{qw(drivename drive snapname)};
8094 my ($newvmid, $dst_drivename, $efisize) = $dest->@{qw(vmid drivename efisize)};
8095 my ($storage, $format) = $dest->@{qw(storage format)};
8097 my $use_drive_mirror = $full && $running && $src_drivename && !$snapname;
8099 if ($src_drivename && $dst_drivename && $src_drivename ne $dst_drivename) {
8100 die "cloning from/to EFI disk requires EFI disk\n"
8101 if $src_drivename eq 'efidisk0' || $dst_drivename eq 'efidisk0';
8102 die "cloning from/to TPM state requires TPM state\n"
8103 if $src_drivename eq 'tpmstate0' || $dst_drivename eq 'tpmstate0';
8105 # This would lead to two device nodes in QEMU pointing to the same backing image!
8106 die "cannot change drive name when cloning disk from/to the same VM\n"
8107 if $use_drive_mirror && $vmid == $newvmid;
8110 die "cannot move TPM state while VM is running\n"
8111 if $use_drive_mirror && $src_drivename eq 'tpmstate0';
8115 print "create " . ($full ?
'full' : 'linked') . " clone of drive ";
8116 print "$src_drivename " if $src_drivename;
8117 print "($drive->{file})\n";
8120 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $drive->{file
}, $newvmid, $snapname);
8121 push @$newvollist, $newvolid;
8123 my ($src_storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
8124 my $storeid = $storage || $src_storeid;
8126 my $dst_format = resolve_dst_disk_format
($storecfg, $storeid, $volname, $format);
8130 if (drive_is_cloudinit
($drive)) {
8131 $name = "vm-$newvmid-cloudinit";
8132 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8133 if ($scfg->{path
}) {
8134 $name .= ".$dst_format";
8137 $size = PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
;
8138 } elsif ($dst_drivename eq 'efidisk0') {
8139 $size = $efisize or die "internal error - need to specify EFI disk size\n";
8140 } elsif ($dst_drivename eq 'tpmstate0') {
8141 $dst_format = 'raw';
8142 $size = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
8144 clone_disk_check_io_uring
($drive, $storecfg, $src_storeid, $storeid, $use_drive_mirror);
8146 $size = PVE
::Storage
::volume_size_info
($storecfg, $drive->{file
}, 10);
8148 $newvolid = PVE
::Storage
::vdisk_alloc
(
8149 $storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024)
8151 push @$newvollist, $newvolid;
8153 PVE
::Storage
::activate_volumes
($storecfg, [$newvolid]);
8155 if (drive_is_cloudinit
($drive)) {
8156 # when cloning multiple disks (e.g. during clone_vm) it might be the last disk
8157 # if this is the case, we have to complete any block-jobs still there from
8158 # previous drive-mirrors
8159 if (($completion eq 'complete') && (scalar(keys %$jobs) > 0)) {
8160 qemu_drive_mirror_monitor
($vmid, $newvmid, $jobs, $completion, $qga);
8165 my $sparseinit = PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $newvolid);
8166 if ($use_drive_mirror) {
8167 qemu_drive_mirror
($vmid, $src_drivename, $newvolid, $newvmid, $sparseinit, $jobs,
8168 $completion, $qga, $bwlimit);
8170 if ($dst_drivename eq 'efidisk0') {
8171 # the relevant data on the efidisk may be smaller than the source
8172 # e.g. on RBD/ZFS, so we use dd to copy only the amount
8173 # that is given by the OVMF_VARS.fd
8174 my $src_path = PVE
::Storage
::path
($storecfg, $drive->{file
}, $snapname);
8175 my $dst_path = PVE
::Storage
::path
($storecfg, $newvolid);
8177 my $src_format = (PVE
::Storage
::parse_volname
($storecfg, $drive->{file
}))[6];
8179 # better for Ceph if block size is not too small, see bug #3324
8182 my $cmd = ['qemu-img', 'dd', '-n', '-O', $dst_format];
8184 if ($src_format eq 'qcow2' && $snapname) {
8185 die "cannot clone qcow2 EFI disk snapshot - requires QEMU >= 6.2\n"
8186 if !min_version
(kvm_user_version
(), 6, 2);
8187 push $cmd->@*, '-l', $snapname;
8189 push $cmd->@*, "bs=$bs", "osize=$size", "if=$src_path", "of=$dst_path";
8192 qemu_img_convert
($drive->{file
}, $newvolid, $size, $snapname, $sparseinit, $bwlimit);
8198 my $size = eval { PVE
::Storage
::volume_size_info
($storecfg, $newvolid, 10) };
8200 my $disk = dclone
($drive);
8201 delete $disk->{format
};
8202 $disk->{file
} = $newvolid;
8203 $disk->{size
} = $size if defined($size);
8208 sub get_running_qemu_version
{
8210 my $res = mon_cmd
($vmid, "query-version");
8211 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
8214 sub qemu_use_old_bios_files
{
8215 my ($machine_type) = @_;
8217 return if !$machine_type;
8219 my $use_old_bios_files = undef;
8221 if ($machine_type =~ m/^(\S+)\.pxe$/) {
8223 $use_old_bios_files = 1;
8225 my $version = extract_version
($machine_type, kvm_user_version
());
8226 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
8227 # load new efi bios files on migration. So this hack is required to allow
8228 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
8229 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
8230 $use_old_bios_files = !min_version
($version, 2, 4);
8233 return ($use_old_bios_files, $machine_type);
8236 sub get_efivars_size
{
8237 my ($conf, $efidisk) = @_;
8239 my $arch = get_vm_arch
($conf);
8240 $efidisk //= $conf->{efidisk0
} ? parse_drive
('efidisk0', $conf->{efidisk0
}) : undef;
8241 my $smm = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
8242 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
8243 return -s
$ovmf_vars;
8246 sub update_efidisk_size
{
8249 return if !defined($conf->{efidisk0
});
8251 my $disk = PVE
::QemuServer
::parse_drive
('efidisk0', $conf->{efidisk0
});
8252 $disk->{size
} = get_efivars_size
($conf);
8253 $conf->{efidisk0
} = print_drive
($disk);
8258 sub update_tpmstate_size
{
8261 my $disk = PVE
::QemuServer
::parse_drive
('tpmstate0', $conf->{tpmstate0
});
8262 $disk->{size
} = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
8263 $conf->{tpmstate0
} = print_drive
($disk);
8266 sub create_efidisk
($$$$$$$) {
8267 my ($storecfg, $storeid, $vmid, $fmt, $arch, $efidisk, $smm) = @_;
8269 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
8271 my $vars_size_b = -s
$ovmf_vars;
8272 my $vars_size = PVE
::Tools
::convert_size
($vars_size_b, 'b' => 'kb');
8273 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
8274 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
8276 qemu_img_convert
($ovmf_vars, $volid, $vars_size_b, undef, 0);
8277 my $size = PVE
::Storage
::volume_size_info
($storecfg, $volid, 3);
8279 return ($volid, $size/1024);
8282 sub vm_iothreads_list
{
8285 my $res = mon_cmd
($vmid, 'query-iothreads');
8288 foreach my $iothread (@$res) {
8289 $iothreads->{ $iothread->{id
} } = $iothread->{"thread-id"};
8296 my ($conf, $drive) = @_;
8300 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)) {
8302 } elsif ($conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single')) {
8308 my $controller = int($drive->{index} / $maxdev);
8309 my $controller_prefix = ($conf->{scsihw
} && $conf->{scsihw
} eq 'virtio-scsi-single')
8313 return ($maxdev, $controller, $controller_prefix);
8316 sub resolve_dst_disk_format
{
8317 my ($storecfg, $storeid, $src_volname, $format) = @_;
8318 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
8321 # if no target format is specified, use the source disk format as hint
8323 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8324 $format = qemu_img_format
($scfg, $src_volname);
8330 # test if requested format is supported - else use default
8331 my $supported = grep { $_ eq $format } @$validFormats;
8332 $format = $defFormat if !$supported;
8336 # NOTE: if this logic changes, please update docs & possibly gui logic
8337 sub find_vmstate_storage
{
8338 my ($conf, $storecfg) = @_;
8340 # first, return storage from conf if set
8341 return $conf->{vmstatestorage
} if $conf->{vmstatestorage
};
8343 my ($target, $shared, $local);
8345 foreach_storage_used_by_vm
($conf, sub {
8347 my $scfg = PVE
::Storage
::storage_config
($storecfg, $sid);
8348 my $dst = $scfg->{shared
} ? \
$shared : \
$local;
8349 $$dst = $sid if !$$dst || $scfg->{path
}; # prefer file based storage
8352 # second, use shared storage where VM has at least one disk
8353 # third, use local storage where VM has at least one disk
8354 # fall back to local storage
8355 $target = $shared // $local // 'local';
8361 my ($uuid, $uuid_str);
8362 UUID
::generate
($uuid);
8363 UUID
::unparse
($uuid, $uuid_str);
8367 sub generate_smbios1_uuid
{
8368 return "uuid=".generate_uuid
();
8374 mon_cmd
($vmid, 'nbd-server-stop', timeout
=> 25);
8377 sub create_reboot_request
{
8379 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
8380 or die "failed to create reboot trigger file: $!\n";
8384 sub clear_reboot_request
{
8386 my $path = "/run/qemu-server/$vmid.reboot";
8389 $res = unlink($path);
8390 die "could not remove reboot request for $vmid: $!"
8391 if !$res && $! != POSIX
::ENOENT
;
8396 sub bootorder_from_legacy
{
8397 my ($conf, $bootcfg) = @_;
8399 my $boot = $bootcfg->{legacy
} || $boot_fmt->{legacy
}->{default};
8400 my $bootindex_hash = {};
8402 foreach my $o (split(//, $boot)) {
8403 $bootindex_hash->{$o} = $i*100;
8409 PVE
::QemuConfig-
>foreach_volume($conf, sub {
8410 my ($ds, $drive) = @_;
8412 if (drive_is_cdrom
($drive, 1)) {
8413 if ($bootindex_hash->{d
}) {
8414 $bootorder->{$ds} = $bootindex_hash->{d
};
8415 $bootindex_hash->{d
} += 1;
8417 } elsif ($bootindex_hash->{c
}) {
8418 $bootorder->{$ds} = $bootindex_hash->{c
}
8419 if $conf->{bootdisk
} && $conf->{bootdisk
} eq $ds;
8420 $bootindex_hash->{c
} += 1;
8424 if ($bootindex_hash->{n
}) {
8425 for (my $i = 0; $i < $MAX_NETS; $i++) {
8426 my $netname = "net$i";
8427 next if !$conf->{$netname};
8428 $bootorder->{$netname} = $bootindex_hash->{n
};
8429 $bootindex_hash->{n
} += 1;
8436 # Generate default device list for 'boot: order=' property. Matches legacy
8437 # default boot order, but with explicit device names. This is important, since
8438 # the fallback for when neither 'order' nor the old format is specified relies
8439 # on 'bootorder_from_legacy' above, and it would be confusing if this diverges.
8440 sub get_default_bootdevices
{
8446 my $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 0);
8447 push @ret, $first if $first;
8450 $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 1);
8451 push @ret, $first if $first;
8454 for (my $i = 0; $i < $MAX_NETS; $i++) {
8455 my $netname = "net$i";
8456 next if !$conf->{$netname};
8457 push @ret, $netname;
8464 sub device_bootorder
{
8467 return bootorder_from_legacy
($conf) if !defined($conf->{boot
});
8469 my $boot = parse_property_string
($boot_fmt, $conf->{boot
});
8472 if (!defined($boot) || $boot->{legacy
}) {
8473 $bootorder = bootorder_from_legacy
($conf, $boot);
8474 } elsif ($boot->{order
}) {
8475 my $i = 100; # start at 100 to allow user to insert devices before us with -args
8476 for my $dev (PVE
::Tools
::split_list
($boot->{order
})) {
8477 $bootorder->{$dev} = $i++;
8484 sub register_qmeventd_handle
{
8488 my $peer = "/var/run/qmeventd.sock";
8493 $fh = IO
::Socket
::UNIX-
>new(Peer
=> $peer, Blocking
=> 0, Timeout
=> 1);
8495 if ($! != EINTR
&& $! != EAGAIN
) {
8496 die "unable to connect to qmeventd socket (vmid: $vmid) - $!\n";
8499 die "unable to connect to qmeventd socket (vmid: $vmid) - timeout "
8500 . "after $count retries\n";
8505 # send handshake to mark VM as backing up
8506 print $fh to_json
({vzdump
=> {vmid
=> "$vmid"}});
8508 # return handle to be closed later when inhibit is no longer required
8512 # bash completion helper
8514 sub complete_backup_archives
{
8515 my ($cmdname, $pname, $cvalue) = @_;
8517 my $cfg = PVE
::Storage
::config
();
8521 if ($cvalue =~ m/^([^:]+):/) {
8525 my $data = PVE
::Storage
::template_list
($cfg, $storeid, 'backup');
8528 foreach my $id (keys %$data) {
8529 foreach my $item (@{$data->{$id}}) {
8530 next if $item->{format
} !~ m/^vma\.(${\PVE::Storage::Plugin::COMPRESSOR_RE})$/;
8531 push @$res, $item->{volid
} if defined($item->{volid
});
8538 my $complete_vmid_full = sub {
8541 my $idlist = vmstatus
();
8545 foreach my $id (keys %$idlist) {
8546 my $d = $idlist->{$id};
8547 if (defined($running)) {
8548 next if $d->{template
};
8549 next if $running && $d->{status
} ne 'running';
8550 next if !$running && $d->{status
} eq 'running';
8559 return &$complete_vmid_full();
8562 sub complete_vmid_stopped
{
8563 return &$complete_vmid_full(0);
8566 sub complete_vmid_running
{
8567 return &$complete_vmid_full(1);
8570 sub complete_storage
{
8572 my $cfg = PVE
::Storage
::config
();
8573 my $ids = $cfg->{ids
};
8576 foreach my $sid (keys %$ids) {
8577 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, undef, 1);
8578 next if !$ids->{$sid}->{content
}->{images
};
8585 sub complete_migration_storage
{
8586 my ($cmd, $param, $current_value, $all_args) = @_;
8588 my $targetnode = @$all_args[1];
8590 my $cfg = PVE
::Storage
::config
();
8591 my $ids = $cfg->{ids
};
8594 foreach my $sid (keys %$ids) {
8595 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, $targetnode, 1);
8596 next if !$ids->{$sid}->{content
}->{images
};
8604 my ($vmid, $include_suspended) = @_;
8605 my $qmpstatus = eval {
8606 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid);
8607 mon_cmd
($vmid, "query-status");
8610 return $qmpstatus && (
8611 $qmpstatus->{status
} eq "paused" ||
8612 $qmpstatus->{status
} eq "prelaunch" ||
8613 ($include_suspended && $qmpstatus->{status
} eq "suspended")
8617 sub check_volume_storage_type
{
8618 my ($storecfg, $vol) = @_;
8620 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($vol);
8621 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8622 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $vol);
8624 die "storage '$storeid' does not support content-type '$vtype'\n"
8625 if !$scfg->{content
}->{$vtype};
8630 sub add_nets_bridge_fdb
{
8631 my ($conf, $vmid) = @_;
8633 for my $opt (keys %$conf) {
8634 next if $opt !~ m/^net(\d+)$/;
8635 my $iface = "tap${vmid}i$1";
8636 # NOTE: expect setups with learning off to *not* use auto-random-generation of MAC on start
8637 my $net = parse_net
($conf->{$opt}, 1) or next;
8639 my $mac = $net->{macaddr
};
8641 log_warn
("MAC learning disabled, but vNIC '$iface' has no static MAC to add to forwarding DB!")
8642 if !file_read_firstline
("/sys/class/net/$iface/brport/learning");
8646 my $bridge = $net->{bridge
};
8648 log_warn
("Interface '$iface' not attached to any bridge.");
8652 PVE
::Network
::SDN
::Zones
::add_bridge_fdb
($iface, $mac, $bridge);
8653 } elsif (-d
"/sys/class/net/$bridge/bridge") { # avoid fdb management with OVS for now
8654 PVE
::Network
::add_bridge_fdb
($iface, $mac);
8659 sub del_nets_bridge_fdb
{
8660 my ($conf, $vmid) = @_;
8662 for my $opt (keys %$conf) {
8663 next if $opt !~ m/^net(\d+)$/;
8664 my $iface = "tap${vmid}i$1";
8666 my $net = parse_net
($conf->{$opt}) or next;
8667 my $mac = $net->{macaddr
} or next;
8669 my $bridge = $net->{bridge
};
8671 PVE
::Network
::SDN
::Zones
::del_bridge_fdb
($iface, $mac, $bridge);
8672 } elsif (-d
"/sys/class/net/$bridge/bridge") { # avoid fdb management with OVS for now
8673 PVE
::Network
::del_bridge_fdb
($iface, $mac);
8678 sub create_ifaces_ipams_ips
{
8679 my ($conf, $vmid) = @_;
8681 return if !$have_sdn;
8683 foreach my $opt (keys %$conf) {
8684 if ($opt =~ m/^net(\d+)$/) {
8685 my $value = $conf->{$opt};
8686 my $net = PVE
::QemuServer
::parse_net
($value);
8687 eval { PVE
::Network
::SDN
::Vnets
::add_next_free_cidr
($net->{bridge
}, $conf->{name
}, $net->{macaddr
}, $vmid, undef, 1) };
8693 sub delete_ifaces_ipams_ips
{
8694 my ($conf, $vmid) = @_;
8696 return if !$have_sdn;
8698 foreach my $opt (keys %$conf) {
8699 if ($opt =~ m/^net(\d+)$/) {
8700 my $net = PVE
::QemuServer
::parse_net
($conf->{$opt});
8701 eval { PVE
::Network
::SDN
::Vnets
::del_ips_from_mac
($net->{bridge
}, $net->{macaddr
}, $conf->{name
}) };