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(min_version config_aware_timeout 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
;
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
::USB
;
65 require PVE
::Network
::SDN
::Zones
;
69 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
73 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
74 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
77 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
78 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
81 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
82 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
85 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
86 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
89 "$EDK2_FW_BASE/OVMF_CODE.fd",
90 "$EDK2_FW_BASE/OVMF_VARS.fd",
95 "$EDK2_FW_BASE/AAVMF_CODE.fd",
96 "$EDK2_FW_BASE/AAVMF_VARS.fd",
101 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
103 # Note about locking: we use flock on the config file protect against concurent actions.
104 # Aditionaly, we have a 'lock' setting in the config file. This can be set to 'migrate',
105 # 'backup', 'snapshot' or 'rollback'. Most actions are not allowed when such lock is set.
106 # But you can ignore this kind of lock with the --skiplock flag.
114 PVE
::JSONSchema
::register_standard_option
('pve-qm-stateuri', {
115 description
=> "Some command save/restore state from this location.",
121 PVE
::JSONSchema
::register_standard_option
('pve-qemu-machine', {
122 description
=> "Specifies the QEMU machine type.",
124 pattern
=> '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
129 # FIXME: remove in favor of just using the INotify one, it's cached there exactly the same way
132 $nodename_cache //= PVE
::INotify
::nodename
();
133 return $nodename_cache;
140 enum
=> [qw(i6300esb ib700)],
141 description
=> "Watchdog type to emulate.",
142 default => 'i6300esb',
147 enum
=> [qw(reset shutdown poweroff pause debug none)],
148 description
=> "The action to perform if after activation the guest fails to poll the watchdog in time.",
152 PVE
::JSONSchema
::register_format
('pve-qm-watchdog', $watchdog_fmt);
156 description
=> "Enable/disable communication with a QEMU Guest Agent (QGA) running in the VM.",
161 fstrim_cloned_disks
=> {
162 description
=> "Run fstrim after moving a disk or migrating the VM.",
167 'freeze-fs-on-backup' => {
168 description
=> "Freeze/thaw guest filesystems on backup for consistency.",
174 description
=> "Select the agent type",
178 enum
=> [qw(virtio isa)],
184 description
=> "Select the VGA type.",
189 enum
=> [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio virtio-gl vmware)],
192 description
=> "Sets the VGA memory (in MiB). Has no effect with serial display.",
204 description
=> "The size of the file in MB.",
208 pattern
=> '[a-zA-Z0-9\-]+',
210 format_description
=> 'string',
211 description
=> "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
218 enum
=> [qw(ich9-intel-hda intel-hda AC97)],
219 description
=> "Configure an audio device."
223 enum
=> ['spice', 'none'],
226 description
=> "Driver backend for the audio device."
230 my $spice_enhancements_fmt = {
235 description
=> "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
239 enum
=> ['off', 'all', 'filter'],
242 description
=> "Enable video streaming. Uses compression for detected video streams."
249 enum
=> ['/dev/urandom', '/dev/random', '/dev/hwrng'],
251 description
=> "The file on the host to gather entropy from. In most cases '/dev/urandom'"
252 ." should be preferred over '/dev/random' to avoid entropy-starvation issues on the"
253 ." host. Using urandom does *not* decrease security in any meaningful way, as it's"
254 ." still seeded from real entropy, and the bytes provided will most likely be mixed"
255 ." with real entropy on the guest as well. '/dev/hwrng' can be used to pass through"
256 ." a hardware RNG from the host.",
260 description
=> "Maximum bytes of entropy allowed to get injected into the guest every"
261 ." 'period' milliseconds. Prefer a lower value when using '/dev/random' as source. Use"
262 ." `0` to disable limiting (potentially dangerous!).",
265 # default is 1 KiB/s, provides enough entropy to the guest to avoid boot-starvation issues
266 # (e.g. systemd etc...) while allowing no chance of overwhelming the host, provided we're
267 # reading from /dev/urandom
272 description
=> "Every 'period' milliseconds the entropy-injection quota is reset, allowing"
273 ." the guest to retrieve another 'max_bytes' of entropy.",
279 my $meta_info_fmt = {
282 description
=> "The guest creation timestamp as UNIX epoch time",
288 description
=> "The QEMU (machine) version from the time this VM was created.",
289 pattern
=> '\d+(\.\d+)+',
298 description
=> "Specifies whether a VM will be started during system bootup.",
304 description
=> "Automatic restart after crash (currently ignored).",
309 type
=> 'string', format
=> 'pve-hotplug-features',
310 description
=> "Selectively enable hotplug features. This is a comma separated list of"
311 ." hotplug features: 'network', 'disk', 'cpu', 'memory', 'usb' and 'cloudinit'. Use '0' to disable"
312 ." hotplug completely. Using '1' as value is an alias for the default `network,disk,usb`."
313 ." USB hotplugging is possible for guests with machine version >= 7.1 and ostype l26 or"
315 default => 'network,disk,usb',
320 description
=> "Allow reboot. If set to '0' the VM exit on reboot.",
326 description
=> "Lock/unlock the VM.",
327 enum
=> [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
332 description
=> "Limit of CPU usage.",
333 verbose_description
=> "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has"
334 ." total of '2' CPU time. Value '0' indicates no CPU limit.",
342 description
=> "CPU weight for a VM, will be clamped to [1, 10000] in cgroup v2.",
343 verbose_description
=> "CPU weight for a VM. Argument is used in the kernel fair scheduler."
344 ." The larger the number is, the more CPU time this VM gets. Number is relative to"
345 ." weights of all the other running VMs.",
348 default => 'cgroup v1: 1024, cgroup v2: 100',
353 description
=> "Amount of RAM for the VM in MiB. This is the maximum available memory when"
354 ." you use the balloon device.",
361 description
=> "Amount of target RAM for the VM in MiB. Using zero disables the ballon driver.",
367 description
=> "Amount of memory shares for auto-ballooning. The larger the number is, the"
368 ." more memory this VM gets. Number is relative to weights of all other running VMs."
369 ." Using zero disables auto-ballooning. Auto-ballooning is done by pvestatd.",
377 description
=> "Keyboard layout for VNC server. This option is generally not required and"
378 ." is often better handled from within the guest OS.",
379 enum
=> PVE
::Tools
::kvmkeymaplist
(),
384 type
=> 'string', format
=> 'dns-name',
385 description
=> "Set a name for the VM. Only used on the configuration web interface.",
390 description
=> "SCSI controller model",
391 enum
=> [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
397 description
=> "Description for the VM. Shown in the web-interface VM's summary."
398 ." This is saved as comment inside the configuration file.",
399 maxLength
=> 1024 * 8,
404 enum
=> [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 win11 l24 l26 solaris)],
405 description
=> "Specify guest operating system.",
406 verbose_description
=> <<EODESC,
407 Specify guest operating system. This is used to enable special
408 optimization/features for specific operating systems:
411 other;; unspecified OS
412 wxp;; Microsoft Windows XP
413 w2k;; Microsoft Windows 2000
414 w2k3;; Microsoft Windows 2003
415 w2k8;; Microsoft Windows 2008
416 wvista;; Microsoft Windows Vista
417 win7;; Microsoft Windows 7
418 win8;; Microsoft Windows 8/2012/2012r2
419 win10;; Microsoft Windows 10/2016/2019
420 win11;; Microsoft Windows 11/2022
421 l24;; Linux 2.4 Kernel
422 l26;; Linux 2.6 - 6.X Kernel
423 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
428 type
=> 'string', format
=> 'pve-qm-boot',
429 description
=> "Specify guest boot order. Use the 'order=' sub-property as usage with no"
430 ." key or 'legacy=' is deprecated.",
434 type
=> 'string', format
=> 'pve-qm-bootdisk',
435 description
=> "Enable booting from specified disk. Deprecated: Use 'boot: order=foo;bar' instead.",
436 pattern
=> '(ide|sata|scsi|virtio)\d+',
441 description
=> "The number of CPUs. Please use option -sockets instead.",
448 description
=> "The number of CPU sockets.",
455 description
=> "The number of cores per socket.",
462 description
=> "Enable/disable NUMA.",
468 description
=> "Enable/disable hugepages memory.",
469 enum
=> [qw(any 2 1024)],
475 description
=> "Use together with hugepages. If enabled, hugepages will not not be deleted"
476 ." after VM shutdown and can be used for subsequent starts.",
481 description
=> "Number of hotplugged vcpus.",
488 description
=> "Enable/disable ACPI.",
493 description
=> "Enable/disable communication with the QEMU Guest Agent and its properties.",
495 format
=> $agent_fmt,
500 description
=> "Enable/disable KVM hardware virtualization.",
506 description
=> "Enable/disable time drift fix.",
512 description
=> "Set the real time clock (RTC) to local time. This is enabled by default if"
513 ." the `ostype` indicates a Microsoft Windows OS.",
518 description
=> "Freeze CPU at startup (use 'c' monitor command to start execution).",
522 type
=> 'string', format
=> $vga_fmt,
523 description
=> "Configure the VGA hardware.",
524 verbose_description
=> "Configure the VGA Hardware. If you want to use high resolution"
525 ." modes (>= 1280x1024x16) you may need to increase the vga memory option. Since QEMU"
526 ." 2.9 the default VGA display type is 'std' for all OS types besides some Windows"
527 ." versions (XP and older) which use 'cirrus'. The 'qxl' option enables the SPICE"
528 ." display server. For win* OS you can select how many independent displays you want,"
529 ." Linux guests can add displays them self.\nYou can also run without any graphic card,"
530 ." using a serial device as terminal.",
534 type
=> 'string', format
=> 'pve-qm-watchdog',
535 description
=> "Create a virtual hardware watchdog device.",
536 verbose_description
=> "Create a virtual hardware watchdog device. Once enabled (by a guest"
537 ." action), the watchdog must be periodically polled by an agent inside the guest or"
538 ." else the watchdog will reset the guest (or execute the respective action specified)",
543 typetext
=> "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
544 description
=> "Set the initial date of the real time clock. Valid format for date are:"
545 ."'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
546 pattern
=> '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
549 startup
=> get_standard_option
('pve-startup-order'),
553 description
=> "Enable/disable Template.",
559 description
=> "Arbitrary arguments passed to kvm.",
560 verbose_description
=> <<EODESCR,
561 Arbitrary arguments passed to kvm, for example:
563 args: -no-reboot -smbios 'type=0,vendor=FOO'
565 NOTE: this option is for experts only.
572 description
=> "Enable/disable the USB tablet device.",
573 verbose_description
=> "Enable/disable the USB tablet device. This device is usually needed"
574 ." to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with"
575 ." normal VNC clients. If you're running lots of console-only guests on one host, you"
576 ." may consider disabling this to save some context switches. This is turned off by"
577 ." default if you use spice (`qm set <vmid> --vga qxl`).",
582 description
=> "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
586 migrate_downtime
=> {
589 description
=> "Set maximum tolerated downtime (in seconds) for migrations.",
595 type
=> 'string', format
=> 'pve-qm-ide',
596 typetext
=> '<volume>',
597 description
=> "This is an alias for option -ide2",
601 description
=> "Emulated CPU type.",
603 format
=> 'pve-vm-cpu-conf',
605 parent
=> get_standard_option
('pve-snapshot-name', {
607 description
=> "Parent snapshot name. This is used internally, and should not be modified.",
611 description
=> "Timestamp for snapshots.",
617 type
=> 'string', format
=> 'pve-volume-id',
618 description
=> "Reference to a volume which stores the VM state. This is used internally"
621 vmstatestorage
=> get_standard_option
('pve-storage-id', {
622 description
=> "Default storage for VM state volumes/files.",
625 runningmachine
=> get_standard_option
('pve-qemu-machine', {
626 description
=> "Specifies the QEMU machine type of the running vm. This is used internally"
630 description
=> "Specifies the QEMU '-cpu' parameter of the running vm. This is used"
631 ." internally for snapshots.",
634 pattern
=> $PVE::QemuServer
::CPUConfig
::qemu_cmdline_cpu_re
,
635 format_description
=> 'QEMU -cpu parameter'
637 machine
=> get_standard_option
('pve-qemu-machine'),
639 description
=> "Virtual processor architecture. Defaults to the host.",
642 enum
=> [qw(x86_64 aarch64)],
645 description
=> "Specify SMBIOS type 1 fields.",
646 type
=> 'string', format
=> 'pve-qm-smbios1',
653 description
=> "Sets the protection flag of the VM. This will disable the remove VM and"
654 ." remove disk operations.",
660 enum
=> [ qw(seabios ovmf) ],
661 description
=> "Select BIOS implementation.",
662 default => 'seabios',
666 pattern
=> '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
667 format_description
=> 'UUID',
668 description
=> "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0'"
669 ." to disable explicitly.",
670 verbose_description
=> "The VM generation ID (vmgenid) device exposes a 128-bit integer"
671 ." value identifier to the guest OS. This allows to notify the guest operating system"
672 ." when the virtual machine is executed with a different configuration (e.g. snapshot"
673 ." execution or creation from a template). The guest operating system notices the"
674 ." change, and is then able to react as appropriate by marking its copies of"
675 ." distributed databases as dirty, re-initializing its random number generator, etc.\n"
676 ."Note that auto-creation only works when done through API/CLI create or update methods"
677 .", but not when manually editing the config file.",
678 default => "1 (autogenerated)",
683 format
=> 'pve-volume-id',
685 description
=> "Script that will be executed during various steps in the vms lifetime.",
689 format
=> $ivshmem_fmt,
690 description
=> "Inter-VM shared memory. Useful for direct communication between VMs, or to"
696 format
=> $audio_fmt,
697 description
=> "Configure a audio device, useful in combination with QXL/Spice.",
700 spice_enhancements
=> {
702 format
=> $spice_enhancements_fmt,
703 description
=> "Configure additional enhancements for SPICE.",
707 type
=> 'string', format
=> 'pve-tag-list',
708 description
=> 'Tags of the VM. This is only meta information.',
714 description
=> "Configure a VirtIO-based Random Number Generator.",
719 format
=> $meta_info_fmt,
720 description
=> "Some (read-only) meta-information about this guest.",
724 type
=> 'string', format
=> 'pve-cpuset',
725 description
=> "List of host cores used to execute guest processes, for example: 0,5,8-11",
734 description
=> 'Specify a custom file containing all meta data passed to the VM via"
735 ." cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
736 format
=> 'pve-volume-id',
737 format_description
=> 'volume',
742 description
=> 'To pass a custom file containing all network data to the VM via cloud-init.',
743 format
=> 'pve-volume-id',
744 format_description
=> 'volume',
749 description
=> 'To pass a custom file containing all user data to the VM via cloud-init.',
750 format
=> 'pve-volume-id',
751 format_description
=> 'volume',
756 description
=> 'To pass a custom file containing all vendor data to the VM via cloud-init.',
757 format
=> 'pve-volume-id',
758 format_description
=> 'volume',
761 PVE
::JSONSchema
::register_format
('pve-qm-cicustom', $cicustom_fmt);
763 # any new option might need to be added to $cloudinitoptions in PVE::API2::Qemu
764 my $confdesc_cloudinit = {
768 description
=> 'Specifies the cloud-init configuration format. The default depends on the'
769 .' configured operating system type (`ostype`. We use the `nocloud` format for Linux,'
770 .' and `configdrive2` for windows.',
771 enum
=> ['configdrive2', 'nocloud', 'opennebula'],
776 description
=> "cloud-init: User name to change ssh keys and password for instead of the"
777 ." image's configured default user.",
782 description
=> 'cloud-init: Password to assign the user. Using this is generally not'
783 .' recommended. Use ssh keys instead. Also note that older cloud-init versions do not'
784 .' support hashed passwords.',
789 description
=> 'cloud-init: do an automatic package upgrade after the first boot.',
795 description
=> 'cloud-init: Specify custom files to replace the automatically generated'
797 format
=> 'pve-qm-cicustom',
802 description
=> 'cloud-init: Sets DNS search domains for a container. Create will'
803 .' automatically use the setting from the host if neither searchdomain nor nameserver'
808 type
=> 'string', format
=> 'address-list',
809 description
=> 'cloud-init: Sets DNS server IP address for a container. Create will'
810 .' automatically use the setting from the host if neither searchdomain nor nameserver'
816 format
=> 'urlencoded',
817 description
=> "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
821 # what about other qemu settings ?
823 #machine => 'string',
836 ##soundhw => 'string',
838 while (my ($k, $v) = each %$confdesc) {
839 PVE
::JSONSchema
::register_standard_option
("pve-qm-$k", $v);
843 my $MAX_SERIAL_PORTS = 4;
844 my $MAX_PARALLEL_PORTS = 3;
850 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
851 description
=> "CPUs accessing this NUMA node.",
852 format_description
=> "id[-id];...",
856 description
=> "Amount of memory this NUMA node provides.",
861 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
862 description
=> "Host NUMA nodes to use.",
863 format_description
=> "id[-id];...",
868 enum
=> [qw(preferred bind interleave)],
869 description
=> "NUMA allocation policy.",
873 PVE
::JSONSchema
::register_format
('pve-qm-numanode', $numa_fmt);
876 type
=> 'string', format
=> $numa_fmt,
877 description
=> "NUMA topology.",
879 PVE
::JSONSchema
::register_standard_option
("pve-qm-numanode", $numadesc);
881 for (my $i = 0; $i < $MAX_NUMA; $i++) {
882 $confdesc->{"numa$i"} = $numadesc;
885 my $nic_model_list = [
901 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
903 my $net_fmt_bridge_descr = <<__EOD__;
904 Bridge to attach the network device to. The Proxmox VE standard bridge
907 If you do not specify a bridge, we create a kvm user (NATed) network
908 device, which provides DHCP and DNS services. The following addresses
915 The DHCP server assign addresses to the guest starting from 10.0.2.15.
919 macaddr
=> get_standard_option
('mac-addr', {
920 description
=> "MAC address. That address must be unique withing your network. This is"
921 ." automatically generated if not specified.",
925 description
=> "Network Card Model. The 'virtio' model provides the best performance with"
926 ." very low CPU overhead. If your guest does not support this driver, it is usually"
927 ." best to use 'e1000'.",
928 enum
=> $nic_model_list,
931 (map { $_ => { keyAlias
=> 'model', alias
=> 'macaddr' }} @$nic_model_list),
932 bridge
=> get_standard_option
('pve-bridge-id', {
933 description
=> $net_fmt_bridge_descr,
938 minimum
=> 0, maximum
=> 64,
939 description
=> 'Number of packet queues to be used on the device.',
945 description
=> "Rate limit in mbps (megabytes per second) as floating point number.",
950 minimum
=> 1, maximum
=> 4094,
951 description
=> 'VLAN tag to apply to packets on this interface.',
956 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
957 description
=> 'VLAN trunks to pass through this interface.',
958 format_description
=> 'vlanid[;vlanid...]',
963 description
=> 'Whether this interface should be protected by the firewall.',
968 description
=> 'Whether this interface should be disconnected (like pulling the plug).',
973 minimum
=> 1, maximum
=> 65520,
974 description
=> "Force MTU, for VirtIO only. Set to '1' to use the bridge MTU",
981 type
=> 'string', format
=> $net_fmt,
982 description
=> "Specify network devices.",
985 PVE
::JSONSchema
::register_standard_option
("pve-qm-net", $netdesc);
990 format
=> 'pve-ipv4-config',
991 format_description
=> 'IPv4Format/CIDR',
992 description
=> 'IPv4 address in CIDR format.',
999 format_description
=> 'GatewayIPv4',
1000 description
=> 'Default gateway for IPv4 traffic.',
1006 format
=> 'pve-ipv6-config',
1007 format_description
=> 'IPv6Format/CIDR',
1008 description
=> 'IPv6 address in CIDR format.',
1015 format_description
=> 'GatewayIPv6',
1016 description
=> 'Default gateway for IPv6 traffic.',
1021 PVE
::JSONSchema
::register_format
('pve-qm-ipconfig', $ipconfig_fmt);
1022 my $ipconfigdesc = {
1024 type
=> 'string', format
=> 'pve-qm-ipconfig',
1025 description
=> <<'EODESCR',
1026 cloud-init: Specify IP addresses and gateways for the corresponding interface.
1028 IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
1030 The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit
1031 gateway should be provided.
1032 For IPv6 the special string 'auto' can be used to use stateless autoconfiguration. This requires
1033 cloud-init 19.4 or newer.
1035 If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using
1039 PVE
::JSONSchema
::register_standard_option
("pve-qm-ipconfig", $netdesc);
1041 for (my $i = 0; $i < $MAX_NETS; $i++) {
1042 $confdesc->{"net$i"} = $netdesc;
1043 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
1046 foreach my $key (keys %$confdesc_cloudinit) {
1047 $confdesc->{$key} = $confdesc_cloudinit->{$key};
1050 PVE
::JSONSchema
::register_format
('pve-cpuset', \
&pve_verify_cpuset
);
1051 sub pve_verify_cpuset
{
1052 my ($set_text, $noerr) = @_;
1054 my ($count, $members) = eval { PVE
::CpuSet
::parse_cpuset
($set_text) };
1058 die "unable to parse cpuset option\n";
1061 return PVE
::CpuSet-
>new($members)->short_string();
1064 PVE
::JSONSchema
::register_format
('pve-volume-id-or-qm-path', \
&verify_volume_id_or_qm_path
);
1065 sub verify_volume_id_or_qm_path
{
1066 my ($volid, $noerr) = @_;
1068 return $volid if $volid eq 'none' || $volid eq 'cdrom';
1070 return verify_volume_id_or_absolute_path
($volid, $noerr);
1073 PVE
::JSONSchema
::register_format
('pve-volume-id-or-absolute-path', \
&verify_volume_id_or_absolute_path
);
1074 sub verify_volume_id_or_absolute_path
{
1075 my ($volid, $noerr) = @_;
1077 return $volid if $volid =~ m
|^/|;
1079 $volid = eval { PVE
::JSONSchema
::check_format
('pve-volume-id', $volid, '') };
1090 pattern
=> '(/dev/.+|socket)',
1091 description
=> "Create a serial device inside the VM (n is 0 to 3)",
1092 verbose_description
=> <<EODESCR,
1093 Create a serial device inside the VM (n is 0 to 3), and pass through a
1094 host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1095 host side (use 'qm terminal' to open a terminal connection).
1097 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines -
1098 use with special care.
1100 CAUTION: Experimental! User reported problems with this option.
1107 pattern
=> '/dev/parport\d+|/dev/usb/lp\d+',
1108 description
=> "Map host parallel devices (n is 0 to 2).",
1109 verbose_description
=> <<EODESCR,
1110 Map host parallel devices (n is 0 to 2).
1112 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1113 machines - use with special care.
1115 CAUTION: Experimental! User reported problems with this option.
1119 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1120 $confdesc->{"parallel$i"} = $paralleldesc;
1123 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1124 $confdesc->{"serial$i"} = $serialdesc;
1127 for (my $i = 0; $i < $PVE::QemuServer
::PCI
::MAX_HOSTPCI_DEVICES
; $i++) {
1128 $confdesc->{"hostpci$i"} = $PVE::QemuServer
::PCI
::hostpcidesc
;
1131 for my $key (keys %{$PVE::QemuServer
::Drive
::drivedesc_hash
}) {
1132 $confdesc->{$key} = $PVE::QemuServer
::Drive
::drivedesc_hash-
>{$key};
1135 for (my $i = 0; $i < $PVE::QemuServer
::USB
::MAX_USB_DEVICES
; $i++) {
1136 $confdesc->{"usb$i"} = $PVE::QemuServer
::USB
::usbdesc
;
1144 description
=> "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)."
1145 . " Deprecated, use 'order=' instead.",
1146 pattern
=> '[acdn]{1,4}',
1147 format_description
=> "[acdn]{1,4}",
1149 # note: this is also the fallback if boot: is not given at all
1155 format
=> 'pve-qm-bootdev-list',
1156 format_description
=> "device[;device...]",
1157 description
=> <<EODESC,
1158 The guest will attempt to boot from devices in the order they appear here.
1160 Disks, optical drives and passed-through storage USB devices will be directly
1161 booted from, NICs will load PXE, and PCIe devices will either behave like disks
1162 (e.g. NVMe) or load an option ROM (e.g. RAID controller, hardware NIC).
1164 Note that only devices in this list will be marked as bootable and thus loaded
1165 by the guest firmware (BIOS/UEFI). If you require multiple disks for booting
1166 (e.g. software-raid), you need to specify all of them here.
1168 Overrides the deprecated 'legacy=[acdn]*' value when given.
1172 PVE
::JSONSchema
::register_format
('pve-qm-boot', $boot_fmt);
1174 PVE
::JSONSchema
::register_format
('pve-qm-bootdev', \
&verify_bootdev
);
1175 sub verify_bootdev
{
1176 my ($dev, $noerr) = @_;
1178 my $special = $dev =~ m/^efidisk/ || $dev =~ m/^tpmstate/;
1179 return $dev if PVE
::QemuServer
::Drive
::is_valid_drivename
($dev) && !$special;
1183 return 0 if $dev !~ m/^$base\d+$/;
1184 return 0 if !$confdesc->{$dev};
1188 return $dev if $check->("net");
1189 return $dev if $check->("usb");
1190 return $dev if $check->("hostpci");
1193 die "invalid boot device '$dev'\n";
1196 sub print_bootorder
{
1198 return "" if !@$devs;
1199 my $data = { order
=> join(';', @$devs) };
1200 return PVE
::JSONSchema
::print_property_string
($data, $boot_fmt);
1203 my $kvm_api_version = 0;
1206 return $kvm_api_version if $kvm_api_version;
1208 open my $fh, '<', '/dev/kvm' or return;
1210 # 0xae00 => KVM_GET_API_VERSION
1211 $kvm_api_version = ioctl($fh, 0xae00, 0);
1214 return $kvm_api_version;
1217 my $kvm_user_version = {};
1220 sub kvm_user_version
{
1223 $binary //= get_command_for_arch
(get_host_arch
()); # get the native arch by default
1224 my $st = stat($binary);
1226 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1227 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1228 $cachedmtime == $st->mtime;
1230 $kvm_user_version->{$binary} = 'unknown';
1231 $kvm_mtime->{$binary} = $st->mtime;
1235 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1236 $kvm_user_version->{$binary} = $2;
1240 eval { run_command
([$binary, '--version'], outfunc
=> $code); };
1243 return $kvm_user_version->{$binary};
1246 my sub extract_version
{
1247 my ($machine_type, $version) = @_;
1248 $version = kvm_user_version
() if !defined($version);
1249 return PVE
::QemuServer
::Machine
::extract_version
($machine_type, $version)
1252 sub kernel_has_vhost_net
{
1253 return -c
'/dev/vhost-net';
1258 return defined($confdesc->{$key});
1262 sub get_cdrom_path
{
1264 return $cdrom_path if defined($cdrom_path);
1266 $cdrom_path = first
{ -l
$_ } map { "/dev/cdrom$_" } ('', '1', '2');
1268 if (!defined($cdrom_path)) {
1269 log_warn
("no physical CD-ROM available, ignoring");
1277 my ($storecfg, $vmid, $cdrom) = @_;
1279 if ($cdrom eq 'cdrom') {
1280 return get_cdrom_path
();
1281 } elsif ($cdrom eq 'none') {
1283 } elsif ($cdrom =~ m
|^/|) {
1286 return PVE
::Storage
::path
($storecfg, $cdrom);
1290 # try to convert old style file names to volume IDs
1291 sub filename_to_volume_id
{
1292 my ($vmid, $file, $media) = @_;
1294 if (!($file eq 'none' || $file eq 'cdrom' ||
1295 $file =~ m
|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1297 return if $file =~ m
|/|;
1299 if ($media && $media eq 'cdrom') {
1300 $file = "local:iso/$file";
1302 $file = "local:$vmid/$file";
1309 sub verify_media_type
{
1310 my ($opt, $vtype, $media) = @_;
1315 if ($media eq 'disk') {
1317 } elsif ($media eq 'cdrom') {
1320 die "internal error";
1323 return if ($vtype eq $etype);
1325 raise_param_exc
({ $opt => "unexpected media type ($vtype != $etype)" });
1328 sub cleanup_drive_path
{
1329 my ($opt, $storecfg, $drive) = @_;
1331 # try to convert filesystem paths to volume IDs
1333 if (($drive->{file
} !~ m/^(cdrom|none)$/) &&
1334 ($drive->{file
} !~ m
|^/dev/.+|) &&
1335 ($drive->{file
} !~ m/^([^:]+):(.+)$/) &&
1336 ($drive->{file
} !~ m/^\d+$/)) {
1337 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($storecfg, $drive->{file
});
1338 raise_param_exc
({ $opt => "unable to associate path '$drive->{file}' to any storage"})
1340 $drive->{media
} = 'cdrom' if !$drive->{media
} && $vtype eq 'iso';
1341 verify_media_type
($opt, $vtype, $drive->{media
});
1342 $drive->{file
} = $volid;
1345 $drive->{media
} = 'cdrom' if !$drive->{media
} && $drive->{file
} =~ m/^(cdrom|none)$/;
1348 sub parse_hotplug_features
{
1353 return $res if $data eq '0';
1355 $data = $confdesc->{hotplug
}->{default} if $data eq '1';
1357 foreach my $feature (PVE
::Tools
::split_list
($data)) {
1358 if ($feature =~ m/^(network|disk|cpu|memory|usb|cloudinit)$/) {
1361 die "invalid hotplug feature '$feature'\n";
1367 PVE
::JSONSchema
::register_format
('pve-hotplug-features', \
&pve_verify_hotplug_features
);
1368 sub pve_verify_hotplug_features
{
1369 my ($value, $noerr) = @_;
1371 return $value if parse_hotplug_features
($value);
1375 die "unable to parse hotplug option\n";
1379 my($fh, $noerr) = @_;
1382 my $SG_GET_VERSION_NUM = 0x2282;
1384 my $versionbuf = "\x00" x
8;
1385 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1387 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
1390 my $version = unpack("I", $versionbuf);
1391 if ($version < 30000) {
1392 die "scsi generic interface too old\n" if !$noerr;
1396 my $buf = "\x00" x
36;
1397 my $sensebuf = "\x00" x
8;
1398 my $cmd = pack("C x3 C x1", 0x12, 36);
1400 # see /usr/include/scsi/sg.h
1401 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";
1404 $sg_io_hdr_t, ord('S'), -3, length($cmd), length($sensebuf), 0, length($buf), $buf, $cmd, $sensebuf, 6000
1407 $ret = ioctl($fh, $SG_IO, $packet);
1409 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
1413 my @res = unpack($sg_io_hdr_t, $packet);
1414 if ($res[17] || $res[18]) {
1415 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
1420 $res->@{qw(type removable vendor product revision)} = unpack("C C x6 A8 A16 A4", $buf);
1422 $res->{removable
} = $res->{removable
} & 128 ?
1 : 0;
1423 $res->{type
} &= 0x1F;
1431 my $fh = IO
::File-
>new("+<$path") || return;
1432 my $res = scsi_inquiry
($fh, 1);
1438 sub print_tabletdevice_full
{
1439 my ($conf, $arch) = @_;
1441 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1443 # we use uhci for old VMs because tablet driver was buggy in older qemu
1445 if ($q35 || $arch eq 'aarch64') {
1451 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1454 sub print_keyboarddevice_full
{
1455 my ($conf, $arch) = @_;
1457 return if $arch ne 'aarch64';
1459 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1462 my sub get_drive_id
{
1464 return "$drive->{interface}$drive->{index}";
1467 sub print_drivedevice_full
{
1468 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1473 my $drive_id = get_drive_id
($drive);
1474 if ($drive->{interface
} eq 'virtio') {
1475 my $pciaddr = print_pci_addr
("$drive_id", $bridges, $arch, $machine_type);
1476 $device = "virtio-blk-pci,drive=drive-$drive_id,id=${drive_id}${pciaddr}";
1477 $device .= ",iothread=iothread-$drive_id" if $drive->{iothread
};
1478 } elsif ($drive->{interface
} eq 'scsi') {
1480 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
1481 my $unit = $drive->{index} % $maxdev;
1482 my $devicetype = 'hd';
1484 if (drive_is_cdrom
($drive)) {
1487 if ($drive->{file
} =~ m
|^/|) {
1488 $path = $drive->{file
};
1489 if (my $info = path_is_scsi
($path)) {
1490 if ($info->{type
} == 0 && $drive->{scsiblock
}) {
1491 $devicetype = 'block';
1492 } elsif ($info->{type
} == 1) { # tape
1493 $devicetype = 'generic';
1497 $path = PVE
::Storage
::path
($storecfg, $drive->{file
});
1500 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1501 my $version = extract_version
($machine_type, kvm_user_version
());
1502 if ($path =~ m/^iscsi\:\/\
// &&
1503 !min_version
($version, 4, 1)) {
1504 $devicetype = 'generic';
1508 if (!$conf->{scsihw
} || $conf->{scsihw
} =~ m/^lsi/ || $conf->{scsihw
} eq 'pvscsi') {
1509 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
1511 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
1512 .",lun=$drive->{index}";
1514 $device .= ",drive=drive-$drive_id,id=$drive_id";
1516 if ($drive->{ssd
} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1517 $device .= ",rotation_rate=1";
1519 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1521 } elsif ($drive->{interface
} eq 'ide' || $drive->{interface
} eq 'sata') {
1522 my $maxdev = ($drive->{interface
} eq 'sata') ?
$PVE::QemuServer
::Drive
::MAX_SATA_DISKS
: 2;
1523 my $controller = int($drive->{index} / $maxdev);
1524 my $unit = $drive->{index} % $maxdev;
1526 # machine type q35 only supports unit=0 for IDE rather than 2 units. This wasn't handled
1527 # correctly before, so e.g. index=2 was mapped to controller=1,unit=0 rather than
1528 # controller=2,unit=0. Note that odd indices never worked, as they would be mapped to
1529 # unit=1, so to keep backwards compat for migration, it suffices to keep even ones as they
1530 # were before. Move odd ones up by 2 where they don't clash.
1531 if (PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf) && $drive->{interface
} eq 'ide') {
1532 $controller += 2 * ($unit % 2);
1536 my $devicetype = ($drive->{media
} && $drive->{media
} eq 'cdrom') ?
"cd" : "hd";
1538 $device = "ide-$devicetype";
1539 if ($drive->{interface
} eq 'ide') {
1540 $device .= ",bus=ide.$controller,unit=$unit";
1542 $device .= ",bus=ahci$controller.$unit";
1544 $device .= ",drive=drive-$drive_id,id=$drive_id";
1546 if ($devicetype eq 'hd') {
1547 if (my $model = $drive->{model
}) {
1548 $model = URI
::Escape
::uri_unescape
($model);
1549 $device .= ",model=$model";
1551 if ($drive->{ssd
}) {
1552 $device .= ",rotation_rate=1";
1555 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1556 } elsif ($drive->{interface
} eq 'usb') {
1558 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1560 die "unsupported interface type";
1563 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex
};
1565 if (my $serial = $drive->{serial
}) {
1566 $serial = URI
::Escape
::uri_unescape
($serial);
1567 $device .= ",serial=$serial";
1574 sub get_initiator_name
{
1577 my $fh = IO
::File-
>new('/etc/iscsi/initiatorname.iscsi') || return;
1578 while (defined(my $line = <$fh>)) {
1579 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1588 my sub storage_allows_io_uring_default
{
1589 my ($scfg, $cache_direct) = @_;
1591 # io_uring with cache mode writeback or writethrough on krbd will hang...
1592 return if $scfg && $scfg->{type
} eq 'rbd' && $scfg->{krbd
} && !$cache_direct;
1594 # io_uring with cache mode writeback or writethrough on LVM will hang, without cache only
1595 # sometimes, just plain disable...
1596 return if $scfg && $scfg->{type
} eq 'lvm';
1598 # io_uring causes problems when used with CIFS since kernel 5.15
1599 # Some discussion: https://www.spinics.net/lists/linux-cifs/msg26734.html
1600 return if $scfg && $scfg->{type
} eq 'cifs';
1605 my sub drive_uses_cache_direct
{
1606 my ($drive, $scfg) = @_;
1608 my $cache_direct = 0;
1610 if (my $cache = $drive->{cache
}) {
1611 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1612 } elsif (!drive_is_cdrom
($drive) && !($scfg && $scfg->{type
} eq 'btrfs' && !$scfg->{nocow
})) {
1616 return $cache_direct;
1619 sub print_drive_commandline_full
{
1620 my ($storecfg, $vmid, $drive, $pbs_name, $io_uring) = @_;
1623 my $volid = $drive->{file
};
1624 my $format = $drive->{format
};
1625 my $drive_id = get_drive_id
($drive);
1627 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1628 my $scfg = $storeid ? PVE
::Storage
::storage_config
($storecfg, $storeid) : undef;
1630 if (drive_is_cdrom
($drive)) {
1631 $path = get_iso_path
($storecfg, $vmid, $volid);
1632 die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
1635 $path = PVE
::Storage
::path
($storecfg, $volid);
1636 $format //= qemu_img_format
($scfg, $volname);
1643 my $is_rbd = $path =~ m/^rbd:/;
1646 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1647 foreach my $o (@qemu_drive_options) {
1648 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1651 # snapshot only accepts on|off
1652 if (defined($drive->{snapshot
})) {
1653 my $v = $drive->{snapshot
} ?
'on' : 'off';
1654 $opts .= ",snapshot=$v";
1657 if (defined($drive->{ro
})) { # ro maps to QEMUs `readonly`, which accepts `on` or `off` only
1658 $opts .= ",readonly=" . ($drive->{ro
} ?
'on' : 'off');
1661 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1662 my ($dir, $qmpname) = @$type;
1663 if (my $v = $drive->{"mbps$dir"}) {
1664 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1666 if (my $v = $drive->{"mbps${dir}_max"}) {
1667 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1669 if (my $v = $drive->{"bps${dir}_max_length"}) {
1670 $opts .= ",throttling.bps$qmpname-max-length=$v";
1672 if (my $v = $drive->{"iops${dir}"}) {
1673 $opts .= ",throttling.iops$qmpname=$v";
1675 if (my $v = $drive->{"iops${dir}_max"}) {
1676 $opts .= ",throttling.iops$qmpname-max=$v";
1678 if (my $v = $drive->{"iops${dir}_max_length"}) {
1679 $opts .= ",throttling.iops$qmpname-max-length=$v";
1684 $format = "rbd" if $is_rbd;
1685 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1687 $opts .= ",format=alloc-track,file.driver=$format";
1689 $opts .= ",format=$format";
1692 my $cache_direct = drive_uses_cache_direct
($drive, $scfg);
1694 $opts .= ",cache=none" if !$drive->{cache
} && $cache_direct;
1696 if (!$drive->{aio
}) {
1697 if ($io_uring && storage_allows_io_uring_default
($scfg, $cache_direct)) {
1698 # io_uring supports all cache modes
1699 $opts .= ",aio=io_uring";
1701 # aio native works only with O_DIRECT
1703 $opts .= ",aio=native";
1705 $opts .= ",aio=threads";
1710 if (!drive_is_cdrom
($drive)) {
1712 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
1713 $detectzeroes = 'off';
1714 } elsif ($drive->{discard
}) {
1715 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
1717 # This used to be our default with discard not being specified:
1718 $detectzeroes = 'on';
1721 # note: 'detect-zeroes' works per blockdev and we want it to persist
1722 # after the alloc-track is removed, so put it on 'file' directly
1723 my $dz_param = $pbs_name ?
"file.detect-zeroes" : "detect-zeroes";
1724 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
1728 $opts .= ",backing=$pbs_name";
1729 $opts .= ",auto-remove=on";
1732 # my $file_param = $pbs_name ? "file.file.filename" : "file";
1733 my $file_param = "file";
1735 # non-rbd drivers require the underlying file to be a seperate block
1736 # node, so add a second .file indirection
1737 $file_param .= ".file" if !$is_rbd;
1738 $file_param .= ".filename";
1740 my $pathinfo = $path ?
"$file_param=$path," : '';
1742 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1745 sub print_pbs_blockdev
{
1746 my ($pbs_conf, $pbs_name) = @_;
1747 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1748 $blockdev .= ",repository=$pbs_conf->{repository}";
1749 $blockdev .= ",namespace=$pbs_conf->{namespace}" if $pbs_conf->{namespace
};
1750 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1751 $blockdev .= ",archive=$pbs_conf->{archive}";
1752 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile
};
1756 sub print_netdevice_full
{
1757 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version) = @_;
1759 my $device = $net->{model
};
1760 if ($net->{model
} eq 'virtio') {
1761 $device = 'virtio-net-pci';
1764 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
1765 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
1766 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
1767 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1768 # and out of each queue plus one config interrupt and control vector queue
1769 my $vectors = $net->{queues
} * 2 + 2;
1770 $tmpstr .= ",vectors=$vectors,mq=on";
1771 if (min_version
($machine_version, 7, 1)) {
1772 $tmpstr .= ",packed=on";
1776 if (min_version
($machine_version, 7, 1) && $net->{model
} eq 'virtio'){
1777 $tmpstr .= ",rx_queue_size=1024,tx_queue_size=1024";
1780 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
1782 if (my $mtu = $net->{mtu
}) {
1783 if ($net->{model
} eq 'virtio' && $net->{bridge
}) {
1784 my $bridge_mtu = PVE
::Network
::read_bridge_mtu
($net->{bridge
});
1787 } elsif ($mtu < 576) {
1788 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1789 } elsif ($mtu > $bridge_mtu) {
1790 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1792 $tmpstr .= ",host_mtu=$mtu";
1794 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
1798 if ($use_old_bios_files) {
1800 if ($device eq 'virtio-net-pci') {
1801 $romfile = 'pxe-virtio.rom';
1802 } elsif ($device eq 'e1000') {
1803 $romfile = 'pxe-e1000.rom';
1804 } elsif ($device eq 'e1000e') {
1805 $romfile = 'pxe-e1000e.rom';
1806 } elsif ($device eq 'ne2k') {
1807 $romfile = 'pxe-ne2k_pci.rom';
1808 } elsif ($device eq 'pcnet') {
1809 $romfile = 'pxe-pcnet.rom';
1810 } elsif ($device eq 'rtl8139') {
1811 $romfile = 'pxe-rtl8139.rom';
1813 $tmpstr .= ",romfile=$romfile" if $romfile;
1819 sub print_netdev_full
{
1820 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
1823 if ($netid =~ m/^net(\d+)$/) {
1827 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
1829 my $ifname = "tap${vmid}i$i";
1831 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1832 die "interface name '$ifname' is too long (max 15 character)\n"
1833 if length($ifname) >= 16;
1835 my $vhostparam = '';
1836 if (is_native
($arch)) {
1837 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
1840 my $vmname = $conf->{name
} || "vm$vmid";
1843 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
1845 if ($net->{bridge
}) {
1846 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1847 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
1849 $netdev = "type=user,id=$netid,hostname=$vmname";
1852 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
1858 'cirrus' => 'cirrus-vga',
1860 'vmware' => 'vmware-svga',
1861 'virtio' => 'virtio-vga',
1862 'virtio-gl' => 'virtio-vga-gl',
1865 sub print_vga_device
{
1866 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
1868 my $type = $vga_map->{$vga->{type
}};
1869 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
1870 $type = 'virtio-gpu';
1872 my $vgamem_mb = $vga->{memory
};
1874 my $max_outputs = '';
1876 $type = $id ?
'qxl' : 'qxl-vga';
1878 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
1879 # set max outputs so linux can have up to 4 qxl displays with one device
1880 if (min_version
($machine_version, 4, 1)) {
1881 $max_outputs = ",max_outputs=4";
1886 die "no devicetype for $vga->{type}\n" if !$type;
1890 if ($vga->{type
} =~ /^virtio/) {
1891 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
1892 $memory = ",max_hostmem=$bytes";
1894 # from https://www.spice-space.org/multiple-monitors.html
1895 $memory = ",vgamem_mb=$vga->{memory}";
1896 my $ram = $vgamem_mb * 4;
1897 my $vram = $vgamem_mb * 2;
1898 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1900 $memory = ",vgamem_mb=$vga->{memory}";
1902 } elsif ($qxlnum && $id) {
1903 $memory = ",ram_size=67108864,vram_size=33554432";
1907 if ($type eq 'VGA' && windows_version
($conf->{ostype
})) {
1908 $edidoff=",edid=off" if (!defined($conf->{bios
}) || $conf->{bios
} ne 'ovmf');
1911 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1912 my $vgaid = "vga" . ($id // '');
1914 if ($q35 && $vgaid eq 'vga') {
1915 # the first display uses pcie.0 bus on q35 machines
1916 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
1918 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
1921 if ($vga->{type
} eq 'virtio-gl') {
1922 my $base = '/usr/lib/x86_64-linux-gnu/lib';
1923 die "missing libraries for '$vga->{type}' detected! Please install 'libgl1' and 'libegl1'\n"
1924 if !-e
"${base}EGL.so.1" || !-e
"${base}GL.so.1";
1926 die "no DRM render node detected (/dev/dri/renderD*), no GPU? - needed for '$vga->{type}' display\n"
1927 if !PVE
::Tools
::dir_glob_regex
('/dev/dri/', "renderD.*");
1930 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
1933 sub parse_number_sets
{
1936 foreach my $part (split(/;/, $set)) {
1937 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
1938 die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1;
1939 push @$res, [ $1, $2 ];
1941 die "invalid range: $part\n";
1950 my $res = parse_property_string
($numa_fmt, $data);
1951 $res->{cpus
} = parse_number_sets
($res->{cpus
}) if defined($res->{cpus
});
1952 $res->{hostnodes
} = parse_number_sets
($res->{hostnodes
}) if defined($res->{hostnodes
});
1956 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1958 my ($data, $disable_mac_autogen) = @_;
1960 my $res = eval { parse_property_string
($net_fmt, $data) };
1965 if (!defined($res->{macaddr
}) && !$disable_mac_autogen) {
1966 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1967 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1972 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1973 sub parse_ipconfig
{
1976 my $res = eval { parse_property_string
($ipconfig_fmt, $data) };
1982 if ($res->{gw
} && !$res->{ip
}) {
1983 warn 'gateway specified without specifying an IP address';
1986 if ($res->{gw6
} && !$res->{ip6
}) {
1987 warn 'IPv6 gateway specified without specifying an IPv6 address';
1990 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
1991 warn 'gateway specified together with DHCP';
1994 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
1996 warn "IPv6 gateway specified together with $res->{ip6} address";
2000 if (!$res->{ip
} && !$res->{ip6
}) {
2001 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
2010 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
2013 sub add_random_macs
{
2014 my ($settings) = @_;
2016 foreach my $opt (keys %$settings) {
2017 next if $opt !~ m/^net(\d+)$/;
2018 my $net = parse_net
($settings->{$opt});
2020 $settings->{$opt} = print_net
($net);
2024 sub vm_is_volid_owner
{
2025 my ($storecfg, $vmid, $volid) = @_;
2027 if ($volid !~ m
|^/|) {
2029 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
2030 if ($owner && ($owner == $vmid)) {
2038 sub vmconfig_register_unused_drive
{
2039 my ($storecfg, $vmid, $conf, $drive) = @_;
2041 if (drive_is_cloudinit
($drive)) {
2042 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
2044 delete $conf->{cloudinit
};
2045 } elsif (!drive_is_cdrom
($drive)) {
2046 my $volid = $drive->{file
};
2047 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
2048 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
2053 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
2057 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
2058 format_description
=> 'UUID',
2059 description
=> "Set SMBIOS1 UUID.",
2064 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2065 format_description
=> 'Base64 encoded string',
2066 description
=> "Set SMBIOS1 version.",
2071 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2072 format_description
=> 'Base64 encoded string',
2073 description
=> "Set SMBIOS1 serial number.",
2078 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2079 format_description
=> 'Base64 encoded string',
2080 description
=> "Set SMBIOS1 manufacturer.",
2085 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2086 format_description
=> 'Base64 encoded string',
2087 description
=> "Set SMBIOS1 product ID.",
2092 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2093 format_description
=> 'Base64 encoded string',
2094 description
=> "Set SMBIOS1 SKU string.",
2099 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2100 format_description
=> 'Base64 encoded string',
2101 description
=> "Set SMBIOS1 family string.",
2106 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
2114 my $res = eval { parse_property_string
($smbios1_fmt, $data) };
2121 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2124 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2126 sub parse_watchdog
{
2131 my $res = eval { parse_property_string
($watchdog_fmt, $value) };
2136 sub parse_guest_agent
{
2139 return {} if !defined($conf->{agent
});
2141 my $res = eval { parse_property_string
($agent_fmt, $conf->{agent
}) };
2144 # if the agent is disabled ignore the other potentially set properties
2145 return {} if !$res->{enabled
};
2150 my ($conf, $key) = @_;
2151 return undef if !defined($conf->{agent
});
2153 my $agent = parse_guest_agent
($conf);
2154 return $agent->{$key};
2160 return {} if !$value;
2161 my $res = eval { parse_property_string
($vga_fmt, $value) };
2171 my $res = eval { parse_property_string
($rng_fmt, $value) };
2176 sub parse_meta_info
{
2181 my $res = eval { parse_property_string
($meta_info_fmt, $value) };
2186 sub new_meta_info_string
{
2187 my () = @_; # for now do not allow to override any value
2189 return PVE
::JSONSchema
::print_property_string
(
2191 'creation-qemu' => kvm_user_version
(),
2192 ctime
=> "". int(time()),
2198 sub qemu_created_version_fixups
{
2199 my ($conf, $forcemachine, $kvmver) = @_;
2201 my $meta = parse_meta_info
($conf->{meta
}) // {};
2202 my $forced_vers = PVE
::QemuServer
::Machine
::extract_version
($forcemachine);
2204 # check if we need to apply some handling for VMs that always use the latest machine version but
2205 # had a machine version transition happen that affected HW such that, e.g., an OS config change
2206 # would be required (we do not want to pin machine version for non-windows OS type)
2208 (!defined($conf->{machine
}) || $conf->{machine
} =~ m/^(?:pc|q35|virt)$/) # non-versioned machine
2209 && (!defined($meta->{'creation-qemu'}) || !min_version
($meta->{'creation-qemu'}, 6, 1)) # created before 6.1
2210 && (!$forced_vers || min_version
($forced_vers, 6, 1)) # handle snapshot-rollback/migrations
2211 && min_version
($kvmver, 6, 1) # only need to apply the change since 6.1
2213 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
2214 if ($q35 && $conf->{ostype
} && $conf->{ostype
} eq 'l26') {
2215 # this changed to default-on in Q 6.1 for q35 machines, it will mess with PCI slot view
2216 # and thus with the predictable interface naming of systemd
2217 return ['-global', 'ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off'];
2223 # add JSON properties for create and set function
2224 sub json_config_properties
{
2225 my ($prop, $with_disk_alloc) = @_;
2227 my $skip_json_config_opts = {
2231 runningmachine
=> 1,
2236 foreach my $opt (keys %$confdesc) {
2237 next if $skip_json_config_opts->{$opt};
2239 if ($with_disk_alloc && is_valid_drivename
($opt)) {
2240 $prop->{$opt} = $PVE::QemuServer
::Drive
::drivedesc_hash_with_alloc-
>{$opt};
2242 $prop->{$opt} = $confdesc->{$opt};
2249 # Properties that we can read from an OVF file
2250 sub json_ovf_properties
{
2253 for my $device (PVE
::QemuServer
::Drive
::valid_drive_names
()) {
2254 $prop->{$device} = {
2256 format
=> 'pve-volume-id-or-absolute-path',
2257 description
=> "Disk image that gets imported to $device",
2264 description
=> "The number of CPU cores.",
2269 description
=> "Amount of RAM for the VM in MB.",
2274 description
=> "Name of the VM.",
2281 # return copy of $confdesc_cloudinit to generate documentation
2282 sub cloudinit_config_properties
{
2284 return dclone
($confdesc_cloudinit);
2287 sub cloudinit_pending_properties
{
2289 map { $_ => 1 } keys $confdesc_cloudinit->%*,
2292 $p->{"net$_"} = 1 for 0..($MAX_NETS-1);
2297 my ($key, $value) = @_;
2299 die "unknown setting '$key'\n" if !$confdesc->{$key};
2301 my $type = $confdesc->{$key}->{type
};
2303 if (!defined($value)) {
2304 die "got undefined value\n";
2307 if ($value =~ m/[\n\r]/) {
2308 die "property contains a line feed\n";
2311 if ($type eq 'boolean') {
2312 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2313 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2314 die "type check ('boolean') failed - got '$value'\n";
2315 } elsif ($type eq 'integer') {
2316 return int($1) if $value =~ m/^(\d+)$/;
2317 die "type check ('integer') failed - got '$value'\n";
2318 } elsif ($type eq 'number') {
2319 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2320 die "type check ('number') failed - got '$value'\n";
2321 } elsif ($type eq 'string') {
2322 if (my $fmt = $confdesc->{$key}->{format
}) {
2323 PVE
::JSONSchema
::check_format
($fmt, $value);
2326 $value =~ s/^\"(.*)\"$/$1/;
2329 die "internal error"
2334 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
2336 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2338 if (!$skiplock && !PVE
::QemuConfig-
>has_lock($conf, 'suspended')) {
2339 PVE
::QemuConfig-
>check_lock($conf);
2342 if ($conf->{template
}) {
2343 # check if any base image is still used by a linked clone
2344 PVE
::QemuConfig-
>foreach_volume_full($conf, { include_unused
=> 1 }, sub {
2345 my ($ds, $drive) = @_;
2346 return if drive_is_cdrom
($drive);
2348 my $volid = $drive->{file
};
2349 return if !$volid || $volid =~ m
|^/|;
2351 die "base volume '$volid' is still in use by linked cloned\n"
2352 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2358 my $remove_owned_drive = sub {
2359 my ($ds, $drive) = @_;
2360 return if drive_is_cdrom
($drive, 1);
2362 my $volid = $drive->{file
};
2363 return if !$volid || $volid =~ m
|^/|;
2364 return if $volids->{$volid};
2366 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2367 return if !$path || !$owner || ($owner != $vmid);
2369 $volids->{$volid} = 1;
2370 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2371 warn "Could not remove disk '$volid', check manually: $@" if $@;
2374 # only remove disks owned by this VM (referenced in the config)
2375 my $include_opts = {
2376 include_unused
=> 1,
2377 extra_keys
=> ['vmstate'],
2379 PVE
::QemuConfig-
>foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2381 for my $snap (values %{$conf->{snapshots
}}) {
2382 next if !defined($snap->{vmstate
});
2383 my $drive = PVE
::QemuConfig-
>parse_volume('vmstate', $snap->{vmstate
}, 1);
2384 next if !defined($drive);
2385 $remove_owned_drive->('vmstate', $drive);
2388 PVE
::QemuConfig-
>foreach_volume_full($conf->{pending
}, $include_opts, $remove_owned_drive);
2390 if ($purge_unreferenced) { # also remove unreferenced disk
2391 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid, undef, 'images');
2392 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2393 my ($volid, $sid, $volname, $d) = @_;
2394 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2399 if (defined $replacement_conf) {
2400 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2402 PVE
::QemuConfig-
>destroy_config($vmid);
2406 sub parse_vm_config
{
2407 my ($filename, $raw, $strict) = @_;
2409 return if !defined($raw);
2412 digest
=> Digest
::SHA
::sha1_hex
($raw),
2418 my $handle_error = sub {
2428 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2429 || die "got strange filename '$filename'";
2435 my $finish_description = sub {
2436 if (defined($descr)) {
2438 $conf->{description
} = $descr;
2444 my @lines = split(/\n/, $raw);
2445 foreach my $line (@lines) {
2446 next if $line =~ m/^\s*$/;
2448 if ($line =~ m/^\[PENDING\]\s*$/i) {
2449 $section = 'pending';
2450 $finish_description->();
2451 $conf = $res->{$section} = {};
2453 } elsif ($line =~ m/^\[special:cloudinit\]\s*$/i) {
2454 $section = 'cloudinit';
2455 $finish_description->();
2456 $conf = $res->{$section} = {};
2459 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2461 $finish_description->();
2462 $conf = $res->{snapshots
}->{$section} = {};
2466 if ($line =~ m/^\#(.*)$/) {
2467 $descr = '' if !defined($descr);
2468 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2472 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2473 $descr = '' if !defined($descr);
2474 $descr .= PVE
::Tools
::decode_text
($2);
2475 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2476 $conf->{snapstate
} = $1;
2477 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2480 $conf->{$key} = $value;
2481 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2483 if ($section eq 'pending') {
2484 $conf->{delete} = $value; # we parse this later
2486 $handle_error->("vm $vmid - property 'delete' is only allowed in [PENDING]\n");
2488 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2491 if ($section eq 'cloudinit') {
2492 # ignore validation only used for informative purpose
2493 $conf->{$key} = $value;
2496 eval { $value = check_type
($key, $value); };
2498 $handle_error->("vm $vmid - unable to parse value of '$key' - $@");
2500 $key = 'ide2' if $key eq 'cdrom';
2501 my $fmt = $confdesc->{$key}->{format
};
2502 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2503 my $v = parse_drive
($key, $value);
2504 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2505 $v->{file
} = $volid;
2506 $value = print_drive
($v);
2508 $handle_error->("vm $vmid - unable to parse value of '$key'\n");
2513 $conf->{$key} = $value;
2516 $handle_error->("vm $vmid - unable to parse config: $line\n");
2520 $finish_description->();
2521 delete $res->{snapstate
}; # just to be sure
2526 sub write_vm_config
{
2527 my ($filename, $conf) = @_;
2529 delete $conf->{snapstate
}; # just to be sure
2531 if ($conf->{cdrom
}) {
2532 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2533 $conf->{ide2
} = $conf->{cdrom
};
2534 delete $conf->{cdrom
};
2537 # we do not use 'smp' any longer
2538 if ($conf->{sockets
}) {
2539 delete $conf->{smp
};
2540 } elsif ($conf->{smp
}) {
2541 $conf->{sockets
} = $conf->{smp
};
2542 delete $conf->{cores
};
2543 delete $conf->{smp
};
2546 my $used_volids = {};
2548 my $cleanup_config = sub {
2549 my ($cref, $pending, $snapname) = @_;
2551 foreach my $key (keys %$cref) {
2552 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2553 $key eq 'snapstate' || $key eq 'pending' || $key eq 'cloudinit';
2554 my $value = $cref->{$key};
2555 if ($key eq 'delete') {
2556 die "propertry 'delete' is only allowed in [PENDING]\n"
2558 # fixme: check syntax?
2561 eval { $value = check_type
($key, $value); };
2562 die "unable to parse value of '$key' - $@" if $@;
2564 $cref->{$key} = $value;
2566 if (!$snapname && is_valid_drivename
($key)) {
2567 my $drive = parse_drive
($key, $value);
2568 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2573 &$cleanup_config($conf);
2575 &$cleanup_config($conf->{pending
}, 1);
2577 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2578 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2579 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2582 # remove 'unusedX' settings if we re-add a volume
2583 foreach my $key (keys %$conf) {
2584 my $value = $conf->{$key};
2585 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2586 delete $conf->{$key};
2590 my $generate_raw_config = sub {
2591 my ($conf, $pending) = @_;
2595 # add description as comment to top of file
2596 if (defined(my $descr = $conf->{description
})) {
2598 foreach my $cl (split(/\n/, $descr)) {
2599 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2602 $raw .= "#\n" if $pending;
2606 foreach my $key (sort keys %$conf) {
2607 next if $key =~ /^(digest|description|pending|cloudinit|snapshots)$/;
2608 $raw .= "$key: $conf->{$key}\n";
2613 my $raw = &$generate_raw_config($conf);
2615 if (scalar(keys %{$conf->{pending
}})){
2616 $raw .= "\n[PENDING]\n";
2617 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2620 if (scalar(keys %{$conf->{cloudinit
}}) && PVE
::QemuConfig-
>has_cloudinit($conf)){
2621 $raw .= "\n[special:cloudinit]\n";
2622 $raw .= &$generate_raw_config($conf->{cloudinit
});
2625 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2626 $raw .= "\n[$snapname]\n";
2627 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2637 # we use static defaults from our JSON schema configuration
2638 foreach my $key (keys %$confdesc) {
2639 if (defined(my $default = $confdesc->{$key}->{default})) {
2640 $res->{$key} = $default;
2648 my $vmlist = PVE
::Cluster
::get_vmlist
();
2650 return $res if !$vmlist || !$vmlist->{ids
};
2651 my $ids = $vmlist->{ids
};
2652 my $nodename = nodename
();
2654 foreach my $vmid (keys %$ids) {
2655 my $d = $ids->{$vmid};
2656 next if !$d->{node
} || $d->{node
} ne $nodename;
2657 next if !$d->{type
} || $d->{type
} ne 'qemu';
2658 $res->{$vmid}->{exists} = 1;
2663 # test if VM uses local resources (to prevent migration)
2664 sub check_local_resources
{
2665 my ($conf, $noerr) = @_;
2668 my $mapped_res = [];
2670 my $nodelist = PVE
::Cluster
::get_nodelist
();
2671 my $pci_map = PVE
::Mapping
::PCI
::config
();
2672 my $usb_map = PVE
::Mapping
::USB
::config
();
2674 my $missing_mappings_by_node = { map { $_ => [] } @$nodelist };
2676 my $add_missing_mapping = sub {
2677 my ($type, $key, $id) = @_;
2678 for my $node (@$nodelist) {
2680 if ($type eq 'pci') {
2681 $entry = PVE
::Mapping
::PCI
::get_node_mapping
($pci_map, $id, $node);
2682 } elsif ($type eq 'usb') {
2683 $entry = PVE
::Mapping
::USB
::get_node_mapping
($usb_map, $id, $node);
2685 if (!scalar($entry->@*)) {
2686 push @{$missing_mappings_by_node->{$node}}, $key;
2691 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2692 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2694 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2696 foreach my $k (keys %$conf) {
2697 if ($k =~ m/^usb/) {
2698 my $entry = parse_property_string
('pve-qm-usb', $conf->{$k});
2699 next if $entry->{host
} =~ m/^spice$/i;
2700 if ($entry->{mapping
}) {
2701 $add_missing_mapping->('usb', $k, $entry->{mapping
});
2702 push @$mapped_res, $k;
2705 if ($k =~ m/^hostpci/) {
2706 my $entry = parse_property_string
('pve-qm-hostpci', $conf->{$k});
2707 if ($entry->{mapping
}) {
2708 $add_missing_mapping->('pci', $k, $entry->{mapping
});
2709 push @$mapped_res, $k;
2712 # sockets are safe: they will recreated be on the target side post-migrate
2713 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2714 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2717 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2719 return wantarray ?
(\
@loc_res, $mapped_res, $missing_mappings_by_node) : \
@loc_res;
2722 # check if used storages are available on all nodes (use by migrate)
2723 sub check_storage_availability
{
2724 my ($storecfg, $conf, $node) = @_;
2726 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2727 my ($ds, $drive) = @_;
2729 my $volid = $drive->{file
};
2732 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2735 # check if storage is available on both nodes
2736 my $scfg = PVE
::Storage
::storage_check_enabled
($storecfg, $sid);
2737 PVE
::Storage
::storage_check_enabled
($storecfg, $sid, $node);
2739 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $volid);
2741 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2742 if !$scfg->{content
}->{$vtype};
2746 # list nodes where all VM images are available (used by has_feature API)
2748 my ($conf, $storecfg) = @_;
2750 my $nodelist = PVE
::Cluster
::get_nodelist
();
2751 my $nodehash = { map { $_ => 1 } @$nodelist };
2752 my $nodename = nodename
();
2754 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2755 my ($ds, $drive) = @_;
2757 my $volid = $drive->{file
};
2760 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2762 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2763 if ($scfg->{disable
}) {
2765 } elsif (my $avail = $scfg->{nodes
}) {
2766 foreach my $node (keys %$nodehash) {
2767 delete $nodehash->{$node} if !$avail->{$node};
2769 } elsif (!$scfg->{shared
}) {
2770 foreach my $node (keys %$nodehash) {
2771 delete $nodehash->{$node} if $node ne $nodename
2780 sub check_local_storage_availability
{
2781 my ($conf, $storecfg) = @_;
2783 my $nodelist = PVE
::Cluster
::get_nodelist
();
2784 my $nodehash = { map { $_ => {} } @$nodelist };
2786 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2787 my ($ds, $drive) = @_;
2789 my $volid = $drive->{file
};
2792 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2794 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2796 if ($scfg->{disable
}) {
2797 foreach my $node (keys %$nodehash) {
2798 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2800 } elsif (my $avail = $scfg->{nodes
}) {
2801 foreach my $node (keys %$nodehash) {
2802 if (!$avail->{$node}) {
2803 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2810 foreach my $node (values %$nodehash) {
2811 if (my $unavail = $node->{unavailable_storages
}) {
2812 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2819 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2821 my ($vmid, $nocheck, $node) = @_;
2823 # $nocheck is set when called during a migration, in which case the config
2824 # file might still or already reside on the *other* node
2825 # - because rename has already happened, and current node is source
2826 # - because rename hasn't happened yet, and current node is target
2827 # - because rename has happened, current node is target, but hasn't yet
2829 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2830 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2835 my $vzlist = config_list
();
2837 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2839 while (defined(my $de = $fd->read)) {
2840 next if $de !~ m/^(\d+)\.pid$/;
2842 next if !defined($vzlist->{$vmid});
2843 if (my $pid = check_running
($vmid)) {
2844 $vzlist->{$vmid}->{pid
} = $pid;
2851 our $vmstatus_return_properties = {
2852 vmid
=> get_standard_option
('pve-vmid'),
2854 description
=> "QEMU process status.",
2856 enum
=> ['stopped', 'running'],
2859 description
=> "Maximum memory in bytes.",
2862 renderer
=> 'bytes',
2865 description
=> "Root disk size in bytes.",
2868 renderer
=> 'bytes',
2871 description
=> "VM name.",
2876 description
=> "VM run state from the 'query-status' QMP monitor command.",
2881 description
=> "PID of running qemu process.",
2886 description
=> "Uptime.",
2889 renderer
=> 'duration',
2892 description
=> "Maximum usable CPUs.",
2897 description
=> "The current config lock, if any.",
2902 description
=> "The current configured tags, if any",
2906 'running-machine' => {
2907 description
=> "The currently running machine type (if running).",
2912 description
=> "The currently running QEMU version (if running).",
2918 my $last_proc_pid_stat;
2920 # get VM status information
2921 # This must be fast and should not block ($full == false)
2922 # We only query KVM using QMP if $full == true (this can be slow)
2924 my ($opt_vmid, $full) = @_;
2928 my $storecfg = PVE
::Storage
::config
();
2930 my $list = vzlist
();
2931 my $defaults = load_defaults
();
2933 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
(1);
2935 my $cpucount = $cpuinfo->{cpus
} || 1;
2937 foreach my $vmid (keys %$list) {
2938 next if $opt_vmid && ($vmid ne $opt_vmid);
2940 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2942 my $d = { vmid
=> int($vmid) };
2943 $d->{pid
} = int($list->{$vmid}->{pid
}) if $list->{$vmid}->{pid
};
2945 # fixme: better status?
2946 $d->{status
} = $list->{$vmid}->{pid
} ?
'running' : 'stopped';
2948 my $size = PVE
::QemuServer
::Drive
::bootdisk_size
($storecfg, $conf);
2949 if (defined($size)) {
2950 $d->{disk
} = 0; # no info available
2951 $d->{maxdisk
} = $size;
2957 $d->{cpus
} = ($conf->{sockets
} || $defaults->{sockets
})
2958 * ($conf->{cores
} || $defaults->{cores
});
2959 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
2960 $d->{cpus
} = $conf->{vcpus
} if $conf->{vcpus
};
2962 $d->{name
} = $conf->{name
} || "VM $vmid";
2963 $d->{maxmem
} = $conf->{memory
} ?
$conf->{memory
}*(1024*1024)
2964 : $defaults->{memory
}*(1024*1024);
2966 if ($conf->{balloon
}) {
2967 $d->{balloon_min
} = $conf->{balloon
}*(1024*1024);
2968 $d->{shares
} = defined($conf->{shares
}) ?
$conf->{shares
}
2969 : $defaults->{shares
};
2980 $d->{diskwrite
} = 0;
2982 $d->{template
} = 1 if PVE
::QemuConfig-
>is_template($conf);
2984 $d->{serial
} = 1 if conf_has_serial
($conf);
2985 $d->{lock} = $conf->{lock} if $conf->{lock};
2986 $d->{tags
} = $conf->{tags
} if defined($conf->{tags
});
2991 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
2992 foreach my $dev (keys %$netdev) {
2993 next if $dev !~ m/^tap([1-9]\d*)i/;
2995 my $d = $res->{$vmid};
2998 $d->{netout
} += $netdev->{$dev}->{receive
};
2999 $d->{netin
} += $netdev->{$dev}->{transmit
};
3002 $d->{nics
}->{$dev}->{netout
} = int($netdev->{$dev}->{receive
});
3003 $d->{nics
}->{$dev}->{netin
} = int($netdev->{$dev}->{transmit
});
3008 my $ctime = gettimeofday
;
3010 foreach my $vmid (keys %$list) {
3012 my $d = $res->{$vmid};
3013 my $pid = $d->{pid
};
3016 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
3017 next if !$pstat; # not running
3019 my $used = $pstat->{utime} + $pstat->{stime
};
3021 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
3023 if ($pstat->{vsize
}) {
3024 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
3027 my $old = $last_proc_pid_stat->{$pid};
3029 $last_proc_pid_stat->{$pid} = {
3037 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
3039 if ($dtime > 1000) {
3040 my $dutime = $used - $old->{used
};
3042 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
3043 $last_proc_pid_stat->{$pid} = {
3049 $d->{cpu
} = $old->{cpu
};
3053 return $res if !$full;
3055 my $qmpclient = PVE
::QMPClient-
>new();
3057 my $ballooncb = sub {
3058 my ($vmid, $resp) = @_;
3060 my $info = $resp->{'return'};
3061 return if !$info->{max_mem
};
3063 my $d = $res->{$vmid};
3065 # use memory assigned to VM
3066 $d->{maxmem
} = $info->{max_mem
};
3067 $d->{balloon
} = $info->{actual
};
3069 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
3070 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
3071 $d->{freemem
} = $info->{free_mem
};
3074 $d->{ballooninfo
} = $info;
3077 my $blockstatscb = sub {
3078 my ($vmid, $resp) = @_;
3079 my $data = $resp->{'return'} || [];
3080 my $totalrdbytes = 0;
3081 my $totalwrbytes = 0;
3083 for my $blockstat (@$data) {
3084 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
3085 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
3087 $blockstat->{device
} =~ s/drive-//;
3088 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
3090 $res->{$vmid}->{diskread
} = $totalrdbytes;
3091 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
3094 my $machinecb = sub {
3095 my ($vmid, $resp) = @_;
3096 my $data = $resp->{'return'} || [];
3098 $res->{$vmid}->{'running-machine'} =
3099 PVE
::QemuServer
::Machine
::current_from_query_machines
($data);
3102 my $versioncb = sub {
3103 my ($vmid, $resp) = @_;
3104 my $data = $resp->{'return'} // {};
3105 my $version = 'unknown';
3107 if (my $v = $data->{qemu
}) {
3108 $version = $v->{major
} . "." . $v->{minor
} . "." . $v->{micro
};
3111 $res->{$vmid}->{'running-qemu'} = $version;
3114 my $statuscb = sub {
3115 my ($vmid, $resp) = @_;
3117 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
3118 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
3119 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
3120 # this fails if ballon driver is not loaded, so this must be
3121 # the last commnand (following command are aborted if this fails).
3122 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
3124 my $status = 'unknown';
3125 if (!defined($status = $resp->{'return'}->{status
})) {
3126 warn "unable to get VM status\n";
3130 $res->{$vmid}->{qmpstatus
} = $resp->{'return'}->{status
};
3133 foreach my $vmid (keys %$list) {
3134 next if $opt_vmid && ($vmid ne $opt_vmid);
3135 next if !$res->{$vmid}->{pid
}; # not running
3136 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
3139 $qmpclient->queue_execute(undef, 2);
3141 foreach my $vmid (keys %$list) {
3142 next if $opt_vmid && ($vmid ne $opt_vmid);
3143 next if !$res->{$vmid}->{pid
}; #not running
3145 # we can't use the $qmpclient since it might have already aborted on
3146 # 'query-balloon', but this might also fail for older versions...
3147 my $qemu_support = eval { mon_cmd
($vmid, "query-proxmox-support") };
3148 $res->{$vmid}->{'proxmox-support'} = $qemu_support // {};
3151 foreach my $vmid (keys %$list) {
3152 next if $opt_vmid && ($vmid ne $opt_vmid);
3153 $res->{$vmid}->{qmpstatus
} = $res->{$vmid}->{status
} if !$res->{$vmid}->{qmpstatus
};
3159 sub conf_has_serial
{
3162 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3163 if ($conf->{"serial$i"}) {
3171 sub conf_has_audio
{
3172 my ($conf, $id) = @_;
3175 my $audio = $conf->{"audio$id"};
3176 return if !defined($audio);
3178 my $audioproperties = parse_property_string
($audio_fmt, $audio);
3179 my $audiodriver = $audioproperties->{driver
} // 'spice';
3182 dev
=> $audioproperties->{device
},
3183 dev_id
=> "audiodev$id",
3184 backend
=> $audiodriver,
3185 backend_id
=> "$audiodriver-backend${id}",
3190 my ($audio, $audiopciaddr, $machine_version) = @_;
3194 my $id = $audio->{dev_id
};
3196 if (min_version
($machine_version, 4, 2)) {
3197 $audiodev = ",audiodev=$audio->{backend_id}";
3200 if ($audio->{dev
} eq 'AC97') {
3201 push @$devs, '-device', "AC97,id=${id}${audiopciaddr}$audiodev";
3202 } elsif ($audio->{dev
} =~ /intel\-hda$/) {
3203 push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
3204 push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0$audiodev";
3205 push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1$audiodev";
3207 die "unkown audio device '$audio->{dev}', implement me!";
3210 push @$devs, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
3218 socket => "/var/run/qemu-server/$vmid.swtpm",
3219 pid
=> "/var/run/qemu-server/$vmid.swtpm.pid",
3223 sub add_tpm_device
{
3224 my ($vmid, $devices, $conf) = @_;
3226 return if !$conf->{tpmstate0
};
3228 my $paths = get_tpm_paths
($vmid);
3230 push @$devices, "-chardev", "socket,id=tpmchar,path=$paths->{socket}";
3231 push @$devices, "-tpmdev", "emulator,id=tpmdev,chardev=tpmchar";
3232 push @$devices, "-device", "tpm-tis,tpmdev=tpmdev";
3236 my ($storecfg, $vmid, $tpmdrive, $migration) = @_;
3238 return if !$tpmdrive;
3241 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
3242 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
3244 $state = PVE
::Storage
::map_volume
($storecfg, $tpm->{file
});
3246 $state = $tpm->{file
};
3249 my $paths = get_tpm_paths
($vmid);
3251 # during migration, we will get state from remote
3254 # run swtpm_setup to create a new TPM state if it doesn't exist yet
3261 "--create-platform-cert",
3264 "/etc/swtpm_setup.conf", # do not use XDG configs
3266 "0", # force creation as root, error if not possible
3267 "--not-overwrite", # ignore existing state, do not modify
3270 push @$setup_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3271 # TPM 2.0 supports ECC crypto, use if possible
3272 push @$setup_cmd, "--ecc" if $tpm->{version
} eq 'v2.0';
3274 run_command
($setup_cmd, outfunc
=> sub {
3275 print "swtpm_setup: $1\n";
3279 # Used to distinguish different invocations in the log.
3280 my $log_prefix = "[id=" . int(time()) . "] ";
3282 my $emulator_cmd = [
3286 "backend-uri=file://$state,mode=0600",
3288 "type=unixio,path=$paths->{socket},mode=0600",
3290 "file=$paths->{pid}",
3291 "--terminate", # terminate on QEMU disconnect
3294 "file=/run/qemu-server/$vmid-swtpm.log,level=1,prefix=$log_prefix",
3296 push @$emulator_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3297 run_command
($emulator_cmd, outfunc
=> sub { print $1; });
3299 my $tries = 100; # swtpm may take a bit to start before daemonizing, wait up to 5s for pid
3300 while (! -e
$paths->{pid
}) {
3301 die "failed to start swtpm: pid file '$paths->{pid}' wasn't created.\n" if --$tries == 0;
3305 # return untainted PID of swtpm daemon so it can be killed on error
3306 file_read_firstline
($paths->{pid
}) =~ m/(\d+)/;
3310 sub vga_conf_has_spice
{
3313 my $vgaconf = parse_vga
($vga);
3314 my $vgatype = $vgaconf->{type
};
3315 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
3322 return get_host_arch
() eq $arch;
3327 return $conf->{arch
} // get_host_arch
();
3330 my $default_machines = {
3335 sub get_installed_machine_version
{
3336 my ($kvmversion) = @_;
3337 $kvmversion = kvm_user_version
() if !defined($kvmversion);
3338 $kvmversion =~ m/^(\d+\.\d+)/;
3342 sub windows_get_pinned_machine_version
{
3343 my ($machine, $base_version, $kvmversion) = @_;
3345 my $pin_version = $base_version;
3346 if (!defined($base_version) ||
3347 !PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($base_version, $kvmversion)
3349 $pin_version = get_installed_machine_version
($kvmversion);
3351 if (!$machine || $machine eq 'pc') {
3352 $machine = "pc-i440fx-$pin_version";
3353 } elsif ($machine eq 'q35') {
3354 $machine = "pc-q35-$pin_version";
3355 } elsif ($machine eq 'virt') {
3356 $machine = "virt-$pin_version";
3358 warn "unknown machine type '$machine', not touching that!\n";
3364 sub get_vm_machine
{
3365 my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
3367 my $machine = $forcemachine || $conf->{machine
};
3369 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
3370 $kvmversion //= kvm_user_version
();
3371 # we must pin Windows VMs without a specific version to 5.1, as 5.2 fixed a bug in ACPI
3372 # layout which confuses windows quite a bit and may result in various regressions..
3373 # see: https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg08484.html
3374 if (windows_version
($conf->{ostype
})) {
3375 $machine = windows_get_pinned_machine_version
($machine, '5.1', $kvmversion);
3378 $machine ||= $default_machines->{$arch};
3379 if ($add_pve_version) {
3380 my $pvever = PVE
::QemuServer
::Machine
::get_pve_version
($kvmversion);
3381 $machine .= "+pve$pvever";
3385 if ($add_pve_version && $machine !~ m/\+pve\d+?(?:\.pxe)?$/) {
3386 my $is_pxe = $machine =~ m/^(.*?)\.pxe$/;
3387 $machine = $1 if $is_pxe;
3389 # for version-pinned machines that do not include a pve-version (e.g.
3390 # pc-q35-4.1), we assume 0 to keep them stable in case we bump
3391 $machine .= '+pve0';
3393 $machine .= '.pxe' if $is_pxe;
3399 sub get_ovmf_files
($$$) {
3400 my ($arch, $efidisk, $smm) = @_;
3402 my $types = $OVMF->{$arch}
3403 or die "no OVMF images known for architecture '$arch'\n";
3405 my $type = 'default';
3406 if ($arch ne "aarch64" && defined($efidisk->{efitype
}) && $efidisk->{efitype
} eq '4m') {
3407 $type = $smm ?
"4m" : "4m-no-smm";
3408 $type .= '-ms' if $efidisk->{'pre-enrolled-keys'};
3411 my ($ovmf_code, $ovmf_vars) = $types->{$type}->@*;
3412 die "EFI base image '$ovmf_code' not found\n" if ! -f
$ovmf_code;
3413 die "EFI vars image '$ovmf_vars' not found\n" if ! -f
$ovmf_vars;
3415 return ($ovmf_code, $ovmf_vars);
3419 aarch64
=> '/usr/bin/qemu-system-aarch64',
3420 x86_64
=> '/usr/bin/qemu-system-x86_64',
3422 sub get_command_for_arch
($) {
3424 return '/usr/bin/kvm' if is_native
($arch);
3426 my $cmd = $Arch2Qemu->{$arch}
3427 or die "don't know how to emulate architecture '$arch'\n";
3431 # To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
3432 # to use in a QEMU command line (-cpu element), first array_intersect the result
3433 # of query_supported_ with query_understood_. This is necessary because:
3435 # a) query_understood_ returns flags the host cannot use and
3436 # b) query_supported_ (rather the QMP call) doesn't actually return CPU
3437 # flags, but CPU settings - with most of them being flags. Those settings
3438 # (and some flags, curiously) cannot be specified as a "-cpu" argument.
3440 # query_supported_ needs to start up to 2 temporary VMs and is therefore rather
3441 # expensive. If you need the value returned from this, you can get it much
3442 # cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags-$accel') with
3443 # $accel being 'kvm' or 'tcg'.
3445 # pvestatd calls this function on startup and whenever the QEMU/KVM version
3446 # changes, automatically populating pmxcfs.
3448 # Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
3449 # since kvm and tcg machines support different flags
3451 sub query_supported_cpu_flags
{
3454 $arch //= get_host_arch
();
3455 my $default_machine = $default_machines->{$arch};
3459 # FIXME: Once this is merged, the code below should work for ARM as well:
3460 # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
3461 die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
3464 my $kvm_supported = defined(kvm_version
());
3465 my $qemu_cmd = get_command_for_arch
($arch);
3467 my $pidfile = PVE
::QemuServer
::Helpers
::pidfile_name
($fakevmid);
3469 # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
3470 my $query_supported_run_qemu = sub {
3476 '-machine', $default_machine,
3478 '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
3479 '-mon', 'chardev=qmp,mode=control',
3480 '-pidfile', $pidfile,
3485 push @$cmd, '-accel', 'tcg';
3488 my $rc = run_command
($cmd, noerr
=> 1, quiet
=> 0);
3489 die "QEMU flag querying VM exited with code " . $rc if $rc;
3492 my $cmd_result = mon_cmd
(
3494 'query-cpu-model-expansion',
3496 model
=> { name
=> 'host' }
3499 my $props = $cmd_result->{model
}->{props
};
3500 foreach my $prop (keys %$props) {
3501 next if $props->{$prop} ne '1';
3502 # QEMU returns some flags multiple times, with '_', '.' or '-'
3503 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
3504 # We only keep those with underscores, to match /proc/cpuinfo
3505 $prop =~ s/\.|-/_/g;
3506 $flags->{$prop} = 1;
3511 # force stop with 10 sec timeout and 'nocheck', always stop, even if QMP failed
3512 vm_stop
(undef, $fakevmid, 1, 1, 10, 0, 1);
3516 return [ sort keys %$flags ];
3519 # We need to query QEMU twice, since KVM and TCG have different supported flags
3520 PVE
::QemuConfig-
>lock_config($fakevmid, sub {
3521 $flags->{tcg
} = eval { $query_supported_run_qemu->(0) };
3522 warn "warning: failed querying supported tcg flags: $@\n" if $@;
3524 if ($kvm_supported) {
3525 $flags->{kvm
} = eval { $query_supported_run_qemu->(1) };
3526 warn "warning: failed querying supported kvm flags: $@\n" if $@;
3533 # Understood CPU flags are written to a file at 'pve-qemu' compile time
3534 my $understood_cpu_flag_dir = "/usr/share/kvm";
3535 sub query_understood_cpu_flags
{
3536 my $arch = get_host_arch
();
3537 my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
3539 die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
3542 my $raw = file_get_contents
($filepath);
3543 $raw =~ s/^\s+|\s+$//g;
3544 my @flags = split(/\s+/, $raw);
3549 # Since commit 277d33454f77ec1d1e0bc04e37621e4dd2424b67 in pve-qemu, smm is not off by default
3550 # anymore. But smm=off seems to be required when using SeaBIOS and serial display.
3551 my sub should_disable_smm
{
3552 my ($conf, $vga, $machine) = @_;
3554 return if $machine =~ m/^virt/; # there is no smm flag that could be disabled
3556 return (!defined($conf->{bios
}) || $conf->{bios
} eq 'seabios') &&
3557 $vga->{type
} && $vga->{type
} =~ m/^(serial\d+|none)$/;
3560 my sub print_ovmf_drive_commandlines
{
3561 my ($conf, $storecfg, $vmid, $arch, $q35, $version_guard) = @_;
3563 my $d = $conf->{efidisk0
} ? parse_drive
('efidisk0', $conf->{efidisk0
}) : undef;
3565 my ($ovmf_code, $ovmf_vars) = get_ovmf_files
($arch, $d, $q35);
3567 my $var_drive_str = "if=pflash,unit=1,id=drive-efidisk0";
3569 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($d->{file
}, 1);
3570 my ($path, $format) = $d->@{'file', 'format'};
3572 $path = PVE
::Storage
::path
($storecfg, $d->{file
});
3573 if (!defined($format)) {
3574 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
3575 $format = qemu_img_format
($scfg, $volname);
3577 } elsif (!defined($format)) {
3578 die "efidisk format must be specified\n";
3580 # SPI flash does lots of read-modify-write OPs, without writeback this gets really slow #3329
3581 if ($path =~ m/^rbd:/) {
3582 $var_drive_str .= ',cache=writeback';
3583 $path .= ':rbd_cache_policy=writeback'; # avoid write-around, we *need* to cache writes too
3585 $var_drive_str .= ",format=$format,file=$path";
3587 $var_drive_str .= ",size=" . (-s
$ovmf_vars) if $format eq 'raw' && $version_guard->(4, 1, 2);
3588 $var_drive_str .= ',readonly=on' if drive_is_read_only
($conf, $d);
3590 log_warn
("no efidisk configured! Using temporary efivars disk.");
3591 my $path = "/tmp/$vmid-ovmf.fd";
3592 PVE
::Tools
::file_copy
($ovmf_vars, $path, -s
$ovmf_vars);
3593 $var_drive_str .= ",format=raw,file=$path";
3594 $var_drive_str .= ",size=" . (-s
$ovmf_vars) if $version_guard->(4, 1, 2);
3597 return ("if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code", $var_drive_str);
3600 sub config_to_command
{
3601 my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
3604 my ($globalFlags, $machineFlags, $rtcFlags) = ([], [], []);
3607 my $ostype = $conf->{ostype
};
3608 my $winversion = windows_version
($ostype);
3609 my $kvm = $conf->{kvm
};
3610 my $nodename = nodename
();
3612 my $arch = get_vm_arch
($conf);
3613 my $kvm_binary = get_command_for_arch
($arch);
3614 my $kvmver = kvm_user_version
($kvm_binary);
3616 if (!$kvmver || $kvmver !~ m/^(\d+)\.(\d+)/ || $1 < 3) {
3617 $kvmver //= "undefined";
3618 die "Detected old QEMU binary ('$kvmver', at least 3.0 is required)\n";
3621 my $add_pve_version = min_version
($kvmver, 4, 1);
3623 my $machine_type = get_vm_machine
($conf, $forcemachine, $arch, $add_pve_version);
3624 my $machine_version = extract_version
($machine_type, $kvmver);
3625 $kvm //= 1 if is_native
($arch);
3627 $machine_version =~ m/(\d+)\.(\d+)/;
3628 my ($machine_major, $machine_minor) = ($1, $2);
3630 if ($kvmver =~ m/^\d+\.\d+\.(\d+)/ && $1 >= 90) {
3631 warn "warning: Installed QEMU version ($kvmver) is a release candidate, ignoring version checks\n";
3632 } elsif (!min_version
($kvmver, $machine_major, $machine_minor)) {
3633 die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type',"
3634 ." please upgrade node '$nodename'\n"
3635 } elsif (!PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($machine_version, $kvmver)) {
3636 my $max_pve_version = PVE
::QemuServer
::Machine
::get_pve_version
($machine_version);
3637 die "Installed qemu-server (max feature level for $machine_major.$machine_minor is"
3638 ." pve$max_pve_version) is too old to run machine type '$machine_type', please upgrade"
3639 ." node '$nodename'\n";
3642 # if a specific +pve version is required for a feature, use $version_guard
3643 # instead of min_version to allow machines to be run with the minimum
3645 my $required_pve_version = 0;
3646 my $version_guard = sub {
3647 my ($major, $minor, $pve) = @_;
3648 return 0 if !min_version
($machine_version, $major, $minor, $pve);
3649 my $max_pve = PVE
::QemuServer
::Machine
::get_pve_version
("$major.$minor");
3650 return 1 if min_version
($machine_version, $major, $minor, $max_pve+1);
3651 $required_pve_version = $pve if $pve && $pve > $required_pve_version;
3655 if ($kvm && !defined kvm_version
()) {
3656 die "KVM virtualisation configured, but not available. Either disable in VM configuration"
3657 ." or enable in BIOS.\n";
3660 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3661 my $hotplug_features = parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
3662 my $use_old_bios_files = undef;
3663 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3666 if ($conf->{affinity
}) {
3667 push @$cmd, '/usr/bin/taskset', '--cpu-list', '--all-tasks', $conf->{affinity
};
3670 push @$cmd, $kvm_binary;
3672 push @$cmd, '-id', $vmid;
3674 my $vmname = $conf->{name
} || "vm$vmid";
3676 push @$cmd, '-name', "$vmname,debug-threads=on";
3678 push @$cmd, '-no-shutdown';
3682 my $qmpsocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid);
3683 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server=on,wait=off";
3684 push @$cmd, '-mon', "chardev=qmp,mode=control";
3686 if (min_version
($machine_version, 2, 12)) {
3687 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3688 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3691 push @$cmd, '-pidfile' , PVE
::QemuServer
::Helpers
::pidfile_name
($vmid);
3693 push @$cmd, '-daemonize';
3695 if ($conf->{smbios1
}) {
3696 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
3697 if ($smbios_conf->{base64
}) {
3698 # Do not pass base64 flag to qemu
3699 delete $smbios_conf->{base64
};
3700 my $smbios_string = "";
3701 foreach my $key (keys %$smbios_conf) {
3703 if ($key eq "uuid") {
3704 $value = $smbios_conf->{uuid
}
3706 $value = decode_base64
($smbios_conf->{$key});
3708 # qemu accepts any binary data, only commas need escaping by double comma
3710 $smbios_string .= "," . $key . "=" . $value if $value;
3712 push @$cmd, '-smbios', "type=1" . $smbios_string;
3714 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3718 if ($conf->{bios
} && $conf->{bios
} eq 'ovmf') {
3719 my ($code_drive_str, $var_drive_str) =
3720 print_ovmf_drive_commandlines
($conf, $storecfg, $vmid, $arch, $q35, $version_guard);
3721 push $cmd->@*, '-drive', $code_drive_str;
3722 push $cmd->@*, '-drive', $var_drive_str;
3725 if ($q35) { # tell QEMU to load q35 config early
3726 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3727 if (min_version
($machine_version, 4, 0)) {
3728 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3730 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3734 if (defined(my $fixups = qemu_created_version_fixups
($conf, $forcemachine, $kvmver))) {
3735 push @$cmd, $fixups->@*;
3738 if ($conf->{vmgenid
}) {
3739 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid
};
3742 # add usb controllers
3743 my @usbcontrollers = PVE
::QemuServer
::USB
::get_usb_controllers
(
3744 $conf, $bridges, $arch, $machine_type, $machine_version);
3745 push @$devices, @usbcontrollers if @usbcontrollers;
3746 my $vga = parse_vga
($conf->{vga
});
3748 my $qxlnum = vga_conf_has_spice
($conf->{vga
});
3749 $vga->{type
} = 'qxl' if $qxlnum;
3751 if (!$vga->{type
}) {
3752 if ($arch eq 'aarch64') {
3753 $vga->{type
} = 'virtio';
3754 } elsif (min_version
($machine_version, 2, 9)) {
3755 $vga->{type
} = (!$winversion || $winversion >= 6) ?
'std' : 'cirrus';
3757 $vga->{type
} = ($winversion >= 6) ?
'std' : 'cirrus';
3761 # enable absolute mouse coordinates (needed by vnc)
3762 my $tablet = $conf->{tablet
};
3763 if (!defined($tablet)) {
3764 $tablet = $defaults->{tablet
};
3765 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3766 $tablet = 0 if $vga->{type
} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3770 push @$devices, '-device', print_tabletdevice_full
($conf, $arch) if $tablet;
3771 my $kbd = print_keyboarddevice_full
($conf, $arch);
3772 push @$devices, '-device', $kbd if defined($kbd);
3775 my $bootorder = device_bootorder
($conf);
3777 # host pci device passthrough
3778 my ($kvm_off, $gpu_passthrough, $legacy_igd, $pci_devices) = PVE
::QemuServer
::PCI
::print_hostpci_devices
(
3779 $vmid, $conf, $devices, $vga, $winversion, $bridges, $arch, $machine_type, $bootorder);
3782 my $usb_dev_features = {};
3783 $usb_dev_features->{spice_usb3
} = 1 if min_version
($machine_version, 4, 0);
3785 my @usbdevices = PVE
::QemuServer
::USB
::get_usb_devices
(
3786 $conf, $usb_dev_features, $bootorder, $machine_version);
3787 push @$devices, @usbdevices if @usbdevices;
3790 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3791 my $path = $conf->{"serial$i"} or next;
3792 if ($path eq 'socket') {
3793 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3794 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
3795 # On aarch64, serial0 is the UART device. QEMU only allows
3796 # connecting UART devices via the '-serial' command line, as
3797 # the device has a fixed slot on the hardware...
3798 if ($arch eq 'aarch64' && $i == 0) {
3799 push @$devices, '-serial', "chardev:serial$i";
3801 push @$devices, '-device', "isa-serial,chardev=serial$i";
3804 die "no such serial device\n" if ! -c
$path;
3805 push @$devices, '-chardev', "serial,id=serial$i,path=$path";
3806 push @$devices, '-device', "isa-serial,chardev=serial$i";
3811 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3812 if (my $path = $conf->{"parallel$i"}) {
3813 die "no such parallel device\n" if ! -c
$path;
3814 my $devtype = $path =~ m!^/dev/usb/lp! ?
'serial' : 'parallel';
3815 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3816 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3820 if (min_version
($machine_version, 4, 0) && (my $audio = conf_has_audio
($conf))) {
3821 my $audiopciaddr = print_pci_addr
("audio0", $bridges, $arch, $machine_type);
3822 my $audio_devs = audio_devs
($audio, $audiopciaddr, $machine_version);
3823 push @$devices, @$audio_devs;
3826 add_tpm_device
($vmid, $devices, $conf);
3829 $sockets = $conf->{smp
} if $conf->{smp
}; # old style - no longer iused
3830 $sockets = $conf->{sockets
} if $conf->{sockets
};
3832 my $cores = $conf->{cores
} || 1;
3834 my $maxcpus = $sockets * $cores;
3836 my $vcpus = $conf->{vcpus
} ?
$conf->{vcpus
} : $maxcpus;
3838 my $allowed_vcpus = $cpuinfo->{cpus
};
3840 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n" if ($allowed_vcpus < $maxcpus);
3842 if ($hotplug_features->{cpu
} && min_version
($machine_version, 2, 7)) {
3843 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3844 for (my $i = 2; $i <= $vcpus; $i++) {
3845 my $cpustr = print_cpu_device
($conf,$i);
3846 push @$cmd, '-device', $cpustr;
3851 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3853 push @$cmd, '-nodefaults';
3855 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3857 push $machineFlags->@*, 'acpi=off' if defined($conf->{acpi
}) && $conf->{acpi
} == 0;
3859 push @$cmd, '-no-reboot' if defined($conf->{reboot
}) && $conf->{reboot
} == 0;
3861 if ($vga->{type
} && $vga->{type
} !~ m/^serial\d+$/ && $vga->{type
} ne 'none'){
3862 push @$devices, '-device', print_vga_device
(
3863 $conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
3865 push @$cmd, '-display', 'egl-headless,gl=core' if $vga->{type
} eq 'virtio-gl'; # VIRGL
3867 my $socket = PVE
::QemuServer
::Helpers
::vnc_socket
($vmid);
3868 push @$cmd, '-vnc', "unix:$socket,password=on";
3870 push @$cmd, '-vga', 'none' if $vga->{type
} eq 'none';
3871 push @$cmd, '-nographic';
3875 my $tdf = defined($conf->{tdf
}) ?
$conf->{tdf
} : $defaults->{tdf
};
3876 my $useLocaltime = $conf->{localtime};
3878 if ($winversion >= 5) { # windows
3879 $useLocaltime = 1 if !defined($conf->{localtime});
3881 # use time drift fix when acpi is enabled
3882 if (!(defined($conf->{acpi
}) && $conf->{acpi
} == 0)) {
3883 $tdf = 1 if !defined($conf->{tdf
});
3887 if ($winversion >= 6) {
3888 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3889 push @$machineFlags, 'hpet=off';
3892 push @$rtcFlags, 'driftfix=slew' if $tdf;
3894 if ($conf->{startdate
} && $conf->{startdate
} ne 'now') {
3895 push @$rtcFlags, "base=$conf->{startdate}";
3896 } elsif ($useLocaltime) {
3897 push @$rtcFlags, 'base=localtime';
3901 push @$cmd, '-cpu', $forcecpu;
3903 push @$cmd, get_cpu_options
($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
3906 PVE
::QemuServer
::Memory
::config
(
3907 $conf, $vmid, $sockets, $cores, $defaults, $hotplug_features->{memory
}, $cmd);
3909 push @$cmd, '-S' if $conf->{freeze
};
3911 push @$cmd, '-k', $conf->{keyboard
} if defined($conf->{keyboard
});
3913 my $guest_agent = parse_guest_agent
($conf);
3915 if ($guest_agent->{enabled
}) {
3916 my $qgasocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid, 1);
3917 push @$devices, '-chardev', "socket,path=$qgasocket,server=on,wait=off,id=qga0";
3919 if (!$guest_agent->{type
} || $guest_agent->{type
} eq 'virtio') {
3920 my $pciaddr = print_pci_addr
("qga0", $bridges, $arch, $machine_type);
3921 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3922 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3923 } elsif ($guest_agent->{type
} eq 'isa') {
3924 push @$devices, '-device', "isa-serial,chardev=qga0";
3928 my $rng = $conf->{rng0
} ? parse_rng
($conf->{rng0
}) : undef;
3929 if ($rng && $version_guard->(4, 1, 2)) {
3930 check_rng_source
($rng->{source
});
3932 my $max_bytes = $rng->{max_bytes
} // $rng_fmt->{max_bytes
}->{default};
3933 my $period = $rng->{period
} // $rng_fmt->{period
}->{default};
3934 my $limiter_str = "";
3936 $limiter_str = ",max-bytes=$max_bytes,period=$period";
3939 my $rng_addr = print_pci_addr
("rng0", $bridges, $arch, $machine_type);
3940 push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
3941 push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
3946 if ($qxlnum || $vga->{type
} =~ /^virtio/) {
3949 for (my $i = 1; $i < $qxlnum; $i++){
3950 push @$devices, '-device', print_vga_device
(
3951 $conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
3954 # assume other OS works like Linux
3955 my ($ram, $vram) = ("134217728", "67108864");
3956 if ($vga->{memory
}) {
3957 $ram = PVE
::Tools
::convert_size
($qxlnum*4*$vga->{memory
}, 'mb' => 'b');
3958 $vram = PVE
::Tools
::convert_size
($qxlnum*2*$vga->{memory
}, 'mb' => 'b');
3960 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3961 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3965 my $pciaddr = print_pci_addr
("spice", $bridges, $arch, $machine_type);
3967 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
3968 my @nodeaddrs = PVE
::Tools
::getaddrinfo_all
('localhost', family
=> $pfamily);
3969 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3971 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3972 push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
3973 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3975 my $localhost = PVE
::Network
::addr_to_ip
($nodeaddrs[0]->{addr
});
3976 $spice_port = PVE
::Tools
::next_spice_port
($pfamily, $localhost);
3978 my $spice_enhancement_str = $conf->{spice_enhancements
} // '';
3979 my $spice_enhancement = parse_property_string
($spice_enhancements_fmt, $spice_enhancement_str);
3980 if ($spice_enhancement->{foldersharing
}) {
3981 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3982 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3985 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3986 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}"
3987 if $spice_enhancement->{videostreaming
};
3989 push @$devices, '-spice', "$spice_opts";
3992 # enable balloon by default, unless explicitly disabled
3993 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
3994 my $pciaddr = print_pci_addr
("balloon0", $bridges, $arch, $machine_type);
3995 my $ballooncmd = "virtio-balloon-pci,id=balloon0$pciaddr";
3996 $ballooncmd .= ",free-page-reporting=on" if min_version
($machine_version, 6, 2);
3997 push @$devices, '-device', $ballooncmd;
4000 if ($conf->{watchdog
}) {
4001 my $wdopts = parse_watchdog
($conf->{watchdog
});
4002 my $pciaddr = print_pci_addr
("watchdog", $bridges, $arch, $machine_type);
4003 my $watchdog = $wdopts->{model
} || 'i6300esb';
4004 push @$devices, '-device', "$watchdog$pciaddr";
4005 push @$devices, '-watchdog-action', $wdopts->{action
} if $wdopts->{action
};
4009 my $scsicontroller = {};
4010 my $ahcicontroller = {};
4011 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : $defaults->{scsihw
};
4013 # Add iscsi initiator name if available
4014 if (my $initiator = get_initiator_name
()) {
4015 push @$devices, '-iscsi', "initiator-name=$initiator";
4018 PVE
::QemuConfig-
>foreach_volume($conf, sub {
4019 my ($ds, $drive) = @_;
4021 if (PVE
::Storage
::parse_volume_id
($drive->{file
}, 1)) {
4022 check_volume_storage_type
($storecfg, $drive->{file
});
4023 push @$vollist, $drive->{file
};
4026 # ignore efidisk here, already added in bios/fw handling code above
4027 return if $drive->{interface
} eq 'efidisk';
4029 return if $drive->{interface
} eq 'tpmstate';
4031 $use_virtio = 1 if $ds =~ m/^virtio/;
4033 $drive->{bootindex
} = $bootorder->{$ds} if $bootorder->{$ds};
4035 if ($drive->{interface
} eq 'virtio'){
4036 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread
};
4039 if ($drive->{interface
} eq 'scsi') {
4041 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
4043 die "scsi$drive->{index}: machine version 4.1~pve2 or higher is required to use more than 14 SCSI disks\n"
4044 if $drive->{index} > 13 && !&$version_guard(4, 1, 2);
4046 my $pciaddr = print_pci_addr
("$controller_prefix$controller", $bridges, $arch, $machine_type);
4047 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ?
"virtio-scsi-pci" : $scsihw;
4050 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{iothread
}){
4051 $iothread .= ",iothread=iothread-$controller_prefix$controller";
4052 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
4053 } elsif ($drive->{iothread
}) {
4055 "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n"
4060 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{queues
}){
4061 $queues = ",num_queues=$drive->{queues}";
4064 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues"
4065 if !$scsicontroller->{$controller};
4066 $scsicontroller->{$controller}=1;
4069 if ($drive->{interface
} eq 'sata') {
4070 my $controller = int($drive->{index} / $PVE::QemuServer
::Drive
::MAX_SATA_DISKS
);
4071 my $pciaddr = print_pci_addr
("ahci$controller", $bridges, $arch, $machine_type);
4072 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr"
4073 if !$ahcicontroller->{$controller};
4074 $ahcicontroller->{$controller}=1;
4077 my $pbs_conf = $pbs_backing->{$ds};
4078 my $pbs_name = undef;
4080 $pbs_name = "drive-$ds-pbs";
4081 push @$devices, '-blockdev', print_pbs_blockdev
($pbs_conf, $pbs_name);
4084 my $drive_cmd = print_drive_commandline_full
(
4085 $storecfg, $vmid, $drive, $pbs_name, min_version
($kvmver, 6, 0));
4087 # extra protection for templates, but SATA and IDE don't support it..
4088 $drive_cmd .= ',readonly=on' if drive_is_read_only
($conf, $drive);
4090 push @$devices, '-drive',$drive_cmd;
4091 push @$devices, '-device', print_drivedevice_full
(
4092 $storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
4095 for (my $i = 0; $i < $MAX_NETS; $i++) {
4096 my $netname = "net$i";
4098 next if !$conf->{$netname};
4099 my $d = parse_net
($conf->{$netname});
4101 # save the MAC addr here (could be auto-gen. in some odd setups) for FDB registering later?
4103 $use_virtio = 1 if $d->{model
} eq 'virtio';
4105 $d->{bootindex
} = $bootorder->{$netname} if $bootorder->{$netname};
4107 my $netdevfull = print_netdev_full
($vmid, $conf, $arch, $d, $netname);
4108 push @$devices, '-netdev', $netdevfull;
4110 my $netdevicefull = print_netdevice_full
(
4111 $vmid, $conf, $d, $netname, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version);
4113 push @$devices, '-device', $netdevicefull;
4116 if ($conf->{ivshmem
}) {
4117 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
4121 $bus = print_pcie_addr
("ivshmem");
4123 $bus = print_pci_addr
("ivshmem", $bridges, $arch, $machine_type);
4126 my $ivshmem_name = $ivshmem->{name
} // $vmid;
4127 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
4129 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
4130 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path"
4131 .",size=$ivshmem->{size}M";
4134 # pci.4 is nested in pci.1
4135 $bridges->{1} = 1 if $bridges->{4};
4137 if (!$q35) { # add pci bridges
4138 if (min_version
($machine_version, 2, 3)) {
4142 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
4145 for my $k (sort {$b cmp $a} keys %$bridges) {
4146 next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
4149 if ($k == 2 && $legacy_igd) {
4152 my $pciaddr = print_pci_addr
("pci.$k_name", undef, $arch, $machine_type);
4153 my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
4155 if ($q35) { # add after -readconfig pve-q35.cfg
4156 splice @$devices, 2, 0, '-device', $devstr;
4158 unshift @$devices, '-device', $devstr if $k > 0;
4163 push @$machineFlags, 'accel=tcg';
4166 push @$machineFlags, 'smm=off' if should_disable_smm
($conf, $vga, $machine_type);
4168 my $machine_type_min = $machine_type;
4169 if ($add_pve_version) {
4170 $machine_type_min =~ s/\+pve\d+$//;
4171 $machine_type_min .= "+pve$required_pve_version";
4173 push @$machineFlags, "type=${machine_type_min}";
4175 push @$cmd, @$devices;
4176 push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
4177 push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
4178 push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags);
4180 if (my $vmstate = $conf->{vmstate
}) {
4181 my $statepath = PVE
::Storage
::path
($storecfg, $vmstate);
4182 push @$vollist, $vmstate;
4183 push @$cmd, '-loadstate', $statepath;
4184 print "activating and using '$vmstate' as vmstate\n";
4187 if (PVE
::QemuConfig-
>is_template($conf)) {
4188 # needed to workaround base volumes being read-only
4189 push @$cmd, '-snapshot';
4193 if ($conf->{args
}) {
4194 my $aa = PVE
::Tools
::split_args
($conf->{args
});
4198 return wantarray ?
($cmd, $vollist, $spice_port, $pci_devices) : $cmd;
4201 sub check_rng_source
{
4204 # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
4205 die "cannot create VirtIO RNG device: source file '$source' doesn't exist\n"
4208 my $rng_current = '/sys/devices/virtual/misc/hw_random/rng_current';
4209 if ($source eq '/dev/hwrng' && file_read_firstline
($rng_current) eq 'none') {
4210 # Needs to abort, otherwise QEMU crashes on first rng access. Note that rng_current cannot
4211 # be changed to 'none' manually, so once the VM is past this point, it's no longer an issue.
4212 die "Cannot start VM with passed-through RNG device: '/dev/hwrng' exists, but"
4213 ." '$rng_current' is set to 'none'. Ensure that a compatible hardware-RNG is attached"
4221 my $res = mon_cmd
($vmid, 'query-spice');
4223 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
4226 sub vm_devices_list
{
4229 my $res = mon_cmd
($vmid, 'query-pci');
4230 my $devices_to_check = [];
4232 foreach my $pcibus (@$res) {
4233 push @$devices_to_check, @{$pcibus->{devices
}},
4236 while (@$devices_to_check) {
4238 for my $d (@$devices_to_check) {
4239 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
4240 next if !$d->{'pci_bridge'} || !$d->{'pci_bridge'}->{devices
};
4242 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices
}});
4243 push @$to_check, @{$d->{'pci_bridge'}->{devices
}};
4245 $devices_to_check = $to_check;
4248 my $resblock = mon_cmd
($vmid, 'query-block');
4249 foreach my $block (@$resblock) {
4250 if($block->{device
} =~ m/^drive-(\S+)/){
4255 my $resmice = mon_cmd
($vmid, 'query-mice');
4256 foreach my $mice (@$resmice) {
4257 if ($mice->{name
} eq 'QEMU HID Tablet') {
4258 $devices->{tablet
} = 1;
4263 # for usb devices there is no query-usb
4264 # but we can iterate over the entries in
4265 # qom-list path=/machine/peripheral
4266 my $resperipheral = mon_cmd
($vmid, 'qom-list', path
=> '/machine/peripheral');
4267 foreach my $per (@$resperipheral) {
4268 if ($per->{name
} =~ m/^usb(?:redirdev)?\d+$/) {
4269 $devices->{$per->{name
}} = 1;
4277 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4279 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
4281 my $devices_list = vm_devices_list
($vmid);
4282 return 1 if defined($devices_list->{$deviceid});
4284 # add PCI bridge if we need it for the device
4285 qemu_add_pci_bridge
($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type);
4287 if ($deviceid eq 'tablet') {
4288 qemu_deviceadd
($vmid, print_tabletdevice_full
($conf, $arch));
4289 } elsif ($deviceid eq 'keyboard') {
4290 qemu_deviceadd
($vmid, print_keyboarddevice_full
($conf, $arch));
4291 } elsif ($deviceid =~ m/^usbredirdev(\d+)$/) {
4293 qemu_spice_usbredir_chardev_add
($vmid, "usbredirchardev$id");
4294 qemu_deviceadd
($vmid, PVE
::QemuServer
::USB
::print_spice_usbdevice
($id, "xhci", $id + 1));
4295 } elsif ($deviceid =~ m/^usb(\d+)$/) {
4296 qemu_deviceadd
($vmid, PVE
::QemuServer
::USB
::print_usbdevice_full
($conf, $deviceid, $device, {}, $1 + 1));
4297 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4298 qemu_iothread_add
($vmid, $deviceid, $device);
4300 qemu_driveadd
($storecfg, $vmid, $device);
4301 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4303 qemu_deviceadd
($vmid, $devicefull);
4304 eval { qemu_deviceaddverify
($vmid, $deviceid); };
4306 eval { qemu_drivedel
($vmid, $deviceid); };
4310 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4311 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : "lsi";
4312 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4313 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ?
"virtio-scsi-pci" : $scsihw;
4315 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
4317 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread
}) {
4318 qemu_iothread_add
($vmid, $deviceid, $device);
4319 $devicefull .= ",iothread=iothread-$deviceid";
4322 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues
}) {
4323 $devicefull .= ",num_queues=$device->{queues}";
4326 qemu_deviceadd
($vmid, $devicefull);
4327 qemu_deviceaddverify
($vmid, $deviceid);
4328 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4329 qemu_findorcreatescsihw
($storecfg,$conf, $vmid, $device, $arch, $machine_type);
4330 qemu_driveadd
($storecfg, $vmid, $device);
4332 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4333 eval { qemu_deviceadd
($vmid, $devicefull); };
4335 eval { qemu_drivedel
($vmid, $deviceid); };
4339 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4340 return if !qemu_netdevadd
($vmid, $conf, $arch, $device, $deviceid);
4342 my $machine_type = PVE
::QemuServer
::Machine
::qemu_machine_pxe
($vmid, $conf);
4343 my $machine_version = PVE
::QemuServer
::Machine
::extract_version
($machine_type);
4344 my $use_old_bios_files = undef;
4345 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
4347 my $netdevicefull = print_netdevice_full
(
4348 $vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type, $machine_version);
4349 qemu_deviceadd
($vmid, $netdevicefull);
4351 qemu_deviceaddverify
($vmid, $deviceid);
4352 qemu_set_link_status
($vmid, $deviceid, !$device->{link_down
});
4355 eval { qemu_netdevdel
($vmid, $deviceid); };
4359 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
4361 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4362 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
4364 qemu_deviceadd
($vmid, $devicefull);
4365 qemu_deviceaddverify
($vmid, $deviceid);
4367 die "can't hotplug device '$deviceid'\n";
4373 # fixme: this should raise exceptions on error!
4374 sub vm_deviceunplug
{
4375 my ($vmid, $conf, $deviceid) = @_;
4377 my $devices_list = vm_devices_list
($vmid);
4378 return 1 if !defined($devices_list->{$deviceid});
4380 my $bootdisks = PVE
::QemuServer
::Drive
::get_bootdisks
($conf);
4381 die "can't unplug bootdisk '$deviceid'\n" if grep {$_ eq $deviceid} @$bootdisks;
4383 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard' || $deviceid eq 'xhci') {
4384 qemu_devicedel
($vmid, $deviceid);
4385 } elsif ($deviceid =~ m/^usbredirdev\d+$/) {
4386 qemu_devicedel
($vmid, $deviceid);
4387 qemu_devicedelverify
($vmid, $deviceid);
4388 } elsif ($deviceid =~ m/^usb\d+$/) {
4389 qemu_devicedel
($vmid, $deviceid);
4390 qemu_devicedelverify
($vmid, $deviceid);
4391 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4392 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4394 qemu_devicedel
($vmid, $deviceid);
4395 qemu_devicedelverify
($vmid, $deviceid);
4396 qemu_drivedel
($vmid, $deviceid);
4397 qemu_iothread_del
($vmid, $deviceid, $device);
4398 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4399 qemu_devicedel
($vmid, $deviceid);
4400 qemu_devicedelverify
($vmid, $deviceid);
4401 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4402 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4404 qemu_devicedel
($vmid, $deviceid);
4405 qemu_devicedelverify
($vmid, $deviceid);
4406 qemu_drivedel
($vmid, $deviceid);
4407 qemu_deletescsihw
($conf, $vmid, $deviceid);
4409 qemu_iothread_del
($vmid, "virtioscsi$device->{index}", $device)
4410 if $conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single');
4411 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4412 qemu_devicedel
($vmid, $deviceid);
4413 qemu_devicedelverify
($vmid, $deviceid);
4414 qemu_netdevdel
($vmid, $deviceid);
4416 die "can't unplug device '$deviceid'\n";
4422 sub qemu_spice_usbredir_chardev_add
{
4423 my ($vmid, $id) = @_;
4425 mon_cmd
($vmid, "chardev-add" , (
4436 sub qemu_deviceadd
{
4437 my ($vmid, $devicefull) = @_;
4439 $devicefull = "driver=".$devicefull;
4440 my %options = split(/[=,]/, $devicefull);
4442 mon_cmd
($vmid, "device_add" , %options);
4445 sub qemu_devicedel
{
4446 my ($vmid, $deviceid) = @_;
4448 my $ret = mon_cmd
($vmid, "device_del", id
=> $deviceid);
4451 sub qemu_iothread_add
{
4452 my ($vmid, $deviceid, $device) = @_;
4454 if ($device->{iothread
}) {
4455 my $iothreads = vm_iothreads_list
($vmid);
4456 qemu_objectadd
($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4460 sub qemu_iothread_del
{
4461 my ($vmid, $deviceid, $device) = @_;
4463 if ($device->{iothread
}) {
4464 my $iothreads = vm_iothreads_list
($vmid);
4465 qemu_objectdel
($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4469 sub qemu_objectadd
{
4470 my ($vmid, $objectid, $qomtype) = @_;
4472 mon_cmd
($vmid, "object-add", id
=> $objectid, "qom-type" => $qomtype);
4477 sub qemu_objectdel
{
4478 my ($vmid, $objectid) = @_;
4480 mon_cmd
($vmid, "object-del", id
=> $objectid);
4486 my ($storecfg, $vmid, $device) = @_;
4488 my $kvmver = get_running_qemu_version
($vmid);
4489 my $io_uring = min_version
($kvmver, 6, 0);
4490 my $drive = print_drive_commandline_full
($storecfg, $vmid, $device, undef, $io_uring);
4491 $drive =~ s/\\/\\\\/g;
4492 my $ret = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "drive_add auto \"$drive\"");
4494 # If the command succeeds qemu prints: "OK
"
4495 return 1 if $ret =~ m/OK/s;
4497 die "adding drive failed
: $ret\n";
4501 my ($vmid, $deviceid) = @_;
4503 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-
$deviceid");
4506 return 1 if $ret eq "";
4508 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4509 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4511 die "deleting drive
$deviceid failed
: $ret\n";
4514 sub qemu_deviceaddverify {
4515 my ($vmid, $deviceid) = @_;
4517 for (my $i = 0; $i <= 5; $i++) {
4518 my $devices_list = vm_devices_list($vmid);
4519 return 1 if defined($devices_list->{$deviceid});
4523 die "error on hotplug device
'$deviceid'\n";
4527 sub qemu_devicedelverify {
4528 my ($vmid, $deviceid) = @_;
4530 # need to verify that the device is correctly removed as device_del
4531 # is async and empty return is not reliable
4533 for (my $i = 0; $i <= 5; $i++) {
4534 my $devices_list = vm_devices_list($vmid);
4535 return 1 if !defined($devices_list->{$deviceid});
4539 die "error on hot-unplugging device
'$deviceid'\n";
4542 sub qemu_findorcreatescsihw {
4543 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4545 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4547 my $scsihwid="$controller_prefix$controller";
4548 my $devices_list = vm_devices_list($vmid);
4550 if (!defined($devices_list->{$scsihwid})) {
4551 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4557 sub qemu_deletescsihw {
4558 my ($conf, $vmid, $opt) = @_;
4560 my $device = parse_drive($opt, $conf->{$opt});
4562 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4563 vm_deviceunplug($vmid, $conf, "virtioscsi
$device->{index}");
4567 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4569 my $devices_list = vm_devices_list($vmid);
4570 foreach my $opt (keys %{$devices_list}) {
4571 if (is_valid_drivename($opt)) {
4572 my $drive = parse_drive($opt, $conf->{$opt});
4573 if ($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4579 my $scsihwid="scsihw
$controller";
4581 vm_deviceunplug($vmid, $conf, $scsihwid);
4586 sub qemu_add_pci_bridge {
4587 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4593 print_pci_addr($device, $bridges, $arch, $machine_type);
4595 while (my ($k, $v) = each %$bridges) {
4598 return 1 if !defined($bridgeid) || $bridgeid < 1;
4600 my $bridge = "pci
.$bridgeid";
4601 my $devices_list = vm_devices_list($vmid);
4603 if (!defined($devices_list->{$bridge})) {
4604 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4610 sub qemu_set_link_status {
4611 my ($vmid, $device, $up) = @_;
4613 mon_cmd($vmid, "set_link
", name => $device,
4614 up => $up ? JSON::true : JSON::false);
4617 sub qemu_netdevadd {
4618 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4620 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4621 my %options = split(/[=,]/, $netdev);
4623 if (defined(my $vhost = $options{vhost})) {
4624 $options{vhost} = JSON::boolean(PVE::JSONSchema::parse_boolean($vhost));
4627 if (defined(my $queues = $options{queues})) {
4628 $options{queues} = $queues + 0;
4631 mon_cmd($vmid, "netdev_add
", %options);
4635 sub qemu_netdevdel {
4636 my ($vmid, $deviceid) = @_;
4638 mon_cmd($vmid, "netdev_del
", id => $deviceid);
4641 sub qemu_usb_hotplug {
4642 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4646 # remove the old one first
4647 vm_deviceunplug($vmid, $conf, $deviceid);
4649 # check if xhci controller is necessary and available
4650 my $devicelist = vm_devices_list($vmid);
4652 if (!$devicelist->{xhci}) {
4653 my $pciaddr = print_pci_addr("xhci
", undef, $arch, $machine_type);
4654 qemu_deviceadd($vmid, PVE::QemuServer::USB::print_qemu_xhci_controller($pciaddr));
4658 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type);
4661 sub qemu_cpu_hotplug {
4662 my ($vmid, $conf, $vcpus) = @_;
4664 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
4667 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4668 $sockets = $conf->{sockets} if $conf->{sockets};
4669 my $cores = $conf->{cores} || 1;
4670 my $maxcpus = $sockets * $cores;
4672 $vcpus = $maxcpus if !$vcpus;
4674 die "you can
't add more vcpus than maxcpus\n"
4675 if $vcpus > $maxcpus;
4677 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4679 if ($vcpus < $currentvcpus) {
4681 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4683 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4684 qemu_devicedel($vmid, "cpu$i");
4686 my $currentrunningvcpus = undef;
4688 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4689 last if scalar(@{$currentrunningvcpus}) == $i-1;
4690 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4694 #update conf after each succesfull cpu unplug
4695 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4696 PVE::QemuConfig->write_config($vmid, $conf);
4699 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4705 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4706 die "vcpus in running vm does not match its configuration\n"
4707 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4709 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4711 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4712 my $cpustr = print_cpu_device($conf, $i);
4713 qemu_deviceadd($vmid, $cpustr);
4716 my $currentrunningvcpus = undef;
4718 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4719 last if scalar(@{$currentrunningvcpus}) == $i;
4720 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4724 #update conf after each succesfull cpu hotplug
4725 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4726 PVE::QemuConfig->write_config($vmid, $conf);
4730 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4731 mon_cmd($vmid, "cpu-add", id => int($i));
4736 sub qemu_block_set_io_throttle {
4737 my ($vmid, $deviceid,
4738 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4739 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4740 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4741 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4743 return if !check_running($vmid) ;
4745 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4747 bps_rd => int($bps_rd),
4748 bps_wr => int($bps_wr),
4750 iops_rd => int($iops_rd),
4751 iops_wr => int($iops_wr),
4752 bps_max => int($bps_max),
4753 bps_rd_max => int($bps_rd_max),
4754 bps_wr_max => int($bps_wr_max),
4755 iops_max => int($iops_max),
4756 iops_rd_max => int($iops_rd_max),
4757 iops_wr_max => int($iops_wr_max),
4758 bps_max_length => int($bps_max_length),
4759 bps_rd_max_length => int($bps_rd_max_length),
4760 bps_wr_max_length => int($bps_wr_max_length),
4761 iops_max_length => int($iops_max_length),
4762 iops_rd_max_length => int($iops_rd_max_length),
4763 iops_wr_max_length => int($iops_wr_max_length),
4768 sub qemu_block_resize {
4769 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4771 my $running = check_running($vmid);
4773 PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4775 return if !$running;
4777 my $padding = (1024 - $size % 1024) % 1024;
4778 $size = $size + $padding;
4783 device => $deviceid,
4789 sub qemu_volume_snapshot {
4790 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4792 my $running = check_running($vmid);
4794 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4795 mon_cmd($vmid, 'blockdev-snapshot-internal-sync
', device => $deviceid, name => $snap);
4797 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4801 sub qemu_volume_snapshot_delete {
4802 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4804 my $running = check_running($vmid);
4809 my $conf = PVE::QemuConfig->load_config($vmid);
4810 PVE::QemuConfig->foreach_volume($conf, sub {
4811 my ($ds, $drive) = @_;
4812 $running = 1 if $drive->{file} eq $volid;
4816 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4817 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync
', device => $deviceid, name => $snap);
4819 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4823 sub set_migration_caps {
4824 my ($vmid, $savevm) = @_;
4826 my $qemu_support = eval { mon_cmd($vmid, "query-proxmox-support") };
4828 my $bitmap_prop = $savevm ? 'pbs-dirty-bitmap-savevm
' : 'pbs-dirty-bitmap-migration
';
4829 my $dirty_bitmaps = $qemu_support->{$bitmap_prop} ? 1 : 0;
4834 "auto-converge" => 1,
4836 "x-rdma-pin-all" => 0,
4839 "dirty-bitmaps" => $dirty_bitmaps,
4842 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4844 for my $supported_capability (@$supported_capabilities) {
4846 capability => $supported_capability->{capability},
4847 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4851 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4855 my ($conf, $func, @param) = @_;
4859 my $test_volid = sub {
4860 my ($key, $drive, $snapname, $pending) = @_;
4862 my $volid = $drive->{file};
4865 $volhash->{$volid}->{cdrom} //= 1;
4866 $volhash->{$volid}->{cdrom} = 0 if !drive_is_cdrom($drive);
4868 my $replicate = $drive->{replicate} // 1;
4869 $volhash->{$volid}->{replicate} //= 0;
4870 $volhash->{$volid}->{replicate} = 1 if $replicate;
4872 $volhash->{$volid}->{shared} //= 0;
4873 $volhash->{$volid}->{shared} = 1 if $drive->{shared};
4875 $volhash->{$volid}->{is_unused} //= 0;
4876 $volhash->{$volid}->{is_unused} = 1 if $key =~ /^unused\d+$/;
4878 $volhash->{$volid}->{is_attached} //= 0;
4879 $volhash->{$volid}->{is_attached} = 1
4880 if !$volhash->{$volid}->{is_unused} && !defined($snapname) && !$pending;
4882 $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
4883 if defined($snapname);
4885 $volhash->{$volid}->{referenced_in_pending} = 1 if $pending;
4887 my $size = $drive->{size};
4888 $volhash->{$volid}->{size} //= $size if $size;
4890 $volhash->{$volid}->{is_vmstate} //= 0;
4891 $volhash->{$volid}->{is_vmstate} = 1 if $key eq 'vmstate
';
4893 $volhash->{$volid}->{is_tpmstate} //= 0;
4894 $volhash->{$volid}->{is_tpmstate} = 1 if $key eq 'tpmstate0
';
4896 $volhash->{$volid}->{drivename} = $key if is_valid_drivename($key);
4899 my $include_opts = {
4900 extra_keys => ['vmstate
'],
4901 include_unused => 1,
4904 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $test_volid);
4906 PVE::QemuConfig->foreach_volume_full($conf->{pending}, $include_opts, $test_volid, undef, 1)
4907 if defined($conf->{pending}) && $conf->{pending}->%*;
4909 foreach my $snapname (keys %{$conf->{snapshots}}) {
4910 my $snap = $conf->{snapshots}->{$snapname};
4911 PVE::QemuConfig->foreach_volume_full($snap, $include_opts, $test_volid, $snapname);
4914 foreach my $volid (keys %$volhash) {
4915 &$func($volid, $volhash->{$volid}, @param);
4919 my $fast_plug_option = {
4923 'migrate_downtime
' => 1,
4924 'migrate_speed
' => 1,
4931 'vmstatestorage
' => 1,
4934 for my $opt (keys %$confdesc_cloudinit) {
4935 $fast_plug_option->{$opt} = 1;
4938 # hotplug changes in [PENDING]
4939 # $selection hash can be used to only apply specified options, for
4940 # example: { cores => 1 } (only apply changed 'cores
')
4941 # $errors ref is used to return error messages
4942 sub vmconfig_hotplug_pending {
4943 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4945 my $defaults = load_defaults();
4946 my $arch = get_vm_arch($conf);
4947 my $machine_type = get_vm_machine($conf, undef, $arch);
4949 # commit values which do not have any impact on running VM first
4950 # Note: those option cannot raise errors, we we do not care about
4951 # $selection and always apply them.
4953 my $add_error = sub {
4954 my ($opt, $msg) = @_;
4955 $errors->{$opt} = "hotplug problem - $msg";
4958 my $cloudinit_pending_properties = PVE::QemuServer::cloudinit_pending_properties();
4960 my $cloudinit_record_changed = sub {
4961 my ($conf, $opt, $old, $new) = @_;
4962 return if !$cloudinit_pending_properties->{$opt};
4964 my $ci = ($conf->{cloudinit} //= {});
4966 my $recorded = $ci->{$opt};
4967 my %added = map { $_ => 1 } PVE::Tools::split_list(delete($ci->{added}) // '');
4969 if (defined($new)) {
4970 if (defined($old)) {
4971 # an existing value is being modified
4972 if (defined($recorded)) {
4973 # the value was already not in sync
4974 if ($new eq $recorded) {
4975 # a value is being reverted to the cloud-init state:
4977 delete $added{$opt};
4979 # the value was changed multiple times, do nothing
4981 } elsif ($added{$opt}) {
4982 # the value had been marked as added and is being changed, do nothing
4984 # the value is new, record it:
4988 # a new value is being added
4989 if (defined($recorded)) {
4990 # it was already not in sync
4991 if ($new eq $recorded) {
4992 # a value is being reverted to the cloud-init state:
4994 delete $added{$opt};
4996 # the value had temporarily been removed, do nothing
4998 } elsif ($added{$opt}) {
4999 # the value had been marked as added already, do nothing
5001 # the value is new, add it
5005 } elsif (!defined($old)) {
5006 # a non-existent value is being removed? ignore...
5008 # a value is being deleted
5009 if (defined($recorded)) {
5010 # a value was already recorded, just keep it
5011 } elsif ($added{$opt}) {
5012 # the value was marked as added, remove it
5013 delete $added{$opt};
5015 # a previously unrecorded value is being removed, record the old value:
5020 my $added = join(',', sort keys %added);
5021 $ci->{added} = $added if length($added);
5025 foreach my $opt (keys %{$conf->{pending}}) { # add/change
5026 if ($fast_plug_option->{$opt}) {
5027 my $new = delete $conf->{pending}->{$opt};
5028 $cloudinit_record_changed->($conf, $opt, $conf->{$opt}, $new);
5029 $conf->{$opt} = $new;
5035 PVE::QemuConfig->write_config($vmid, $conf);
5038 my $ostype = $conf->{ostype};
5039 my $version = extract_version($machine_type, get_running_qemu_version($vmid));
5040 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
5041 my $usb_hotplug = $hotplug_features->{usb}
5042 && min_version($version, 7, 1)
5043 && defined($ostype) && ($ostype eq 'l26
' || windows_version($ostype) > 7);
5045 my $cgroup = PVE::QemuServer::CGroup->new($vmid);
5046 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
5048 foreach my $opt (sort keys %$pending_delete_hash) {
5049 next if $selection && !$selection->{$opt};
5050 my $force = $pending_delete_hash->{$opt}->{force};
5052 if ($opt eq 'hotplug
') {
5053 die "skip\n" if ($conf->{hotplug} =~ /memory/);
5054 } elsif ($opt eq 'tablet
') {
5055 die "skip\n" if !$hotplug_features->{usb};
5056 if ($defaults->{tablet}) {
5057 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
5058 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
5059 if $arch eq 'aarch64
';
5061 vm_deviceunplug($vmid, $conf, 'tablet
');
5062 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
5064 } elsif ($opt =~ m/^usb(\d+)$/) {
5066 die "skip\n" if !$usb_hotplug;
5067 vm_deviceunplug($vmid, $conf, "usbredirdev$index"); # if it's a spice port
5068 vm_deviceunplug
($vmid, $conf, $opt);
5069 } elsif ($opt eq 'vcpus') {
5070 die "skip\n" if !$hotplug_features->{cpu
};
5071 qemu_cpu_hotplug
($vmid, $conf, undef);
5072 } elsif ($opt eq 'balloon') {
5073 # enable balloon device is not hotpluggable
5074 die "skip\n" if defined($conf->{balloon
}) && $conf->{balloon
} == 0;
5075 # here we reset the ballooning value to memory
5076 my $balloon = $conf->{memory
} || $defaults->{memory
};
5077 mon_cmd
($vmid, "balloon", value
=> $balloon*1024*1024);
5078 } elsif ($fast_plug_option->{$opt}) {
5080 } elsif ($opt =~ m/^net(\d+)$/) {
5081 die "skip\n" if !$hotplug_features->{network
};
5082 vm_deviceunplug
($vmid, $conf, $opt);
5083 } elsif (is_valid_drivename
($opt)) {
5084 die "skip\n" if !$hotplug_features->{disk
} || $opt =~ m/(ide|sata)(\d+)/;
5085 vm_deviceunplug
($vmid, $conf, $opt);
5086 vmconfig_delete_or_detach_drive
($vmid, $storecfg, $conf, $opt, $force);
5087 } elsif ($opt =~ m/^memory$/) {
5088 die "skip\n" if !$hotplug_features->{memory
};
5089 PVE
::QemuServer
::Memory
::qemu_memory_hotplug
($vmid, $conf, $defaults);
5090 } elsif ($opt eq 'cpuunits') {
5091 $cgroup->change_cpu_shares(undef);
5092 } elsif ($opt eq 'cpulimit') {
5093 $cgroup->change_cpu_quota(undef, undef); # reset, cgroup module can better decide values
5099 &$add_error($opt, $err) if $err ne "skip\n";
5101 my $old = delete $conf->{$opt};
5102 $cloudinit_record_changed->($conf, $opt, $old, undef);
5103 PVE
::QemuConfig-
>remove_from_pending_delete($conf, $opt);
5108 foreach my $opt (keys %{$conf->{pending
}}) {
5109 next if $selection && !$selection->{$opt};
5110 my $value = $conf->{pending
}->{$opt};
5112 if ($opt eq 'hotplug') {
5113 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug
} =~ /memory/);
5114 } elsif ($opt eq 'tablet') {
5115 die "skip\n" if !$hotplug_features->{usb
};
5117 vm_deviceplug
($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
5118 vm_deviceplug
($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
5119 if $arch eq 'aarch64';
5120 } elsif ($value == 0) {
5121 vm_deviceunplug
($vmid, $conf, 'tablet');
5122 vm_deviceunplug
($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
5124 } elsif ($opt =~ m/^usb(\d+)$/) {
5126 die "skip\n" if !$usb_hotplug;
5127 my $d = eval { parse_property_string
('pve-qm-usb', $value) };
5129 if ($d->{host
} =~ m/^spice$/i) {
5130 $id = "usbredirdev$index";
5132 qemu_usb_hotplug
($storecfg, $conf, $vmid, $id, $d, $arch, $machine_type);
5133 } elsif ($opt eq 'vcpus') {
5134 die "skip\n" if !$hotplug_features->{cpu
};
5135 qemu_cpu_hotplug
($vmid, $conf, $value);
5136 } elsif ($opt eq 'balloon') {
5137 # enable/disable balloning device is not hotpluggable
5138 my $old_balloon_enabled = !!(!defined($conf->{balloon
}) || $conf->{balloon
});
5139 my $new_balloon_enabled = !!(!defined($conf->{pending
}->{balloon
}) || $conf->{pending
}->{balloon
});
5140 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
5142 # allow manual ballooning if shares is set to zero
5143 if ((defined($conf->{shares
}) && ($conf->{shares
} == 0))) {
5144 my $balloon = $conf->{pending
}->{balloon
} || $conf->{memory
} || $defaults->{memory
};
5145 mon_cmd
($vmid, "balloon", value
=> $balloon*1024*1024);
5147 } elsif ($opt =~ m/^net(\d+)$/) {
5148 # some changes can be done without hotplug
5149 vmconfig_update_net
($storecfg, $conf, $hotplug_features->{network
},
5150 $vmid, $opt, $value, $arch, $machine_type);
5151 } elsif (is_valid_drivename
($opt)) {
5152 die "skip\n" if $opt eq 'efidisk0' || $opt eq 'tpmstate0';
5153 # some changes can be done without hotplug
5154 my $drive = parse_drive
($opt, $value);
5155 if (drive_is_cloudinit
($drive)) {
5156 $cloudinit_opt = [$opt, $drive];
5157 # apply all the other changes first, then generate the cloudinit disk
5160 vmconfig_update_disk
($storecfg, $conf, $hotplug_features->{disk
},
5161 $vmid, $opt, $value, $arch, $machine_type);
5162 } elsif ($opt =~ m/^memory$/) { #dimms
5163 die "skip\n" if !$hotplug_features->{memory
};
5164 $value = PVE
::QemuServer
::Memory
::qemu_memory_hotplug
($vmid, $conf, $defaults, $value);
5165 } elsif ($opt eq 'cpuunits') {
5166 my $new_cpuunits = PVE
::CGroup
::clamp_cpu_shares
($conf->{pending
}->{$opt}); #clamp
5167 $cgroup->change_cpu_shares($new_cpuunits);
5168 } elsif ($opt eq 'cpulimit') {
5169 my $cpulimit = $conf->{pending
}->{$opt} == 0 ?
-1 : int($conf->{pending
}->{$opt} * 100000);
5170 $cgroup->change_cpu_quota($cpulimit, 100000);
5171 } elsif ($opt eq 'agent') {
5172 vmconfig_update_agent
($conf, $opt, $value);
5174 die "skip\n"; # skip non-hot-pluggable options
5178 &$add_error($opt, $err) if $err ne "skip\n";
5180 $cloudinit_record_changed->($conf, $opt, $conf->{$opt}, $value);
5181 $conf->{$opt} = $value;
5182 delete $conf->{pending
}->{$opt};
5186 if (defined($cloudinit_opt)) {
5187 my ($opt, $drive) = @$cloudinit_opt;
5188 my $value = $conf->{pending
}->{$opt};
5190 my $temp = {%$conf, $opt => $value};
5191 PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($temp, $vmid);
5192 vmconfig_update_disk
($storecfg, $conf, $hotplug_features->{disk
},
5193 $vmid, $opt, $value, $arch, $machine_type);
5196 &$add_error($opt, $err) if $err ne "skip\n";
5198 $conf->{$opt} = $value;
5199 delete $conf->{pending
}->{$opt};
5203 # unplug xhci controller if no usb device is left
5206 for (my $i = 0; $i < $PVE::QemuServer
::USB
::MAX_USB_DEVICES
; $i++) {
5207 next if !defined($conf->{"usb$i"});
5212 vm_deviceunplug
($vmid, $conf, 'xhci');
5216 PVE
::QemuConfig-
>write_config($vmid, $conf);
5218 if ($hotplug_features->{cloudinit
} && PVE
::QemuServer
::Cloudinit
::has_changes
($conf)) {
5219 PVE
::QemuServer
::vmconfig_update_cloudinit_drive
($storecfg, $conf, $vmid);
5223 sub try_deallocate_drive
{
5224 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
5226 if (($force || $key =~ /^unused/) && !drive_is_cdrom
($drive, 1)) {
5227 my $volid = $drive->{file
};
5228 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
5229 my $sid = PVE
::Storage
::parse_volume_id
($volid);
5230 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
5232 # check if the disk is really unused
5233 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
5234 if PVE
::QemuServer
::Drive
::is_volume_in_use
($storecfg, $conf, $key, $volid);
5235 PVE
::Storage
::vdisk_free
($storecfg, $volid);
5238 # If vm is not owner of this disk remove from config
5246 sub vmconfig_delete_or_detach_drive
{
5247 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
5249 my $drive = parse_drive
($opt, $conf->{$opt});
5251 my $rpcenv = PVE
::RPCEnvironment
::get
();
5252 my $authuser = $rpcenv->get_user();
5255 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
5256 try_deallocate_drive
($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
5258 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $drive);
5264 sub vmconfig_apply_pending
{
5265 my ($vmid, $conf, $storecfg, $errors, $skip_cloud_init) = @_;
5267 return if !scalar(keys %{$conf->{pending
}});
5269 my $add_apply_error = sub {
5270 my ($opt, $msg) = @_;
5271 my $err_msg = "unable to apply pending change $opt : $msg";
5272 $errors->{$opt} = $err_msg;
5278 my $pending_delete_hash = PVE
::QemuConfig-
>parse_pending_delete($conf->{pending
}->{delete});
5279 foreach my $opt (sort keys %$pending_delete_hash) {
5280 my $force = $pending_delete_hash->{$opt}->{force
};
5282 if ($opt =~ m/^unused/) {
5283 die "internal error";
5284 } elsif (defined($conf->{$opt}) && is_valid_drivename
($opt)) {
5285 vmconfig_delete_or_detach_drive
($vmid, $storecfg, $conf, $opt, $force);
5289 $add_apply_error->($opt, $err);
5291 PVE
::QemuConfig-
>remove_from_pending_delete($conf, $opt);
5292 delete $conf->{$opt};
5296 PVE
::QemuConfig-
>cleanup_pending($conf);
5298 my $generate_cloudinit = $skip_cloud_init ?
0 : undef;
5300 foreach my $opt (keys %{$conf->{pending
}}) { # add/change
5301 next if $opt eq 'delete'; # just to be sure
5303 if (defined($conf->{$opt}) && is_valid_drivename
($opt)) {
5304 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, parse_drive
($opt, $conf->{$opt}))
5308 $add_apply_error->($opt, $err);
5311 if (is_valid_drivename
($opt)) {
5312 my $drive = parse_drive
($opt, $conf->{pending
}->{$opt});
5313 $generate_cloudinit //= 1 if drive_is_cloudinit
($drive);
5316 $conf->{$opt} = delete $conf->{pending
}->{$opt};
5320 # write all changes at once to avoid unnecessary i/o
5321 PVE
::QemuConfig-
>write_config($vmid, $conf);
5322 if ($generate_cloudinit) {
5323 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5324 # After successful generation and if there were changes to be applied, update the
5325 # config to drop the {cloudinit} entry.
5326 PVE
::QemuConfig-
>write_config($vmid, $conf);
5331 sub vmconfig_update_net
{
5332 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5334 my $newnet = parse_net
($value);
5336 if ($conf->{$opt}) {
5337 my $oldnet = parse_net
($conf->{$opt});
5339 if (safe_string_ne
($oldnet->{model
}, $newnet->{model
}) ||
5340 safe_string_ne
($oldnet->{macaddr
}, $newnet->{macaddr
}) ||
5341 safe_num_ne
($oldnet->{queues
}, $newnet->{queues
}) ||
5342 safe_num_ne
($oldnet->{mtu
}, $newnet->{mtu
}) ||
5343 !($newnet->{bridge
} && $oldnet->{bridge
})) { # bridge/nat mode change
5345 # for non online change, we try to hot-unplug
5346 die "skip\n" if !$hotplug;
5347 vm_deviceunplug
($vmid, $conf, $opt);
5350 die "internal error" if $opt !~ m/net(\d+)/;
5351 my $iface = "tap${vmid}i$1";
5353 if (safe_string_ne
($oldnet->{bridge
}, $newnet->{bridge
}) ||
5354 safe_num_ne
($oldnet->{tag
}, $newnet->{tag
}) ||
5355 safe_string_ne
($oldnet->{trunks
}, $newnet->{trunks
}) ||
5356 safe_num_ne
($oldnet->{firewall
}, $newnet->{firewall
})) {
5357 PVE
::Network
::tap_unplug
($iface);
5360 PVE
::Network
::SDN
::Zones
::tap_plug
($iface, $newnet->{bridge
}, $newnet->{tag
}, $newnet->{firewall
}, $newnet->{trunks
}, $newnet->{rate
});
5362 PVE
::Network
::tap_plug
($iface, $newnet->{bridge
}, $newnet->{tag
}, $newnet->{firewall
}, $newnet->{trunks
}, $newnet->{rate
});
5364 } elsif (safe_num_ne
($oldnet->{rate
}, $newnet->{rate
})) {
5365 # Rate can be applied on its own but any change above needs to
5366 # include the rate in tap_plug since OVS resets everything.
5367 PVE
::Network
::tap_rate_limit
($iface, $newnet->{rate
});
5370 if (safe_string_ne
($oldnet->{link_down
}, $newnet->{link_down
})) {
5371 qemu_set_link_status
($vmid, $opt, !$newnet->{link_down
});
5379 vm_deviceplug
($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
5385 sub vmconfig_update_agent
{
5386 my ($conf, $opt, $value) = @_;
5388 die "skip\n" if !$conf->{$opt};
5390 my $hotplug_options = { fstrim_cloned_disks
=> 1 };
5392 my $old_agent = parse_guest_agent
($conf);
5393 my $agent = parse_guest_agent
({$opt => $value});
5395 for my $option (keys %$agent) { # added/changed options
5396 next if defined($hotplug_options->{$option});
5397 die "skip\n" if safe_string_ne
($agent->{$option}, $old_agent->{$option});
5400 for my $option (keys %$old_agent) { # removed options
5401 next if defined($hotplug_options->{$option});
5402 die "skip\n" if safe_string_ne
($old_agent->{$option}, $agent->{$option});
5405 return; # either no actual change (e.g., format string reordered) or just hotpluggable changes
5408 sub vmconfig_update_disk
{
5409 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5411 my $drive = parse_drive
($opt, $value);
5413 if ($conf->{$opt} && (my $old_drive = parse_drive
($opt, $conf->{$opt}))) {
5414 my $media = $drive->{media
} || 'disk';
5415 my $oldmedia = $old_drive->{media
} || 'disk';
5416 die "unable to change media type\n" if $media ne $oldmedia;
5418 if (!drive_is_cdrom
($old_drive)) {
5420 if ($drive->{file
} ne $old_drive->{file
}) {
5422 die "skip\n" if !$hotplug;
5424 # unplug and register as unused
5425 vm_deviceunplug
($vmid, $conf, $opt);
5426 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $old_drive)
5429 # update existing disk
5431 # skip non hotpluggable value
5432 if (safe_string_ne
($drive->{aio
}, $old_drive->{aio
}) ||
5433 safe_string_ne
($drive->{discard
}, $old_drive->{discard
}) ||
5434 safe_string_ne
($drive->{iothread
}, $old_drive->{iothread
}) ||
5435 safe_string_ne
($drive->{queues
}, $old_drive->{queues
}) ||
5436 safe_string_ne
($drive->{cache
}, $old_drive->{cache
}) ||
5437 safe_string_ne
($drive->{ssd
}, $old_drive->{ssd
}) ||
5438 safe_string_ne
($drive->{ro
}, $old_drive->{ro
})) {
5443 if (safe_num_ne
($drive->{mbps
}, $old_drive->{mbps
}) ||
5444 safe_num_ne
($drive->{mbps_rd
}, $old_drive->{mbps_rd
}) ||
5445 safe_num_ne
($drive->{mbps_wr
}, $old_drive->{mbps_wr
}) ||
5446 safe_num_ne
($drive->{iops
}, $old_drive->{iops
}) ||
5447 safe_num_ne
($drive->{iops_rd
}, $old_drive->{iops_rd
}) ||
5448 safe_num_ne
($drive->{iops_wr
}, $old_drive->{iops_wr
}) ||
5449 safe_num_ne
($drive->{mbps_max
}, $old_drive->{mbps_max
}) ||
5450 safe_num_ne
($drive->{mbps_rd_max
}, $old_drive->{mbps_rd_max
}) ||
5451 safe_num_ne
($drive->{mbps_wr_max
}, $old_drive->{mbps_wr_max
}) ||
5452 safe_num_ne
($drive->{iops_max
}, $old_drive->{iops_max
}) ||
5453 safe_num_ne
($drive->{iops_rd_max
}, $old_drive->{iops_rd_max
}) ||
5454 safe_num_ne
($drive->{iops_wr_max
}, $old_drive->{iops_wr_max
}) ||
5455 safe_num_ne
($drive->{bps_max_length
}, $old_drive->{bps_max_length
}) ||
5456 safe_num_ne
($drive->{bps_rd_max_length
}, $old_drive->{bps_rd_max_length
}) ||
5457 safe_num_ne
($drive->{bps_wr_max_length
}, $old_drive->{bps_wr_max_length
}) ||
5458 safe_num_ne
($drive->{iops_max_length
}, $old_drive->{iops_max_length
}) ||
5459 safe_num_ne
($drive->{iops_rd_max_length
}, $old_drive->{iops_rd_max_length
}) ||
5460 safe_num_ne
($drive->{iops_wr_max_length
}, $old_drive->{iops_wr_max_length
})) {
5462 qemu_block_set_io_throttle
(
5464 ($drive->{mbps
} || 0)*1024*1024,
5465 ($drive->{mbps_rd
} || 0)*1024*1024,
5466 ($drive->{mbps_wr
} || 0)*1024*1024,
5467 $drive->{iops
} || 0,
5468 $drive->{iops_rd
} || 0,
5469 $drive->{iops_wr
} || 0,
5470 ($drive->{mbps_max
} || 0)*1024*1024,
5471 ($drive->{mbps_rd_max
} || 0)*1024*1024,
5472 ($drive->{mbps_wr_max
} || 0)*1024*1024,
5473 $drive->{iops_max
} || 0,
5474 $drive->{iops_rd_max
} || 0,
5475 $drive->{iops_wr_max
} || 0,
5476 $drive->{bps_max_length
} || 1,
5477 $drive->{bps_rd_max_length
} || 1,
5478 $drive->{bps_wr_max_length
} || 1,
5479 $drive->{iops_max_length
} || 1,
5480 $drive->{iops_rd_max_length
} || 1,
5481 $drive->{iops_wr_max_length
} || 1,
5491 if ($drive->{file
} eq 'none') {
5492 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$opt");
5493 if (drive_is_cloudinit
($old_drive)) {
5494 vmconfig_register_unused_drive
($storecfg, $vmid, $conf, $old_drive);
5497 my $path = get_iso_path
($storecfg, $vmid, $drive->{file
});
5499 # force eject if locked
5500 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$opt");
5503 mon_cmd
($vmid, "blockdev-change-medium",
5504 id
=> "$opt", filename
=> "$path");
5512 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
5514 PVE
::Storage
::activate_volumes
($storecfg, [$drive->{file
}]) if $drive->{file
} !~ m
|^/dev/.+|;
5515 vm_deviceplug
($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
5518 sub vmconfig_update_cloudinit_drive
{
5519 my ($storecfg, $conf, $vmid) = @_;
5521 my $cloudinit_ds = undef;
5522 my $cloudinit_drive = undef;
5524 PVE
::QemuConfig-
>foreach_volume($conf, sub {
5525 my ($ds, $drive) = @_;
5526 if (PVE
::QemuServer
::drive_is_cloudinit
($drive)) {
5527 $cloudinit_ds = $ds;
5528 $cloudinit_drive = $drive;
5532 return if !$cloudinit_drive;
5534 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5535 PVE
::QemuConfig-
>write_config($vmid, $conf);
5538 my $running = PVE
::QemuServer
::check_running
($vmid);
5541 my $path = PVE
::Storage
::path
($storecfg, $cloudinit_drive->{file
});
5543 mon_cmd
($vmid, "eject", force
=> JSON
::true
, id
=> "$cloudinit_ds");
5544 mon_cmd
($vmid, "blockdev-change-medium", id
=> "$cloudinit_ds", filename
=> "$path");
5549 # called in locked context by incoming migration
5550 sub vm_migrate_get_nbd_disks
{
5551 my ($storecfg, $conf, $replicated_volumes) = @_;
5553 my $local_volumes = {};
5554 PVE
::QemuConfig-
>foreach_volume($conf, sub {
5555 my ($ds, $drive) = @_;
5557 return if drive_is_cdrom
($drive);
5558 return if $ds eq 'tpmstate0';
5560 my $volid = $drive->{file
};
5564 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid);
5566 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
5567 return if $scfg->{shared
};
5569 my $format = qemu_img_format
($scfg, $volname);
5571 # replicated disks re-use existing state via bitmap
5572 my $use_existing = $replicated_volumes->{$volid} ?
1 : 0;
5573 $local_volumes->{$ds} = [$volid, $storeid, $drive, $use_existing, $format];
5575 return $local_volumes;
5578 # called in locked context by incoming migration
5579 sub vm_migrate_alloc_nbd_disks
{
5580 my ($storecfg, $vmid, $source_volumes, $storagemap) = @_;
5583 foreach my $opt (sort keys %$source_volumes) {
5584 my ($volid, $storeid, $drive, $use_existing, $format) = @{$source_volumes->{$opt}};
5586 if ($use_existing) {
5587 $nbd->{$opt}->{drivestr
} = print_drive
($drive);
5588 $nbd->{$opt}->{volid
} = $volid;
5589 $nbd->{$opt}->{replicated
} = 1;
5593 $storeid = PVE
::JSONSchema
::map_id
($storagemap, $storeid);
5595 # order of precedence, filtered by whether storage supports it:
5596 # 1. explicit requested format
5597 # 2. default format of storage
5598 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
5599 $format = $defFormat if !$format || !grep { $format eq $_ } $validFormats->@*;
5601 my $size = $drive->{size
} / 1024;
5602 my $newvolid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $format, undef, $size);
5603 my $newdrive = $drive;
5604 $newdrive->{format
} = $format;
5605 $newdrive->{file
} = $newvolid;
5606 my $drivestr = print_drive
($newdrive);
5607 $nbd->{$opt}->{drivestr
} = $drivestr;
5608 $nbd->{$opt}->{volid
} = $newvolid;
5614 # see vm_start_nolock for parameters, additionally:
5616 # storagemap = parsed storage map for allocating NBD disks
5618 my ($storecfg, $vmid, $params, $migrate_opts) = @_;
5620 return PVE
::QemuConfig-
>lock_config($vmid, sub {
5621 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migrate_opts->{migratedfrom
});
5623 die "you can't start a vm if it's a template\n"
5624 if !$params->{skiptemplate
} && PVE
::QemuConfig-
>is_template($conf);
5626 my $has_suspended_lock = PVE
::QemuConfig-
>has_lock($conf, 'suspended');
5627 my $has_backup_lock = PVE
::QemuConfig-
>has_lock($conf, 'backup');
5629 my $running = check_running
($vmid, undef, $migrate_opts->{migratedfrom
});
5631 if ($has_backup_lock && $running) {
5632 # a backup is currently running, attempt to start the guest in the
5633 # existing QEMU instance
5634 return vm_resume
($vmid);
5637 PVE
::QemuConfig-
>check_lock($conf)
5638 if !($params->{skiplock
} || $has_suspended_lock);
5640 $params->{resume
} = $has_suspended_lock || defined($conf->{vmstate
});
5642 die "VM $vmid already running\n" if $running;
5644 if (my $storagemap = $migrate_opts->{storagemap
}) {
5645 my $replicated = $migrate_opts->{replicated_volumes
};
5646 my $disks = vm_migrate_get_nbd_disks
($storecfg, $conf, $replicated);
5647 $migrate_opts->{nbd
} = vm_migrate_alloc_nbd_disks
($storecfg, $vmid, $disks, $storagemap);
5649 foreach my $opt (keys %{$migrate_opts->{nbd
}}) {
5650 $conf->{$opt} = $migrate_opts->{nbd
}->{$opt}->{drivestr
};
5654 return vm_start_nolock
($storecfg, $vmid, $conf, $params, $migrate_opts);
5660 # statefile => 'tcp', 'unix' for migration or path/volid for RAM state
5661 # skiplock => 0/1, skip checking for config lock
5662 # skiptemplate => 0/1, skip checking whether VM is template
5663 # forcemachine => to force QEMU machine (rollback/migration)
5664 # forcecpu => a QEMU '-cpu' argument string to override get_cpu_options
5665 # timeout => in seconds
5666 # paused => start VM in paused state (backup)
5667 # resume => resume from hibernation
5678 # nbd => volumes for NBD exports (vm_migrate_alloc_nbd_disks)
5679 # migratedfrom => source node
5680 # spice_ticket => used for spice migration, passed via tunnel/stdin
5681 # network => CIDR of migration network
5682 # type => secure/insecure - tunnel over encrypted connection or plain-text
5683 # nbd_proto_version => int, 0 for TCP, 1 for UNIX
5684 # replicated_volumes => which volids should be re-used with bitmaps for nbd migration
5685 # offline_volumes => new volids of offline migrated disks like tpmstate and cloudinit, not yet
5686 # contained in config
5687 sub vm_start_nolock
{
5688 my ($storecfg, $vmid, $conf, $params, $migrate_opts) = @_;
5690 my $statefile = $params->{statefile
};
5691 my $resume = $params->{resume
};
5693 my $migratedfrom = $migrate_opts->{migratedfrom
};
5694 my $migration_type = $migrate_opts->{type
};
5698 # clean up leftover reboot request files
5699 eval { clear_reboot_request
($vmid); };
5702 if (!$statefile && scalar(keys %{$conf->{pending
}})) {
5703 vmconfig_apply_pending
($vmid, $conf, $storecfg);
5704 $conf = PVE
::QemuConfig-
>load_config($vmid); # update/reload
5707 # don't regenerate the ISO if the VM is started as part of a live migration
5708 # this way we can reuse the old ISO with the correct config
5709 if (!$migratedfrom) {
5710 if (PVE
::QemuServer
::Cloudinit
::apply_cloudinit_config
($conf, $vmid)) {
5711 # FIXME: apply_cloudinit_config updates $conf in this case, and it would only drop
5712 # $conf->{cloudinit}, so we could just not do this?
5713 # But we do it above, so for now let's be consistent.
5714 $conf = PVE
::QemuConfig-
>load_config($vmid); # update/reload
5718 # override offline migrated volumes, conf is out of date still
5719 if (my $offline_volumes = $migrate_opts->{offline_volumes
}) {
5720 for my $key (sort keys $offline_volumes->%*) {
5721 my $parsed = parse_drive
($key, $conf->{$key});
5722 $parsed->{file
} = $offline_volumes->{$key};
5723 $conf->{$key} = print_drive
($parsed);
5727 my $defaults = load_defaults
();
5729 # set environment variable useful inside network script
5730 # for remote migration the config is available on the target node!
5731 if (!$migrate_opts->{remote_node
}) {
5732 $ENV{PVE_MIGRATED_FROM
} = $migratedfrom;
5735 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-start', 1);
5737 my $forcemachine = $params->{forcemachine
};
5738 my $forcecpu = $params->{forcecpu
};
5740 # enforce machine and CPU type on suspended vm to ensure HW compatibility
5741 $forcemachine = $conf->{runningmachine
};
5742 $forcecpu = $conf->{runningcpu
};
5743 print "Resuming suspended VM\n";
5746 my ($cmd, $vollist, $spice_port, $pci_devices) = config_to_command
($storecfg, $vmid,
5747 $conf, $defaults, $forcemachine, $forcecpu, $params->{'pbs-backing'});
5750 my $get_migration_ip = sub {
5751 my ($nodename) = @_;
5753 return $migration_ip if defined($migration_ip);
5755 my $cidr = $migrate_opts->{network
};
5757 if (!defined($cidr)) {
5758 my $dc_conf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5759 $cidr = $dc_conf->{migration
}->{network
};
5762 if (defined($cidr)) {
5763 my $ips = PVE
::Network
::get_local_ip_from_cidr
($cidr);
5765 die "could not get IP: no address configured on local " .
5766 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5768 die "could not get IP: multiple addresses configured on local " .
5769 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5771 $migration_ip = @$ips[0];
5774 $migration_ip = PVE
::Cluster
::remote_node_ip
($nodename, 1)
5775 if !defined($migration_ip);
5777 return $migration_ip;
5781 if ($statefile eq 'tcp') {
5782 my $migrate = $res->{migrate
} = { proto
=> 'tcp' };
5783 $migrate->{addr
} = "localhost";
5784 my $datacenterconf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5785 my $nodename = nodename
();
5787 if (!defined($migration_type)) {
5788 if (defined($datacenterconf->{migration
}->{type
})) {
5789 $migration_type = $datacenterconf->{migration
}->{type
};
5791 $migration_type = 'secure';
5795 if ($migration_type eq 'insecure') {
5796 $migrate->{addr
} = $get_migration_ip->($nodename);
5797 $migrate->{addr
} = "[$migrate->{addr}]" if Net
::IP
::ip_is_ipv6
($migrate->{addr
});
5800 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
5801 $migrate->{port
} = PVE
::Tools
::next_migrate_port
($pfamily);
5802 $migrate->{uri
} = "tcp:$migrate->{addr}:$migrate->{port}";
5803 push @$cmd, '-incoming', $migrate->{uri
};
5806 } elsif ($statefile eq 'unix') {
5807 # should be default for secure migrations as a ssh TCP forward
5808 # tunnel is not deterministic reliable ready and fails regurarly
5809 # to set up in time, so use UNIX socket forwards
5810 my $migrate = $res->{migrate
} = { proto
=> 'unix' };
5811 $migrate->{addr
} = "/run/qemu-server/$vmid.migrate";
5812 unlink $migrate->{addr
};
5814 $migrate->{uri
} = "unix:$migrate->{addr}";
5815 push @$cmd, '-incoming', $migrate->{uri
};
5818 } elsif (-e
$statefile) {
5819 push @$cmd, '-loadstate', $statefile;
5821 my $statepath = PVE
::Storage
::path
($storecfg, $statefile);
5822 push @$vollist, $statefile;
5823 push @$cmd, '-loadstate', $statepath;
5825 } elsif ($params->{paused
}) {
5829 my $start_timeout = $params->{timeout
} // config_aware_timeout
($conf, $resume);
5831 my $pci_reserve_list = [];
5832 for my $device (values $pci_devices->%*) {
5833 next if $device->{mdev
}; # we don't reserve for mdev devices
5834 push $pci_reserve_list->@*, map { $_->{id
} } $device->{ids
}->@*;
5837 # reserve all PCI IDs before actually doing anything with them
5838 PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_reserve_list, $vmid, $start_timeout);
5842 for my $id (sort keys %$pci_devices) {
5843 my $d = $pci_devices->{$id};
5844 my ($index) = ($id =~ m/^hostpci(\d+)$/);
5847 for my $dev ($d->{ids
}->@*) {
5848 my $info = eval { PVE
::QemuServer
::PCI
::prepare_pci_device
($vmid, $dev->{id
}, $index, $d->{mdev
}) };
5851 $chosen_mdev = $info;
5852 last if $chosen_mdev; # if successful, we're done
5858 next if !$d->{mdev
};
5859 die "could not create mediated device\n" if !defined($chosen_mdev);
5861 # nvidia grid needs the uuid of the mdev as qemu parameter
5862 if (!defined($uuid) && $chosen_mdev->{vendor
} =~ m/^(0x)?10de$/) {
5863 if (defined($conf->{smbios1
})) {
5864 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
5865 $uuid = $smbios_conf->{uuid
} if defined($smbios_conf->{uuid
});
5867 $uuid = PVE
::QemuServer
::PCI
::generate_mdev_uuid
($vmid, $index) if !defined($uuid);
5870 push @$cmd, '-uuid', $uuid if defined($uuid);
5873 eval { cleanup_pci_devices
($vmid, $conf) };
5878 PVE
::Storage
::activate_volumes
($storecfg, $vollist);
5881 my %silence_std_outs = (outfunc
=> sub {}, errfunc
=> sub {});
5882 eval { run_command
(['/bin/systemctl', 'reset-failed', "$vmid.scope"], %silence_std_outs) };
5883 eval { run_command
(['/bin/systemctl', 'stop', "$vmid.scope"], %silence_std_outs) };
5884 # Issues with the above 'stop' not being fully completed are extremely rare, a very low
5885 # timeout should be more than enough here...
5886 PVE
::Systemd
::wait_for_unit_removed
("$vmid.scope", 20);
5888 my $cpuunits = PVE
::CGroup
::clamp_cpu_shares
($conf->{cpuunits
});
5891 timeout
=> $statefile ?
undef : $start_timeout,
5896 # when migrating, prefix QEMU output so other side can pick up any
5897 # errors that might occur and show the user
5898 if ($migratedfrom) {
5899 $run_params{quiet
} = 1;
5900 $run_params{logfunc
} = sub { print "QEMU: $_[0]\n" };
5903 my %systemd_properties = (
5904 Slice
=> 'qemu.slice',
5905 KillMode
=> 'process',
5907 TimeoutStopUSec
=> ULONG_MAX
, # infinity
5910 if (PVE
::CGroup
::cgroup_mode
() == 2) {
5911 $systemd_properties{CPUWeight
} = $cpuunits;
5913 $systemd_properties{CPUShares
} = $cpuunits;
5916 if (my $cpulimit = $conf->{cpulimit
}) {
5917 $systemd_properties{CPUQuota
} = int($cpulimit * 100);
5919 $systemd_properties{timeout
} = 10 if $statefile; # setting up the scope shoul be quick
5921 my $run_qemu = sub {
5922 PVE
::Tools
::run_fork
sub {
5923 PVE
::Systemd
::enter_systemd_scope
($vmid, "Proxmox VE VM $vmid", %systemd_properties);
5926 if (my $tpm = $conf->{tpmstate0
}) {
5927 # start the TPM emulator so QEMU can connect on start
5928 $tpmpid = start_swtpm
($storecfg, $vmid, $tpm, $migratedfrom);
5931 my $exitcode = run_command
($cmd, %run_params);
5934 warn "stopping swtpm instance (pid $tpmpid) due to QEMU startup error\n";
5935 kill 'TERM', $tpmpid;
5937 die "QEMU exited with code $exitcode\n";
5942 if ($conf->{hugepages
}) {
5945 my $hotplug_features =
5946 parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
5947 my $hugepages_topology =
5948 PVE
::QemuServer
::Memory
::hugepages_topology
($conf, $hotplug_features->{memory
});
5950 my $hugepages_host_topology = PVE
::QemuServer
::Memory
::hugepages_host_topology
();
5952 PVE
::QemuServer
::Memory
::hugepages_mount
();
5953 PVE
::QemuServer
::Memory
::hugepages_allocate
($hugepages_topology, $hugepages_host_topology);
5955 eval { $run_qemu->() };
5957 PVE
::QemuServer
::Memory
::hugepages_reset
($hugepages_host_topology)
5958 if !$conf->{keephugepages
};
5962 PVE
::QemuServer
::Memory
::hugepages_pre_deallocate
($hugepages_topology)
5963 if !$conf->{keephugepages
};
5965 eval { PVE
::QemuServer
::Memory
::hugepages_update_locked
($code); };
5968 eval { $run_qemu->() };
5972 # deactivate volumes if start fails
5973 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
5975 eval { cleanup_pci_devices
($vmid, $conf) };
5978 die "start failed: $err";
5981 # re-reserve all PCI IDs now that we can know the actual VM PID
5982 my $pid = PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
5983 eval { PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_reserve_list, $vmid, undef, $pid) };
5986 if (defined($res->{migrate
})) {
5987 print "migration listens on $res->{migrate}->{uri}\n";
5988 } elsif ($statefile) {
5989 eval { mon_cmd
($vmid, "cont"); };
5993 #start nbd server for storage migration
5994 if (my $nbd = $migrate_opts->{nbd
}) {
5995 my $nbd_protocol_version = $migrate_opts->{nbd_proto_version
} // 0;
5997 my $migrate_storage_uri;
5998 # nbd_protocol_version > 0 for unix socket support
5999 if ($nbd_protocol_version > 0 && ($migration_type eq 'secure' || $migration_type eq 'websocket')) {
6000 my $socket_path = "/run/qemu-server/$vmid\_nbd.migrate";
6001 mon_cmd
($vmid, "nbd-server-start", addr
=> { type
=> 'unix', data
=> { path
=> $socket_path } } );
6002 $migrate_storage_uri = "nbd:unix:$socket_path";
6003 $res->{migrate
}->{unix_sockets
} = [$socket_path];
6005 my $nodename = nodename
();
6006 my $localip = $get_migration_ip->($nodename);
6007 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
6008 my $storage_migrate_port = PVE
::Tools
::next_migrate_port
($pfamily);
6010 mon_cmd
($vmid, "nbd-server-start", addr
=> {
6013 host
=> "${localip}",
6014 port
=> "${storage_migrate_port}",
6017 $localip = "[$localip]" if Net
::IP
::ip_is_ipv6
($localip);
6018 $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}";
6021 my $block_info = mon_cmd
($vmid, "query-block");
6022 $block_info = { map { $_->{device
} => $_ } $block_info->@* };
6024 foreach my $opt (sort keys %$nbd) {
6025 my $drivestr = $nbd->{$opt}->{drivestr
};
6026 my $volid = $nbd->{$opt}->{volid
};
6028 my $block_node = $block_info->{"drive-$opt"}->{inserted
}->{'node-name'};
6034 'node-name' => $block_node,
6035 writable
=> JSON
::true
,
6037 name
=> "drive-$opt", # NBD export name
6040 my $nbd_uri = "$migrate_storage_uri:exportname=drive-$opt";
6041 print "storage migration listens on $nbd_uri volume:$drivestr\n";
6042 print "re-using replicated volume: $opt - $volid\n"
6043 if $nbd->{$opt}->{replicated
};
6045 $res->{drives
}->{$opt} = $nbd->{$opt};
6046 $res->{drives
}->{$opt}->{nbd_uri
} = $nbd_uri;
6050 if ($migratedfrom) {
6052 set_migration_caps
($vmid);
6057 print "spice listens on port $spice_port\n";
6058 $res->{spice_port
} = $spice_port;
6059 if ($migrate_opts->{spice_ticket
}) {
6060 mon_cmd
($vmid, "set_password", protocol
=> 'spice', password
=>
6061 $migrate_opts->{spice_ticket
});
6062 mon_cmd
($vmid, "expire_password", protocol
=> 'spice', time => "+30");
6067 mon_cmd
($vmid, "balloon", value
=> $conf->{balloon
}*1024*1024)
6068 if !$statefile && $conf->{balloon
};
6070 foreach my $opt (keys %$conf) {
6071 next if $opt !~ m/^net\d+$/;
6072 my $nicconf = parse_net
($conf->{$opt});
6073 qemu_set_link_status
($vmid, $opt, 0) if $nicconf->{link_down
};
6075 add_nets_bridge_fdb
($conf, $vmid);
6078 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
6083 path
=> "machine/peripheral/balloon0",
6084 property
=> "guest-stats-polling-interval",
6088 log_warn
("could not set polling interval for ballooning - $@") if $@;
6092 print "Resumed VM, removing state\n";
6093 if (my $vmstate = $conf->{vmstate
}) {
6094 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
6095 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
6097 delete $conf->@{qw(lock vmstate runningmachine runningcpu)};
6098 PVE
::QemuConfig-
>write_config($vmid, $conf);
6101 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'post-start');
6106 sub vm_commandline
{
6107 my ($storecfg, $vmid, $snapname) = @_;
6109 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6111 my ($forcemachine, $forcecpu);
6113 my $snapshot = $conf->{snapshots
}->{$snapname};
6114 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
6116 # check for machine or CPU overrides in snapshot
6117 $forcemachine = $snapshot->{runningmachine
};
6118 $forcecpu = $snapshot->{runningcpu
};
6120 $snapshot->{digest
} = $conf->{digest
}; # keep file digest for API
6125 my $defaults = load_defaults
();
6127 my $cmd = config_to_command
($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu);
6129 return PVE
::Tools
::cmd2string
($cmd);
6133 my ($vmid, $skiplock) = @_;
6135 PVE
::QemuConfig-
>lock_config($vmid, sub {
6137 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6139 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
6141 mon_cmd
($vmid, "system_reset");
6145 sub get_vm_volumes
{
6149 foreach_volid
($conf, sub {
6150 my ($volid, $attr) = @_;
6152 return if $volid =~ m
|^/|;
6154 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
6157 push @$vollist, $volid;
6163 sub cleanup_pci_devices
{
6164 my ($vmid, $conf) = @_;
6166 foreach my $key (keys %$conf) {
6167 next if $key !~ m/^hostpci(\d+)$/;
6168 my $hostpciindex = $1;
6169 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $hostpciindex);
6170 my $d = parse_hostpci
($conf->{$key});
6172 # NOTE: avoid PVE::SysFSTools::pci_cleanup_mdev_device as it requires PCI ID and we
6173 # don't want to break ABI just for this two liner
6174 my $dev_sysfs_dir = "/sys/bus/mdev/devices/$uuid";
6176 # some nvidia vgpu driver versions want to clean the mdevs up themselves, and error
6177 # out when we do it first. so wait for 10 seconds and then try it
6178 if ($d->{ids
}->[0]->[0]->{vendor
} =~ m/^(0x)?10de$/) {
6182 PVE
::SysFSTools
::file_write
("$dev_sysfs_dir/remove", "1") if -e
$dev_sysfs_dir;
6185 PVE
::QemuServer
::PCI
::remove_pci_reservation
($vmid);
6188 sub vm_stop_cleanup
{
6189 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
6194 my $vollist = get_vm_volumes
($conf);
6195 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
6197 if (my $tpmdrive = $conf->{tpmstate0
}) {
6198 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
6199 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
6201 PVE
::Storage
::unmap_volume
($storecfg, $tpm->{file
});
6206 foreach my $ext (qw(mon qmp pid vnc qga)) {
6207 unlink "/var/run/qemu-server/${vmid}.$ext";
6210 if ($conf->{ivshmem
}) {
6211 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
6212 # just delete it for now, VMs which have this already open do not
6213 # are affected, but new VMs will get a separated one. If this
6214 # becomes an issue we either add some sort of ref-counting or just
6215 # add a "don't delete on stop" flag to the ivshmem format.
6216 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name
} // $vmid);
6219 cleanup_pci_devices
($vmid, $conf);
6221 vmconfig_apply_pending
($vmid, $conf, $storecfg) if $apply_pending_changes;
6223 warn $@ if $@; # avoid errors - just warn
6226 # call only in locked context
6228 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
6230 my $pid = check_running
($vmid, $nocheck);
6235 $conf = PVE
::QemuConfig-
>load_config($vmid);
6236 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
6237 if (!defined($timeout) && $shutdown && $conf->{startup
}) {
6238 my $opts = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
6239 $timeout = $opts->{down
} if $opts->{down
};
6241 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-stop');
6246 if (defined($conf) && get_qga_key
($conf, 'enabled')) {
6247 mon_cmd
($vmid, "guest-shutdown", timeout
=> $timeout);
6249 mon_cmd
($vmid, "system_powerdown");
6252 mon_cmd
($vmid, "quit");
6258 $timeout = 60 if !defined($timeout);
6261 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
6266 if ($count >= $timeout) {
6268 warn "VM still running - terminating now with SIGTERM\n";
6271 die "VM quit/powerdown failed - got timeout\n";
6274 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6278 if (!check_running
($vmid, $nocheck)) {
6279 warn "Unexpected: VM shutdown command failed, but VM not running anymore..\n";
6283 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
6286 die "VM quit/powerdown failed\n";
6294 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
6299 if ($count >= $timeout) {
6300 warn "VM still running - terminating now with SIGKILL\n";
6305 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6308 # Note: use $nocheck to skip tests if VM configuration file exists.
6309 # We need that when migration VMs to other nodes (files already moved)
6310 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
6312 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
6314 $force = 1 if !defined($force) && !$shutdown;
6317 my $pid = check_running
($vmid, $nocheck, $migratedfrom);
6318 kill 15, $pid if $pid;
6319 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migratedfrom);
6320 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 0);
6324 PVE
::QemuConfig-
>lock_config($vmid, sub {
6325 _do_vm_stop
($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
6330 my ($vmid, $timeout) = @_;
6332 PVE
::QemuConfig-
>lock_config($vmid, sub {
6335 # only reboot if running, as qmeventd starts it again on a stop event
6336 return if !check_running
($vmid);
6338 create_reboot_request
($vmid);
6340 my $storecfg = PVE
::Storage
::config
();
6341 _do_vm_stop
($storecfg, $vmid, undef, undef, $timeout, 1);
6345 # avoid that the next normal shutdown will be confused for a reboot
6346 clear_reboot_request
($vmid);
6352 # note: if using the statestorage parameter, the caller has to check privileges
6354 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
6361 PVE
::QemuConfig-
>lock_config($vmid, sub {
6363 $conf = PVE
::QemuConfig-
>load_config($vmid);
6365 my $is_backing_up = PVE
::QemuConfig-
>has_lock($conf, 'backup');
6366 PVE
::QemuConfig-
>check_lock($conf)
6367 if !($skiplock || $is_backing_up);
6369 die "cannot suspend to disk during backup\n"
6370 if $is_backing_up && $includestate;
6372 if ($includestate) {
6373 $conf->{lock} = 'suspending';
6374 my $date = strftime
("%Y-%m-%d", localtime(time()));
6375 $storecfg = PVE
::Storage
::config
();
6376 if (!$statestorage) {
6377 $statestorage = find_vmstate_storage
($conf, $storecfg);
6378 # check permissions for the storage
6379 my $rpcenv = PVE
::RPCEnvironment
::get
();
6380 if ($rpcenv->{type
} ne 'cli') {
6381 my $authuser = $rpcenv->get_user();
6382 $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
6387 $vmstate = PVE
::QemuConfig-
>__snapshot_save_vmstate(
6388 $vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
6389 $path = PVE
::Storage
::path
($storecfg, $vmstate);
6390 PVE
::QemuConfig-
>write_config($vmid, $conf);
6392 mon_cmd
($vmid, "stop");
6396 if ($includestate) {
6398 PVE
::Storage
::activate_volumes
($storecfg, [$vmstate]);
6401 set_migration_caps
($vmid, 1);
6402 mon_cmd
($vmid, "savevm-start", statefile
=> $path);
6404 my $state = mon_cmd
($vmid, "query-savevm");
6405 if (!$state->{status
}) {
6406 die "savevm not active\n";
6407 } elsif ($state->{status
} eq 'active') {
6410 } elsif ($state->{status
} eq 'completed') {
6411 print "State saved, quitting\n";
6413 } elsif ($state->{status
} eq 'failed' && $state->{error
}) {
6414 die "query-savevm failed with error '$state->{error}'\n"
6416 die "query-savevm returned status '$state->{status}'\n";
6422 PVE
::QemuConfig-
>lock_config($vmid, sub {
6423 $conf = PVE
::QemuConfig-
>load_config($vmid);
6425 # cleanup, but leave suspending lock, to indicate something went wrong
6427 mon_cmd
($vmid, "savevm-end");
6428 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
6429 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
6430 delete $conf->@{qw(vmstate runningmachine runningcpu)};
6431 PVE
::QemuConfig-
>write_config($vmid, $conf);
6437 die "lock changed unexpectedly\n"
6438 if !PVE
::QemuConfig-
>has_lock($conf, 'suspending');
6440 mon_cmd
($vmid, "quit");
6441 $conf->{lock} = 'suspended';
6442 PVE
::QemuConfig-
>write_config($vmid, $conf);
6447 # $nocheck is set when called as part of a migration - in this context the
6448 # location of the config file (source or target node) is not deterministic,
6449 # since migration cannot wait for pmxcfs to process the rename
6451 my ($vmid, $skiplock, $nocheck) = @_;
6453 PVE
::QemuConfig-
>lock_config($vmid, sub {
6454 my $res = mon_cmd
($vmid, 'query-status');
6455 my $resume_cmd = 'cont';
6459 $conf = eval { PVE
::QemuConfig-
>load_config($vmid) }; # try on target node
6461 my $vmlist = PVE
::Cluster
::get_vmlist
();
6462 if (exists($vmlist->{ids
}->{$vmid})) {
6463 my $node = $vmlist->{ids
}->{$vmid}->{node
};
6464 $conf = eval { PVE
::QemuConfig-
>load_config($vmid, $node) }; # try on source node
6467 PVE
::Cluster
::cfs_update
(); # vmlist was wrong, invalidate cache
6468 $conf = PVE
::QemuConfig-
>load_config($vmid); # last try on target node again
6472 $conf = PVE
::QemuConfig-
>load_config($vmid);
6475 if ($res->{status
}) {
6476 return if $res->{status
} eq 'running'; # job done, go home
6477 $resume_cmd = 'system_wakeup' if $res->{status
} eq 'suspended';
6478 $reset = 1 if $res->{status
} eq 'shutdown';
6482 PVE
::QemuConfig-
>check_lock($conf)
6483 if !($skiplock || PVE
::QemuConfig-
>has_lock($conf, 'backup'));
6487 # required if a VM shuts down during a backup and we get a resume
6488 # request before the backup finishes for example
6489 mon_cmd
($vmid, "system_reset");
6492 add_nets_bridge_fdb
($conf, $vmid) if $resume_cmd eq 'cont';
6494 mon_cmd
($vmid, $resume_cmd);
6499 my ($vmid, $skiplock, $key) = @_;
6501 PVE
::QemuConfig-
>lock_config($vmid, sub {
6503 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6505 # there is no qmp command, so we use the human monitor command
6506 my $res = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "sendkey $key");
6507 die $res if $res ne '';
6511 sub check_bridge_access
{
6512 my ($rpcenv, $authuser, $conf) = @_;
6514 return 1 if $authuser eq 'root@pam';
6516 for my $opt (sort keys $conf->%*) {
6517 next if $opt !~ m/^net\d+$/;
6518 my $net = parse_net
($conf->{$opt});
6519 my ($bridge, $tag, $trunks) = $net->@{'bridge', 'tag', 'trunks'};
6520 PVE
::GuestHelpers
::check_vnet_access
($rpcenv, $authuser, $bridge, $tag, $trunks);
6525 sub check_mapping_access
{
6526 my ($rpcenv, $user, $conf) = @_;
6528 for my $opt (keys $conf->%*) {
6529 if ($opt =~ m/^usb\d+$/) {
6530 my $device = PVE
::JSONSchema
::parse_property_string
('pve-qm-usb', $conf->{$opt});
6531 if (my $host = $device->{host
}) {
6532 die "only root can set '$opt' config for real devices\n"
6533 if $host !~ m/^spice$/i && $user ne 'root@pam';
6534 } elsif ($device->{mapping
}) {
6535 $rpcenv->check_full($user, "/mapping/usb/$device->{mapping}", ['Mapping.Use']);
6537 die "either 'host' or 'mapping' must be set.\n";
6539 } elsif ($opt =~ m/^hostpci\d+$/) {
6540 my $device = PVE
::JSONSchema
::parse_property_string
('pve-qm-hostpci', $conf->{$opt});
6541 if ($device->{host
}) {
6542 die "only root can set '$opt' config for non-mapped devices\n" if $user ne 'root@pam';
6543 } elsif ($device->{mapping
}) {
6544 $rpcenv->check_full($user, "/mapping/pci/$device->{mapping}", ['Mapping.Use']);
6546 die "either 'host' or 'mapping' must be set.\n";
6552 sub check_restore_permissions
{
6553 my ($rpcenv, $user, $conf) = @_;
6555 check_bridge_access
($rpcenv, $user, $conf);
6556 check_mapping_access
($rpcenv, $user, $conf);
6558 # vzdump restore implementaion
6560 sub tar_archive_read_firstfile
{
6561 my $archive = shift;
6563 die "ERROR: file '$archive' does not exist\n" if ! -f
$archive;
6565 # try to detect archive type first
6566 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
6567 die "unable to open file '$archive'\n";
6568 my $firstfile = <$fh>;
6572 die "ERROR: archive contaions no data\n" if !$firstfile;
6578 sub tar_restore_cleanup
{
6579 my ($storecfg, $statfile) = @_;
6581 print STDERR
"starting cleanup\n";
6583 if (my $fd = IO
::File-
>new($statfile, "r")) {
6584 while (defined(my $line = <$fd>)) {
6585 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6588 if ($volid =~ m
|^/|) {
6589 unlink $volid || die 'unlink failed\n';
6591 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6593 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6595 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6597 print STDERR
"unable to parse line in statfile - $line";
6604 sub restore_file_archive
{
6605 my ($archive, $vmid, $user, $opts) = @_;
6607 return restore_vma_archive
($archive, $vmid, $user, $opts)
6610 my $info = PVE
::Storage
::archive_info
($archive);
6611 my $format = $opts->{format
} // $info->{format
};
6612 my $comp = $info->{compression
};
6614 # try to detect archive format
6615 if ($format eq 'tar') {
6616 return restore_tar_archive
($archive, $vmid, $user, $opts);
6618 return restore_vma_archive
($archive, $vmid, $user, $opts, $comp);
6622 # hepler to remove disks that will not be used after restore
6623 my $restore_cleanup_oldconf = sub {
6624 my ($storecfg, $vmid, $oldconf, $virtdev_hash) = @_;
6626 my $kept_disks = {};
6628 PVE
::QemuConfig-
>foreach_volume($oldconf, sub {
6629 my ($ds, $drive) = @_;
6631 return if drive_is_cdrom
($drive, 1);
6633 my $volid = $drive->{file
};
6634 return if !$volid || $volid =~ m
|^/|;
6636 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
6637 return if !$path || !$owner || ($owner != $vmid);
6639 # Note: only delete disk we want to restore
6640 # other volumes will become unused
6641 if ($virtdev_hash->{$ds}) {
6642 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
6647 $kept_disks->{$volid} = 1;
6651 # after the restore we have no snapshots anymore
6652 for my $snapname (keys $oldconf->{snapshots
}->%*) {
6653 my $snap = $oldconf->{snapshots
}->{$snapname};
6654 if ($snap->{vmstate
}) {
6655 eval { PVE
::Storage
::vdisk_free
($storecfg, $snap->{vmstate
}); };
6661 for my $volid (keys $kept_disks->%*) {
6662 eval { PVE
::Storage
::volume_snapshot_delete
($storecfg, $volid, $snapname); };
6668 # Helper to parse vzdump backup device hints
6670 # $rpcenv: Environment, used to ckeck storage permissions
6671 # $user: User ID, to check storage permissions
6672 # $storecfg: Storage configuration
6673 # $fh: the file handle for reading the configuration
6674 # $devinfo: should contain device sizes for all backu-up'ed devices
6675 # $options: backup options (pool, default storage)
6677 # Return: $virtdev_hash, updates $devinfo (add devname, virtdev, format, storeid)
6678 my $parse_backup_hints = sub {
6679 my ($rpcenv, $user, $storecfg, $fh, $devinfo, $options) = @_;
6681 my $check_storage = sub { # assert if an image can be allocate
6682 my ($storeid, $scfg) = @_;
6683 die "Content type 'images' is not available on storage '$storeid'\n"
6684 if !$scfg->{content
}->{images
};
6685 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace'])
6686 if $user ne 'root@pam';
6689 my $virtdev_hash = {};
6690 while (defined(my $line = <$fh>)) {
6691 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
6692 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
6693 die "archive does not contain data for drive '$virtdev'\n"
6694 if !$devinfo->{$devname};
6696 if (defined($options->{storage
})) {
6697 $storeid = $options->{storage
} || 'local';
6698 } elsif (!$storeid) {
6701 $format = 'raw' if !$format;
6702 $devinfo->{$devname}->{devname
} = $devname;
6703 $devinfo->{$devname}->{virtdev
} = $virtdev;
6704 $devinfo->{$devname}->{format
} = $format;
6705 $devinfo->{$devname}->{storeid
} = $storeid;
6707 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6708 $check_storage->($storeid, $scfg); # permission and content type check
6710 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
6711 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
6713 my $drive = parse_drive
($virtdev, $2);
6715 if (drive_is_cloudinit
($drive)) {
6716 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
6717 $storeid = $options->{storage
} if defined ($options->{storage
});
6718 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6719 my $format = qemu_img_format
($scfg, $volname); # has 'raw' fallback
6721 $check_storage->($storeid, $scfg); # permission and content type check
6723 $virtdev_hash->{$virtdev} = {
6725 storeid
=> $storeid,
6726 size
=> PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
,
6733 return $virtdev_hash;
6736 # Helper to allocate and activate all volumes required for a restore
6738 # $storecfg: Storage configuration
6739 # $virtdev_hash: as returned by parse_backup_hints()
6741 # Returns: { $virtdev => $volid }
6742 my $restore_allocate_devices = sub {
6743 my ($storecfg, $virtdev_hash, $vmid) = @_;
6746 foreach my $virtdev (sort keys %$virtdev_hash) {
6747 my $d = $virtdev_hash->{$virtdev};
6748 my $alloc_size = int(($d->{size
} + 1024 - 1)/1024);
6749 my $storeid = $d->{storeid
};
6750 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6752 # test if requested format is supported
6753 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
6754 my $supported = grep { $_ eq $d->{format
} } @$validFormats;
6755 $d->{format
} = $defFormat if !$supported;
6758 if ($d->{is_cloudinit
}) {
6759 $name = "vm-$vmid-cloudinit";
6760 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6761 if ($scfg->{path
}) {
6762 $name .= ".$d->{format}";
6766 my $volid = PVE
::Storage
::vdisk_alloc
(
6767 $storecfg, $storeid, $vmid, $d->{format
}, $name, $alloc_size);
6769 print STDERR
"new volume ID is '$volid'\n";
6770 $d->{volid
} = $volid;
6772 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
6774 $map->{$virtdev} = $volid;
6780 sub restore_update_config_line
{
6781 my ($cookie, $map, $line, $unique) = @_;
6783 return '' if $line =~ m/^\#qmdump\#/;
6784 return '' if $line =~ m/^\#vzdump\#/;
6785 return '' if $line =~ m/^lock:/;
6786 return '' if $line =~ m/^unused\d+:/;
6787 return '' if $line =~ m/^parent:/;
6791 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
6792 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
6793 # try to convert old 1.X settings
6794 my ($id, $ind, $ethcfg) = ($1, $2, $3);
6795 foreach my $devconfig (PVE
::Tools
::split_list
($ethcfg)) {
6796 my ($model, $macaddr) = split(/\=/, $devconfig);
6797 $macaddr = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if !$macaddr || $unique;
6800 bridge
=> "vmbr$ind",
6801 macaddr
=> $macaddr,
6803 my $netstr = print_net
($net);
6805 $res .= "net$cookie->{netcount}: $netstr\n";
6806 $cookie->{netcount
}++;
6808 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
6809 my ($id, $netstr) = ($1, $2);
6810 my $net = parse_net
($netstr);
6811 $net->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if $net->{macaddr
};
6812 $netstr = print_net
($net);
6813 $res .= "$id: $netstr\n";
6814 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk|tpmstate)\d+):\s*(\S+)\s*$/) {
6817 my $di = parse_drive
($virtdev, $value);
6818 if (defined($di->{backup
}) && !$di->{backup
}) {
6820 } elsif ($map->{$virtdev}) {
6821 delete $di->{format
}; # format can change on restore
6822 $di->{file
} = $map->{$virtdev};
6823 $value = print_drive
($di);
6824 $res .= "$virtdev: $value\n";
6828 } elsif (($line =~ m/^vmgenid: (.*)/)) {
6830 if ($vmgenid ne '0') {
6831 # always generate a new vmgenid if there was a valid one setup
6832 $vmgenid = generate_uuid
();
6834 $res .= "vmgenid: $vmgenid\n";
6835 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
6836 my ($uuid, $uuid_str);
6837 UUID
::generate
($uuid);
6838 UUID
::unparse
($uuid, $uuid_str);
6839 my $smbios1 = parse_smbios1
($2);
6840 $smbios1->{uuid
} = $uuid_str;
6841 $res .= $1.print_smbios1
($smbios1)."\n";
6849 my $restore_deactivate_volumes = sub {
6850 my ($storecfg, $virtdev_hash) = @_;
6853 for my $dev (values $virtdev_hash->%*) {
6854 push $vollist->@*, $dev->{volid
} if $dev->{volid
};
6857 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
6858 print STDERR
$@ if $@;
6861 my $restore_destroy_volumes = sub {
6862 my ($storecfg, $virtdev_hash) = @_;
6864 for my $dev (values $virtdev_hash->%*) {
6865 my $volid = $dev->{volid
} or next;
6867 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6868 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6870 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6874 sub restore_merge_config
{
6875 my ($filename, $backup_conf_raw, $override_conf) = @_;
6877 my $backup_conf = parse_vm_config
($filename, $backup_conf_raw);
6878 for my $key (keys $override_conf->%*) {
6879 $backup_conf->{$key} = $override_conf->{$key};
6882 return $backup_conf;
6886 my ($cfg, $vmid) = @_;
6888 my $info = PVE
::Storage
::vdisk_list
($cfg, undef, $vmid, undef, 'images');
6890 my $volid_hash = {};
6891 foreach my $storeid (keys %$info) {
6892 foreach my $item (@{$info->{$storeid}}) {
6893 next if !($item->{volid
} && $item->{size
});
6894 $item->{path
} = PVE
::Storage
::path
($cfg, $item->{volid
});
6895 $volid_hash->{$item->{volid
}} = $item;
6902 sub update_disk_config
{
6903 my ($vmid, $conf, $volid_hash) = @_;
6906 my $prefix = "VM $vmid";
6908 # used and unused disks
6909 my $referenced = {};
6911 # Note: it is allowed to define multiple storages with same path (alias), so
6912 # we need to check both 'volid' and real 'path' (two different volid can point
6913 # to the same path).
6915 my $referencedpath = {};
6918 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6919 my ($opt, $drive) = @_;
6921 my $volid = $drive->{file
};
6923 my $volume = $volid_hash->{$volid};
6925 # mark volid as "in-use" for next step
6926 $referenced->{$volid} = 1;
6927 if ($volume && (my $path = $volume->{path
})) {
6928 $referencedpath->{$path} = 1;
6931 return if drive_is_cdrom
($drive);
6934 my ($updated, $msg) = PVE
::QemuServer
::Drive
::update_disksize
($drive, $volume->{size
});
6935 if (defined($updated)) {
6937 $conf->{$opt} = print_drive
($updated);
6938 print "$prefix ($opt): $msg\n";
6942 # remove 'unusedX' entry if volume is used
6943 PVE
::QemuConfig-
>foreach_unused_volume($conf, sub {
6944 my ($opt, $drive) = @_;
6946 my $volid = $drive->{file
};
6950 $path = $volid_hash->{$volid}->{path
} if $volid_hash->{$volid};
6951 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6952 print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
6954 delete $conf->{$opt};
6957 $referenced->{$volid} = 1;
6958 $referencedpath->{$path} = 1 if $path;
6961 foreach my $volid (sort keys %$volid_hash) {
6962 next if $volid =~ m/vm-$vmid-state-/;
6963 next if $referenced->{$volid};
6964 my $path = $volid_hash->{$volid}->{path
};
6965 next if !$path; # just to be sure
6966 next if $referencedpath->{$path};
6968 my $key = PVE
::QemuConfig-
>add_unused_volume($conf, $volid);
6969 print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
6970 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6977 my ($vmid, $nolock, $dryrun) = @_;
6979 my $cfg = PVE
::Storage
::config
();
6981 print "rescan volumes...\n";
6982 my $volid_hash = scan_volids
($cfg, $vmid);
6984 my $updatefn = sub {
6987 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6989 PVE
::QemuConfig-
>check_lock($conf);
6992 foreach my $volid (keys %$volid_hash) {
6993 my $info = $volid_hash->{$volid};
6994 $vm_volids->{$volid} = $info if $info->{vmid
} && $info->{vmid
} == $vmid;
6997 my $changes = update_disk_config
($vmid, $conf, $vm_volids);
6999 PVE
::QemuConfig-
>write_config($vmid, $conf) if $changes && !$dryrun;
7002 if (defined($vmid)) {
7006 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
7009 my $vmlist = config_list
();
7010 foreach my $vmid (keys %$vmlist) {
7014 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
7020 sub restore_proxmox_backup_archive
{
7021 my ($archive, $vmid, $user, $options) = @_;
7023 my $storecfg = PVE
::Storage
::config
();
7025 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($archive);
7026 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7028 my $fingerprint = $scfg->{fingerprint
};
7029 my $keyfile = PVE
::Storage
::PBSPlugin
::pbs_encryption_key_file_name
($storecfg, $storeid);
7031 my $repo = PVE
::PBSClient
::get_repository
($scfg);
7032 my $namespace = $scfg->{namespace
};
7034 # This is only used for `pbs-restore` and the QEMU PBS driver (live-restore)
7035 my $password = PVE
::Storage
::PBSPlugin
::pbs_get_password
($scfg, $storeid);
7036 local $ENV{PBS_PASSWORD
} = $password;
7037 local $ENV{PBS_FINGERPRINT
} = $fingerprint if defined($fingerprint);
7039 my ($vtype, $pbs_backup_name, undef, undef, undef, undef, $format) =
7040 PVE
::Storage
::parse_volname
($storecfg, $archive);
7042 die "got unexpected vtype '$vtype'\n" if $vtype ne 'backup';
7044 die "got unexpected backup format '$format'\n" if $format ne 'pbs-vm';
7046 my $tmpdir = "/var/tmp/vzdumptmp$$";
7050 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7051 # disable interrupts (always do cleanups)
7055 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
7057 # Note: $oldconf is undef if VM does not exists
7058 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
7059 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
7060 my $new_conf_raw = '';
7062 my $rpcenv = PVE
::RPCEnvironment
::get
();
7063 my $devinfo = {}; # info about drives included in backup
7064 my $virtdev_hash = {}; # info about allocated drives
7072 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7074 my $cfgfn = "$tmpdir/qemu-server.conf";
7075 my $firewall_config_fn = "$tmpdir/fw.conf";
7076 my $index_fn = "$tmpdir/index.json";
7078 my $cmd = "restore";
7080 my $param = [$pbs_backup_name, "index.json", $index_fn];
7081 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7082 my $index = PVE
::Tools
::file_get_contents
($index_fn);
7083 $index = decode_json
($index);
7085 foreach my $info (@{$index->{files
}}) {
7086 if ($info->{filename
} =~ m/^(drive-\S+).img.fidx$/) {
7088 if ($info->{size
} =~ m/^(\d+)$/) { # untaint size
7089 $devinfo->{$devname}->{size
} = $1;
7091 die "unable to parse file size in 'index.json' - got '$info->{size}'\n";
7096 my $is_qemu_server_backup = scalar(
7097 grep { $_->{filename
} eq 'qemu-server.conf.blob' } @{$index->{files
}}
7099 if (!$is_qemu_server_backup) {
7100 die "backup does not look like a qemu-server backup (missing 'qemu-server.conf' file)\n";
7102 my $has_firewall_config = scalar(grep { $_->{filename
} eq 'fw.conf.blob' } @{$index->{files
}});
7104 $param = [$pbs_backup_name, "qemu-server.conf", $cfgfn];
7105 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7107 if ($has_firewall_config) {
7108 $param = [$pbs_backup_name, "fw.conf", $firewall_config_fn];
7109 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
7111 my $pve_firewall_dir = '/etc/pve/firewall';
7112 mkdir $pve_firewall_dir; # make sure the dir exists
7113 PVE
::Tools
::file_copy
($firewall_config_fn, "${pve_firewall_dir}/$vmid.fw");
7116 my $fh = IO
::File-
>new($cfgfn, "r") ||
7117 die "unable to read qemu-server.conf - $!\n";
7119 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $storecfg, $fh, $devinfo, $options);
7121 # fixme: rate limit?
7123 # create empty/temp config
7124 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\nlock: create");
7126 $restore_cleanup_oldconf->($storecfg, $vmid, $oldconf, $virtdev_hash) if $oldconf;
7129 my $map = $restore_allocate_devices->($storecfg, $virtdev_hash, $vmid);
7131 foreach my $virtdev (sort keys %$virtdev_hash) {
7132 my $d = $virtdev_hash->{$virtdev};
7133 next if $d->{is_cloudinit
}; # no need to restore cloudinit
7135 # this fails if storage is unavailable
7136 my $volid = $d->{volid
};
7137 my $path = PVE
::Storage
::path
($storecfg, $volid);
7139 # for live-restore we only want to preload the efidisk and TPM state
7140 next if $options->{live
} && $virtdev ne 'efidisk0' && $virtdev ne 'tpmstate0';
7143 if (defined(my $ns = $scfg->{namespace
})) {
7144 @ns_arg = ('--ns', $ns);
7147 my $pbs_restore_cmd = [
7148 '/usr/bin/pbs-restore',
7149 '--repository', $repo,
7152 "$d->{devname}.img.fidx",
7157 push @$pbs_restore_cmd, '--format', $d->{format
} if $d->{format
};
7158 push @$pbs_restore_cmd, '--keyfile', $keyfile if -e
$keyfile;
7160 if (PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $volid)) {
7161 push @$pbs_restore_cmd, '--skip-zero';
7164 my $dbg_cmdstring = PVE
::Tools
::cmd2string
($pbs_restore_cmd);
7165 print "restore proxmox backup image: $dbg_cmdstring\n";
7166 run_command
($pbs_restore_cmd);
7169 $fh->seek(0, 0) || die "seek failed - $!\n";
7171 my $cookie = { netcount
=> 0 };
7172 while (defined(my $line = <$fh>)) {
7173 $new_conf_raw .= restore_update_config_line
(
7185 if ($err || !$options->{live
}) {
7186 $restore_deactivate_volumes->($storecfg, $virtdev_hash);
7192 $restore_destroy_volumes->($storecfg, $virtdev_hash);
7196 if ($options->{live
}) {
7197 # keep lock during live-restore
7198 $new_conf_raw .= "\nlock: create";
7201 my $new_conf = restore_merge_config
($conffile, $new_conf_raw, $options->{override_conf
});
7202 check_restore_permissions
($rpcenv, $user, $new_conf);
7203 PVE
::QemuConfig-
>write_config($vmid, $new_conf);
7205 eval { rescan
($vmid, 1); };
7208 PVE
::AccessControl
::add_vm_to_pool
($vmid, $options->{pool
}) if $options->{pool
};
7210 if ($options->{live
}) {
7216 local $SIG{PIPE
} = sub { die "got signal ($!) - abort\n"; };
7218 my $conf = PVE
::QemuConfig-
>load_config($vmid);
7219 die "cannot do live-restore for template\n" if PVE
::QemuConfig-
>is_template($conf);
7221 # these special drives are already restored before start
7222 delete $devinfo->{'drive-efidisk0'};
7223 delete $devinfo->{'drive-tpmstate0-backup'};
7227 keyfile
=> $keyfile,
7228 snapshot
=> $pbs_backup_name,
7229 namespace
=> $namespace,
7231 pbs_live_restore
($vmid, $conf, $storecfg, $devinfo, $pbs_opts);
7233 PVE
::QemuConfig-
>remove_lock($vmid, "create");
7237 sub pbs_live_restore
{
7238 my ($vmid, $conf, $storecfg, $restored_disks, $opts) = @_;
7240 print "starting VM for live-restore\n";
7241 print "repository: '$opts->{repo}', snapshot: '$opts->{snapshot}'\n";
7243 my $pbs_backing = {};
7244 for my $ds (keys %$restored_disks) {
7245 $ds =~ m/^drive-(.*)$/;
7247 $pbs_backing->{$confname} = {
7248 repository
=> $opts->{repo
},
7249 snapshot
=> $opts->{snapshot
},
7250 archive
=> "$ds.img.fidx",
7252 $pbs_backing->{$confname}->{keyfile
} = $opts->{keyfile
} if -e
$opts->{keyfile
};
7253 $pbs_backing->{$confname}->{namespace
} = $opts->{namespace
} if defined($opts->{namespace
});
7255 my $drive = parse_drive
($confname, $conf->{$confname});
7256 print "restoring '$ds' to '$drive->{file}'\n";
7259 my $drives_streamed = 0;
7261 # make sure HA doesn't interrupt our restore by stopping the VM
7262 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid)) {
7263 run_command
(['ha-manager', 'set', "vm:$vmid", '--state', 'started']);
7266 # start VM with backing chain pointing to PBS backup, environment vars for PBS driver
7267 # in QEMU (PBS_PASSWORD and PBS_FINGERPRINT) are already set by our caller
7268 vm_start_nolock
($storecfg, $vmid, $conf, {paused
=> 1, 'pbs-backing' => $pbs_backing}, {});
7270 my $qmeventd_fd = register_qmeventd_handle
($vmid);
7272 # begin streaming, i.e. data copy from PBS to target disk for every vol,
7273 # this will effectively collapse the backing image chain consisting of
7274 # [target <- alloc-track -> PBS snapshot] to just [target] (alloc-track
7275 # removes itself once all backing images vanish with 'auto-remove=on')
7277 for my $ds (sort keys %$restored_disks) {
7278 my $job_id = "restore-$ds";
7279 mon_cmd
($vmid, 'block-stream',
7280 'job-id' => $job_id,
7283 $jobs->{$job_id} = {};
7286 mon_cmd
($vmid, 'cont');
7287 qemu_drive_mirror_monitor
($vmid, undef, $jobs, 'auto', 0, 'stream');
7289 print "restore-drive jobs finished successfully, removing all tracking block devices"
7290 ." to disconnect from Proxmox Backup Server\n";
7292 for my $ds (sort keys %$restored_disks) {
7293 mon_cmd
($vmid, 'blockdev-del', 'node-name' => "$ds-pbs");
7296 close($qmeventd_fd);
7302 warn "An error occurred during live-restore: $err\n";
7303 _do_vm_stop
($storecfg, $vmid, 1, 1, 10, 0, 1);
7304 die "live-restore failed\n";
7308 sub restore_vma_archive
{
7309 my ($archive, $vmid, $user, $opts, $comp) = @_;
7311 my $readfrom = $archive;
7313 my $cfg = PVE
::Storage
::config
();
7315 my $bwlimit = $opts->{bwlimit
};
7317 my $dbg_cmdstring = '';
7318 my $add_pipe = sub {
7320 push @$commands, $cmd;
7321 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
7322 $dbg_cmdstring .= PVE
::Tools
::cmd2string
($cmd);
7327 if ($archive eq '-') {
7330 # If we use a backup from a PVE defined storage we also consider that
7331 # storage's rate limit:
7332 my (undef, $volid) = PVE
::Storage
::path_to_volume_id
($cfg, $archive);
7333 if (defined($volid)) {
7334 my ($sid, undef) = PVE
::Storage
::parse_volume_id
($volid);
7335 my $readlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [$sid], $bwlimit);
7337 print STDERR
"applying read rate limit: $readlimit\n";
7338 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
7339 $add_pipe->($cstream);
7345 my $info = PVE
::Storage
::decompressor_info
('vma', $comp);
7346 my $cmd = $info->{decompressor
};
7347 push @$cmd, $readfrom;
7351 my $tmpdir = "/var/tmp/vzdumptmp$$";
7354 # disable interrupts (always do cleanups)
7358 local $SIG{HUP
} = sub { warn "got interrupt - ignored\n"; };
7360 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
7361 POSIX
::mkfifo
($mapfifo, 0600);
7363 my $openfifo = sub { open($fifofh, '>', $mapfifo) or die $! };
7365 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
7370 my $devinfo = {}; # info about drives included in backup
7371 my $virtdev_hash = {}; # info about allocated drives
7373 my $rpcenv = PVE
::RPCEnvironment
::get
();
7375 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7377 # Note: $oldconf is undef if VM does not exist
7378 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
7379 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
7380 my $new_conf_raw = '';
7384 my $print_devmap = sub {
7385 my $cfgfn = "$tmpdir/qemu-server.conf";
7387 # we can read the config - that is already extracted
7388 my $fh = IO
::File-
>new($cfgfn, "r") ||
7389 die "unable to read qemu-server.conf - $!\n";
7391 my $fwcfgfn = "$tmpdir/qemu-server.fw";
7393 my $pve_firewall_dir = '/etc/pve/firewall';
7394 mkdir $pve_firewall_dir; # make sure the dir exists
7395 PVE
::Tools
::file_copy
($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
7398 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $cfg, $fh, $devinfo, $opts);
7400 foreach my $info (values %{$virtdev_hash}) {
7401 my $storeid = $info->{storeid
};
7402 next if defined($storage_limits{$storeid});
7404 my $limit = PVE
::Storage
::get_bandwidth_limit
('restore', [$storeid], $bwlimit) // 0;
7405 print STDERR
"rate limit for storage $storeid: $limit KiB/s\n" if $limit;
7406 $storage_limits{$storeid} = $limit * 1024;
7409 foreach my $devname (keys %$devinfo) {
7410 die "found no device mapping information for device '$devname'\n"
7411 if !$devinfo->{$devname}->{virtdev
};
7414 # create empty/temp config
7416 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\n");
7417 $restore_cleanup_oldconf->($cfg, $vmid, $oldconf, $virtdev_hash);
7421 my $map = $restore_allocate_devices->($cfg, $virtdev_hash, $vmid);
7423 # print restore information to $fifofh
7424 foreach my $virtdev (sort keys %$virtdev_hash) {
7425 my $d = $virtdev_hash->{$virtdev};
7426 next if $d->{is_cloudinit
}; # no need to restore cloudinit
7428 my $storeid = $d->{storeid
};
7429 my $volid = $d->{volid
};
7432 if (my $limit = $storage_limits{$storeid}) {
7433 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
7436 my $write_zeros = 1;
7437 if (PVE
::Storage
::volume_has_feature
($cfg, 'sparseinit', $volid)) {
7441 my $path = PVE
::Storage
::path
($cfg, $volid);
7443 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
7445 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
7448 $fh->seek(0, 0) || die "seek failed - $!\n";
7450 my $cookie = { netcount
=> 0 };
7451 while (defined(my $line = <$fh>)) {
7452 $new_conf_raw .= restore_update_config_line
(
7469 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7470 local $SIG{ALRM
} = sub { die "got timeout\n"; };
7472 $oldtimeout = alarm($timeout);
7479 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
7480 my ($dev_id, $size, $devname) = ($1, $2, $3);
7481 $devinfo->{$devname} = { size
=> $size, dev_id
=> $dev_id };
7482 } elsif ($line =~ m/^CTIME: /) {
7483 # we correctly received the vma config, so we can disable
7484 # the timeout now for disk allocation (set to 10 minutes, so
7485 # that we always timeout if something goes wrong)
7488 print $fifofh "done\n";
7489 my $tmp = $oldtimeout || 0;
7490 $oldtimeout = undef;
7497 print "restore vma archive: $dbg_cmdstring\n";
7498 run_command
($commands, input
=> $input, outfunc
=> $parser, afterfork
=> $openfifo);
7502 alarm($oldtimeout) if $oldtimeout;
7504 $restore_deactivate_volumes->($cfg, $virtdev_hash);
7506 close($fifofh) if $fifofh;
7511 $restore_destroy_volumes->($cfg, $virtdev_hash);
7515 my $new_conf = restore_merge_config
($conffile, $new_conf_raw, $opts->{override_conf
});
7516 check_restore_permissions
($rpcenv, $user, $new_conf);
7517 PVE
::QemuConfig-
>write_config($vmid, $new_conf);
7519 eval { rescan
($vmid, 1); };
7522 PVE
::AccessControl
::add_vm_to_pool
($vmid, $opts->{pool
}) if $opts->{pool
};
7525 sub restore_tar_archive
{
7526 my ($archive, $vmid, $user, $opts) = @_;
7528 if (scalar(keys $opts->{override_conf
}->%*) > 0) {
7529 my $keystring = join(' ', keys $opts->{override_conf
}->%*);
7530 die "cannot pass along options ($keystring) when restoring from tar archive\n";
7533 if ($archive ne '-') {
7534 my $firstfile = tar_archive_read_firstfile
($archive);
7535 die "ERROR: file '$archive' does not look like a QemuServer vzdump backup\n"
7536 if $firstfile ne 'qemu-server.conf';
7539 my $storecfg = PVE
::Storage
::config
();
7541 # avoid zombie disks when restoring over an existing VM -> cleanup first
7542 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
7543 # skiplock=1 because qmrestore has set the 'create' lock itself already
7544 my $vmcfgfn = PVE
::QemuConfig-
>config_file($vmid);
7545 destroy_vm
($storecfg, $vmid, 1, { lock => 'restore' }) if -f
$vmcfgfn;
7547 my $tocmd = "/usr/lib/qemu-server/qmextract";
7549 $tocmd .= " --storage " . PVE
::Tools
::shellquote
($opts->{storage
}) if $opts->{storage
};
7550 $tocmd .= " --pool " . PVE
::Tools
::shellquote
($opts->{pool
}) if $opts->{pool
};
7551 $tocmd .= ' --prealloc' if $opts->{prealloc
};
7552 $tocmd .= ' --info' if $opts->{info
};
7554 # tar option "xf" does not autodetect compression when read from STDIN,
7555 # so we pipe to zcat
7556 my $cmd = "zcat -f|tar xf " . PVE
::Tools
::shellquote
($archive) . " " .
7557 PVE
::Tools
::shellquote
("--to-command=$tocmd");
7559 my $tmpdir = "/var/tmp/vzdumptmp$$";
7562 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
7563 local $ENV{VZDUMP_VMID
} = $vmid;
7564 local $ENV{VZDUMP_USER
} = $user;
7566 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7567 my $new_conf_raw = '';
7569 # disable interrupts (always do cleanups)
7573 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
7581 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7583 if ($archive eq '-') {
7584 print "extracting archive from STDIN\n";
7585 run_command
($cmd, input
=> "<&STDIN");
7587 print "extracting archive '$archive'\n";
7591 return if $opts->{info
};
7595 my $statfile = "$tmpdir/qmrestore.stat";
7596 if (my $fd = IO
::File-
>new($statfile, "r")) {
7597 while (defined (my $line = <$fd>)) {
7598 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
7599 $map->{$1} = $2 if $1;
7601 print STDERR
"unable to parse line in statfile - $line\n";
7607 my $confsrc = "$tmpdir/qemu-server.conf";
7609 my $srcfd = IO
::File-
>new($confsrc, "r") || die "unable to open file '$confsrc'\n";
7611 my $cookie = { netcount
=> 0 };
7612 while (defined (my $line = <$srcfd>)) {
7613 $new_conf_raw .= restore_update_config_line
(
7624 tar_restore_cleanup
($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info
};
7630 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
7632 PVE
::Cluster
::cfs_update
(); # make sure we read new file
7634 eval { rescan
($vmid, 1); };
7638 sub foreach_storage_used_by_vm
{
7639 my ($conf, $func) = @_;
7643 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7644 my ($ds, $drive) = @_;
7645 return if drive_is_cdrom
($drive);
7647 my $volid = $drive->{file
};
7649 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
7650 $sidhash->{$sid} = $sid if $sid;
7653 foreach my $sid (sort keys %$sidhash) {
7658 my $qemu_snap_storage = {
7661 sub do_snapshots_with_qemu
{
7662 my ($storecfg, $volid, $deviceid) = @_;
7664 return if $deviceid =~ m/tpmstate0/;
7666 my $storage_name = PVE
::Storage
::parse_volume_id
($volid);
7667 my $scfg = $storecfg->{ids
}->{$storage_name};
7668 die "could not find storage '$storage_name'\n" if !defined($scfg);
7670 if ($qemu_snap_storage->{$scfg->{type
}} && !$scfg->{krbd
}){
7674 if ($volid =~ m/\.(qcow2|qed)$/){
7681 sub qga_check_running
{
7682 my ($vmid, $nowarn) = @_;
7684 eval { mon_cmd
($vmid, "guest-ping", timeout
=> 3); };
7686 warn "QEMU Guest Agent is not running - $@" if !$nowarn;
7692 sub template_create
{
7693 my ($vmid, $conf, $disk) = @_;
7695 my $storecfg = PVE
::Storage
::config
();
7697 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7698 my ($ds, $drive) = @_;
7700 return if drive_is_cdrom
($drive);
7701 return if $disk && $ds ne $disk;
7703 my $volid = $drive->{file
};
7704 return if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
7706 my $voliddst = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
7707 $drive->{file
} = $voliddst;
7708 $conf->{$ds} = print_drive
($drive);
7709 PVE
::QemuConfig-
>write_config($vmid, $conf);
7713 sub convert_iscsi_path
{
7716 if ($path =~ m
|^iscsi
://([^/]+)/([^/]+)/(.+)$|) {
7721 my $initiator_name = get_initiator_name
();
7723 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
7724 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
7727 die "cannot convert iscsi path '$path', unkown format\n";
7730 sub qemu_img_convert
{
7731 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized, $bwlimit) = @_;
7733 my $storecfg = PVE
::Storage
::config
();
7734 my ($src_storeid, $src_volname) = PVE
::Storage
::parse_volume_id
($src_volid, 1);
7735 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid, 1);
7737 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
7741 my $src_is_iscsi = 0;
7745 PVE
::Storage
::activate_volumes
($storecfg, [$src_volid], $snapname);
7746 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
7747 $src_format = qemu_img_format
($src_scfg, $src_volname);
7748 $src_path = PVE
::Storage
::path
($storecfg, $src_volid, $snapname);
7749 $src_is_iscsi = ($src_path =~ m
|^iscsi
://|);
7750 $cachemode = 'none' if $src_scfg->{type
} eq 'zfspool';
7751 } elsif (-f
$src_volid || -b
$src_volid) {
7752 $src_path = $src_volid;
7753 if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7758 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
7760 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7761 my $dst_format = qemu_img_format
($dst_scfg, $dst_volname);
7762 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7763 my $dst_is_iscsi = ($dst_path =~ m
|^iscsi
://|);
7766 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
7767 push @$cmd, '-l', "snapshot.name=$snapname"
7768 if $snapname && $src_format && $src_format eq "qcow2";
7769 push @$cmd, '-t', 'none' if $dst_scfg->{type
} eq 'zfspool';
7770 push @$cmd, '-T', $cachemode if defined($cachemode);
7771 push @$cmd, '-r', "${bwlimit}K" if defined($bwlimit);
7773 if ($src_is_iscsi) {
7774 push @$cmd, '--image-opts';
7775 $src_path = convert_iscsi_path
($src_path);
7776 } elsif ($src_format) {
7777 push @$cmd, '-f', $src_format;
7780 if ($dst_is_iscsi) {
7781 push @$cmd, '--target-image-opts';
7782 $dst_path = convert_iscsi_path
($dst_path);
7784 push @$cmd, '-O', $dst_format;
7787 push @$cmd, $src_path;
7789 if (!$dst_is_iscsi && $is_zero_initialized) {
7790 push @$cmd, "zeroinit:$dst_path";
7792 push @$cmd, $dst_path;
7797 if($line =~ m/\((\S+)\/100\
%\)/){
7799 my $transferred = int($size * $percent / 100);
7800 my $total_h = render_bytes
($size, 1);
7801 my $transferred_h = render_bytes
($transferred, 1);
7803 print "transferred $transferred_h of $total_h ($percent%)\n";
7808 eval { run_command
($cmd, timeout
=> undef, outfunc
=> $parser); };
7810 die "copy failed: $err" if $err;
7813 sub qemu_img_format
{
7814 my ($scfg, $volname) = @_;
7816 if ($scfg->{path
} && $volname =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7823 sub qemu_drive_mirror
{
7824 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
7826 $jobs = {} if !$jobs;
7830 $jobs->{"drive-$drive"} = {};
7832 if ($dst_volid =~ /^nbd:/) {
7833 $qemu_target = $dst_volid;
7836 my $storecfg = PVE
::Storage
::config
();
7837 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid);
7839 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7841 $format = qemu_img_format
($dst_scfg, $dst_volname);
7843 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7845 $qemu_target = $is_zero_initialized ?
"zeroinit:$dst_path" : $dst_path;
7848 my $opts = { timeout
=> 10, device
=> "drive-$drive", mode
=> "existing", sync
=> "full", target
=> $qemu_target };
7849 $opts->{format
} = $format if $format;
7851 if (defined($src_bitmap)) {
7852 $opts->{sync
} = 'incremental';
7853 $opts->{bitmap
} = $src_bitmap;
7854 print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
7857 if (defined($bwlimit)) {
7858 $opts->{speed
} = $bwlimit * 1024;
7859 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
7861 print "drive mirror is starting for drive-$drive\n";
7864 # if a job already runs for this device we get an error, catch it for cleanup
7865 eval { mon_cmd
($vmid, "drive-mirror", %$opts); };
7867 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7869 die "mirroring error: $err\n";
7872 qemu_drive_mirror_monitor
($vmid, $vmiddst, $jobs, $completion, $qga);
7875 # $completion can be either
7876 # 'complete': wait until all jobs are ready, block-job-complete them (default)
7877 # 'cancel': wait until all jobs are ready, block-job-cancel them
7878 # 'skip': wait until all jobs are ready, return with block jobs in ready state
7879 # 'auto': wait until all jobs disappear, only use for jobs which complete automatically
7880 sub qemu_drive_mirror_monitor
{
7881 my ($vmid, $vmiddst, $jobs, $completion, $qga, $op) = @_;
7883 $completion //= 'complete';
7887 my $err_complete = 0;
7889 my $starttime = time ();
7891 die "block job ('$op') timed out\n" if $err_complete > 300;
7893 my $stats = mon_cmd
($vmid, "query-block-jobs");
7896 my $running_jobs = {};
7897 for my $stat (@$stats) {
7898 next if $stat->{type
} ne $op;
7899 $running_jobs->{$stat->{device
}} = $stat;
7902 my $readycounter = 0;
7904 for my $job_id (sort keys %$jobs) {
7905 my $job = $running_jobs->{$job_id};
7907 my $vanished = !defined($job);
7908 my $complete = defined($jobs->{$job_id}->{complete
}) && $vanished;
7909 if($complete || ($vanished && $completion eq 'auto')) {
7910 print "$job_id: $op-job finished\n";
7911 delete $jobs->{$job_id};
7915 die "$job_id: '$op' has been cancelled\n" if !defined($job);
7917 my $busy = $job->{busy
};
7918 my $ready = $job->{ready
};
7919 if (my $total = $job->{len
}) {
7920 my $transferred = $job->{offset
} || 0;
7921 my $remaining = $total - $transferred;
7922 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
7924 my $duration = $ctime - $starttime;
7925 my $total_h = render_bytes
($total, 1);
7926 my $transferred_h = render_bytes
($transferred, 1);
7928 my $status = sprintf(
7929 "transferred $transferred_h of $total_h ($percent%%) in %s",
7930 render_duration
($duration),
7935 $status .= ", still busy"; # shouldn't even happen? but mirror is weird
7937 $status .= ", ready";
7940 print "$job_id: $status\n" if !$jobs->{$job_id}->{ready
};
7941 $jobs->{$job_id}->{ready
} = $ready;
7944 $readycounter++ if $job->{ready
};
7947 last if scalar(keys %$jobs) == 0;
7949 if ($readycounter == scalar(keys %$jobs)) {
7950 print "all '$op' jobs are ready\n";
7952 # do the complete later (or has already been done)
7953 last if $completion eq 'skip' || $completion eq 'auto';
7955 if ($vmiddst && $vmiddst != $vmid) {
7956 my $agent_running = $qga && qga_check_running
($vmid);
7957 if ($agent_running) {
7958 print "freeze filesystem\n";
7959 eval { mon_cmd
($vmid, "guest-fsfreeze-freeze"); };
7962 print "suspend vm\n";
7963 eval { PVE
::QemuServer
::vm_suspend
($vmid, 1); };
7967 # if we clone a disk for a new target vm, we don't switch the disk
7968 PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs);
7970 if ($agent_running) {
7971 print "unfreeze filesystem\n";
7972 eval { mon_cmd
($vmid, "guest-fsfreeze-thaw"); };
7975 print "resume vm\n";
7976 eval { PVE
::QemuServer
::vm_resume
($vmid, 1, 1); };
7983 for my $job_id (sort keys %$jobs) {
7984 # try to switch the disk if source and destination are on the same guest
7985 print "$job_id: Completing block job_id...\n";
7988 if ($completion eq 'complete') {
7989 $op = 'block-job-complete';
7990 } elsif ($completion eq 'cancel') {
7991 $op = 'block-job-cancel';
7993 die "invalid completion value: $completion\n";
7995 eval { mon_cmd
($vmid, $op, device
=> $job_id) };
7996 if ($@ =~ m/cannot be completed/) {
7997 print "$job_id: block job cannot be completed, trying again.\n";
8000 print "$job_id: Completed successfully.\n";
8001 $jobs->{$job_id}->{complete
} = 1;
8012 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
8013 die "block job ($op) error: $err";
8017 sub qemu_blockjobs_cancel
{
8018 my ($vmid, $jobs) = @_;
8020 foreach my $job (keys %$jobs) {
8021 print "$job: Cancelling block job\n";
8022 eval { mon_cmd
($vmid, "block-job-cancel", device
=> $job); };
8023 $jobs->{$job}->{cancel
} = 1;
8027 my $stats = mon_cmd
($vmid, "query-block-jobs");
8029 my $running_jobs = {};
8030 foreach my $stat (@$stats) {
8031 $running_jobs->{$stat->{device
}} = $stat;
8034 foreach my $job (keys %$jobs) {
8036 if (defined($jobs->{$job}->{cancel
}) && !defined($running_jobs->{$job})) {
8037 print "$job: Done.\n";
8038 delete $jobs->{$job};
8042 last if scalar(keys %$jobs) == 0;
8048 # Check for bug #4525: drive-mirror will open the target drive with the same aio setting as the
8049 # source, but some storages have problems with io_uring, sometimes even leading to crashes.
8050 my sub clone_disk_check_io_uring
{
8051 my ($src_drive, $storecfg, $src_storeid, $dst_storeid, $use_drive_mirror) = @_;
8053 return if !$use_drive_mirror;
8055 # Don't complain when not changing storage.
8056 # Assume if it works for the source, it'll work for the target too.
8057 return if $src_storeid eq $dst_storeid;
8059 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
8060 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
8062 my $cache_direct = drive_uses_cache_direct
($src_drive);
8064 my $src_uses_io_uring;
8065 if ($src_drive->{aio
}) {
8066 $src_uses_io_uring = $src_drive->{aio
} eq 'io_uring';
8068 $src_uses_io_uring = storage_allows_io_uring_default
($src_scfg, $cache_direct);
8071 die "target storage is known to cause issues with aio=io_uring (used by current drive)\n"
8072 if $src_uses_io_uring && !storage_allows_io_uring_default
($dst_scfg, $cache_direct);
8076 my ($storecfg, $source, $dest, $full, $newvollist, $jobs, $completion, $qga, $bwlimit) = @_;
8078 my ($vmid, $running) = $source->@{qw(vmid running)};
8079 my ($src_drivename, $drive, $snapname) = $source->@{qw(drivename drive snapname)};
8081 my ($newvmid, $dst_drivename, $efisize) = $dest->@{qw(vmid drivename efisize)};
8082 my ($storage, $format) = $dest->@{qw(storage format)};
8084 my $use_drive_mirror = $full && $running && $src_drivename && !$snapname;
8086 if ($src_drivename && $dst_drivename && $src_drivename ne $dst_drivename) {
8087 die "cloning from/to EFI disk requires EFI disk\n"
8088 if $src_drivename eq 'efidisk0' || $dst_drivename eq 'efidisk0';
8089 die "cloning from/to TPM state requires TPM state\n"
8090 if $src_drivename eq 'tpmstate0' || $dst_drivename eq 'tpmstate0';
8092 # This would lead to two device nodes in QEMU pointing to the same backing image!
8093 die "cannot change drive name when cloning disk from/to the same VM\n"
8094 if $use_drive_mirror && $vmid == $newvmid;
8097 die "cannot move TPM state while VM is running\n"
8098 if $use_drive_mirror && $src_drivename eq 'tpmstate0';
8102 print "create " . ($full ?
'full' : 'linked') . " clone of drive ";
8103 print "$src_drivename " if $src_drivename;
8104 print "($drive->{file})\n";
8107 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $drive->{file
}, $newvmid, $snapname);
8108 push @$newvollist, $newvolid;
8110 my ($src_storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
8111 my $storeid = $storage || $src_storeid;
8113 my $dst_format = resolve_dst_disk_format
($storecfg, $storeid, $volname, $format);
8117 if (drive_is_cloudinit
($drive)) {
8118 $name = "vm-$newvmid-cloudinit";
8119 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8120 if ($scfg->{path
}) {
8121 $name .= ".$dst_format";
8124 $size = PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
;
8125 } elsif ($dst_drivename eq 'efidisk0') {
8126 $size = $efisize or die "internal error - need to specify EFI disk size\n";
8127 } elsif ($dst_drivename eq 'tpmstate0') {
8128 $dst_format = 'raw';
8129 $size = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
8131 clone_disk_check_io_uring
($drive, $storecfg, $src_storeid, $storeid, $use_drive_mirror);
8133 $size = PVE
::Storage
::volume_size_info
($storecfg, $drive->{file
}, 10);
8135 $newvolid = PVE
::Storage
::vdisk_alloc
(
8136 $storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024)
8138 push @$newvollist, $newvolid;
8140 PVE
::Storage
::activate_volumes
($storecfg, [$newvolid]);
8142 if (drive_is_cloudinit
($drive)) {
8143 # when cloning multiple disks (e.g. during clone_vm) it might be the last disk
8144 # if this is the case, we have to complete any block-jobs still there from
8145 # previous drive-mirrors
8146 if (($completion eq 'complete') && (scalar(keys %$jobs) > 0)) {
8147 qemu_drive_mirror_monitor
($vmid, $newvmid, $jobs, $completion, $qga);
8152 my $sparseinit = PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $newvolid);
8153 if ($use_drive_mirror) {
8154 qemu_drive_mirror
($vmid, $src_drivename, $newvolid, $newvmid, $sparseinit, $jobs,
8155 $completion, $qga, $bwlimit);
8157 if ($dst_drivename eq 'efidisk0') {
8158 # the relevant data on the efidisk may be smaller than the source
8159 # e.g. on RBD/ZFS, so we use dd to copy only the amount
8160 # that is given by the OVMF_VARS.fd
8161 my $src_path = PVE
::Storage
::path
($storecfg, $drive->{file
}, $snapname);
8162 my $dst_path = PVE
::Storage
::path
($storecfg, $newvolid);
8164 my $src_format = (PVE
::Storage
::parse_volname
($storecfg, $drive->{file
}))[6];
8166 # better for Ceph if block size is not too small, see bug #3324
8169 my $cmd = ['qemu-img', 'dd', '-n', '-O', $dst_format];
8171 if ($src_format eq 'qcow2' && $snapname) {
8172 die "cannot clone qcow2 EFI disk snapshot - requires QEMU >= 6.2\n"
8173 if !min_version
(kvm_user_version
(), 6, 2);
8174 push $cmd->@*, '-l', $snapname;
8176 push $cmd->@*, "bs=$bs", "osize=$size", "if=$src_path", "of=$dst_path";
8179 qemu_img_convert
($drive->{file
}, $newvolid, $size, $snapname, $sparseinit, $bwlimit);
8185 my $size = eval { PVE
::Storage
::volume_size_info
($storecfg, $newvolid, 10) };
8187 my $disk = dclone
($drive);
8188 delete $disk->{format
};
8189 $disk->{file
} = $newvolid;
8190 $disk->{size
} = $size if defined($size);
8195 sub get_running_qemu_version
{
8197 my $res = mon_cmd
($vmid, "query-version");
8198 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
8201 sub qemu_use_old_bios_files
{
8202 my ($machine_type) = @_;
8204 return if !$machine_type;
8206 my $use_old_bios_files = undef;
8208 if ($machine_type =~ m/^(\S+)\.pxe$/) {
8210 $use_old_bios_files = 1;
8212 my $version = extract_version
($machine_type, kvm_user_version
());
8213 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
8214 # load new efi bios files on migration. So this hack is required to allow
8215 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
8216 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
8217 $use_old_bios_files = !min_version
($version, 2, 4);
8220 return ($use_old_bios_files, $machine_type);
8223 sub get_efivars_size
{
8224 my ($conf, $efidisk) = @_;
8226 my $arch = get_vm_arch
($conf);
8227 $efidisk //= $conf->{efidisk0
} ? parse_drive
('efidisk0', $conf->{efidisk0
}) : undef;
8228 my $smm = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
8229 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
8230 return -s
$ovmf_vars;
8233 sub update_efidisk_size
{
8236 return if !defined($conf->{efidisk0
});
8238 my $disk = PVE
::QemuServer
::parse_drive
('efidisk0', $conf->{efidisk0
});
8239 $disk->{size
} = get_efivars_size
($conf);
8240 $conf->{efidisk0
} = print_drive
($disk);
8245 sub update_tpmstate_size
{
8248 my $disk = PVE
::QemuServer
::parse_drive
('tpmstate0', $conf->{tpmstate0
});
8249 $disk->{size
} = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
8250 $conf->{tpmstate0
} = print_drive
($disk);
8253 sub create_efidisk
($$$$$$$) {
8254 my ($storecfg, $storeid, $vmid, $fmt, $arch, $efidisk, $smm) = @_;
8256 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
8258 my $vars_size_b = -s
$ovmf_vars;
8259 my $vars_size = PVE
::Tools
::convert_size
($vars_size_b, 'b' => 'kb');
8260 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
8261 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
8263 qemu_img_convert
($ovmf_vars, $volid, $vars_size_b, undef, 0);
8264 my $size = PVE
::Storage
::volume_size_info
($storecfg, $volid, 3);
8266 return ($volid, $size/1024);
8269 sub vm_iothreads_list
{
8272 my $res = mon_cmd
($vmid, 'query-iothreads');
8275 foreach my $iothread (@$res) {
8276 $iothreads->{ $iothread->{id
} } = $iothread->{"thread-id"};
8283 my ($conf, $drive) = @_;
8287 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)) {
8289 } elsif ($conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single')) {
8295 my $controller = int($drive->{index} / $maxdev);
8296 my $controller_prefix = ($conf->{scsihw
} && $conf->{scsihw
} eq 'virtio-scsi-single')
8300 return ($maxdev, $controller, $controller_prefix);
8303 sub resolve_dst_disk_format
{
8304 my ($storecfg, $storeid, $src_volname, $format) = @_;
8305 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
8308 # if no target format is specified, use the source disk format as hint
8310 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8311 $format = qemu_img_format
($scfg, $src_volname);
8317 # test if requested format is supported - else use default
8318 my $supported = grep { $_ eq $format } @$validFormats;
8319 $format = $defFormat if !$supported;
8323 # NOTE: if this logic changes, please update docs & possibly gui logic
8324 sub find_vmstate_storage
{
8325 my ($conf, $storecfg) = @_;
8327 # first, return storage from conf if set
8328 return $conf->{vmstatestorage
} if $conf->{vmstatestorage
};
8330 my ($target, $shared, $local);
8332 foreach_storage_used_by_vm
($conf, sub {
8334 my $scfg = PVE
::Storage
::storage_config
($storecfg, $sid);
8335 my $dst = $scfg->{shared
} ? \
$shared : \
$local;
8336 $$dst = $sid if !$$dst || $scfg->{path
}; # prefer file based storage
8339 # second, use shared storage where VM has at least one disk
8340 # third, use local storage where VM has at least one disk
8341 # fall back to local storage
8342 $target = $shared // $local // 'local';
8348 my ($uuid, $uuid_str);
8349 UUID
::generate
($uuid);
8350 UUID
::unparse
($uuid, $uuid_str);
8354 sub generate_smbios1_uuid
{
8355 return "uuid=".generate_uuid
();
8361 mon_cmd
($vmid, 'nbd-server-stop');
8364 sub create_reboot_request
{
8366 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
8367 or die "failed to create reboot trigger file: $!\n";
8371 sub clear_reboot_request
{
8373 my $path = "/run/qemu-server/$vmid.reboot";
8376 $res = unlink($path);
8377 die "could not remove reboot request for $vmid: $!"
8378 if !$res && $! != POSIX
::ENOENT
;
8383 sub bootorder_from_legacy
{
8384 my ($conf, $bootcfg) = @_;
8386 my $boot = $bootcfg->{legacy
} || $boot_fmt->{legacy
}->{default};
8387 my $bootindex_hash = {};
8389 foreach my $o (split(//, $boot)) {
8390 $bootindex_hash->{$o} = $i*100;
8396 PVE
::QemuConfig-
>foreach_volume($conf, sub {
8397 my ($ds, $drive) = @_;
8399 if (drive_is_cdrom
($drive, 1)) {
8400 if ($bootindex_hash->{d
}) {
8401 $bootorder->{$ds} = $bootindex_hash->{d
};
8402 $bootindex_hash->{d
} += 1;
8404 } elsif ($bootindex_hash->{c
}) {
8405 $bootorder->{$ds} = $bootindex_hash->{c
}
8406 if $conf->{bootdisk
} && $conf->{bootdisk
} eq $ds;
8407 $bootindex_hash->{c
} += 1;
8411 if ($bootindex_hash->{n
}) {
8412 for (my $i = 0; $i < $MAX_NETS; $i++) {
8413 my $netname = "net$i";
8414 next if !$conf->{$netname};
8415 $bootorder->{$netname} = $bootindex_hash->{n
};
8416 $bootindex_hash->{n
} += 1;
8423 # Generate default device list for 'boot: order=' property. Matches legacy
8424 # default boot order, but with explicit device names. This is important, since
8425 # the fallback for when neither 'order' nor the old format is specified relies
8426 # on 'bootorder_from_legacy' above, and it would be confusing if this diverges.
8427 sub get_default_bootdevices
{
8433 my $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 0);
8434 push @ret, $first if $first;
8437 $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 1);
8438 push @ret, $first if $first;
8441 for (my $i = 0; $i < $MAX_NETS; $i++) {
8442 my $netname = "net$i";
8443 next if !$conf->{$netname};
8444 push @ret, $netname;
8451 sub device_bootorder
{
8454 return bootorder_from_legacy
($conf) if !defined($conf->{boot
});
8456 my $boot = parse_property_string
($boot_fmt, $conf->{boot
});
8459 if (!defined($boot) || $boot->{legacy
}) {
8460 $bootorder = bootorder_from_legacy
($conf, $boot);
8461 } elsif ($boot->{order
}) {
8462 my $i = 100; # start at 100 to allow user to insert devices before us with -args
8463 for my $dev (PVE
::Tools
::split_list
($boot->{order
})) {
8464 $bootorder->{$dev} = $i++;
8471 sub register_qmeventd_handle
{
8475 my $peer = "/var/run/qmeventd.sock";
8480 $fh = IO
::Socket
::UNIX-
>new(Peer
=> $peer, Blocking
=> 0, Timeout
=> 1);
8482 if ($! != EINTR
&& $! != EAGAIN
) {
8483 die "unable to connect to qmeventd socket (vmid: $vmid) - $!\n";
8486 die "unable to connect to qmeventd socket (vmid: $vmid) - timeout "
8487 . "after $count retries\n";
8492 # send handshake to mark VM as backing up
8493 print $fh to_json
({vzdump
=> {vmid
=> "$vmid"}});
8495 # return handle to be closed later when inhibit is no longer required
8499 # bash completion helper
8501 sub complete_backup_archives
{
8502 my ($cmdname, $pname, $cvalue) = @_;
8504 my $cfg = PVE
::Storage
::config
();
8508 if ($cvalue =~ m/^([^:]+):/) {
8512 my $data = PVE
::Storage
::template_list
($cfg, $storeid, 'backup');
8515 foreach my $id (keys %$data) {
8516 foreach my $item (@{$data->{$id}}) {
8517 next if $item->{format
} !~ m/^vma\.(${\PVE::Storage::Plugin::COMPRESSOR_RE})$/;
8518 push @$res, $item->{volid
} if defined($item->{volid
});
8525 my $complete_vmid_full = sub {
8528 my $idlist = vmstatus
();
8532 foreach my $id (keys %$idlist) {
8533 my $d = $idlist->{$id};
8534 if (defined($running)) {
8535 next if $d->{template
};
8536 next if $running && $d->{status
} ne 'running';
8537 next if !$running && $d->{status
} eq 'running';
8546 return &$complete_vmid_full();
8549 sub complete_vmid_stopped
{
8550 return &$complete_vmid_full(0);
8553 sub complete_vmid_running
{
8554 return &$complete_vmid_full(1);
8557 sub complete_storage
{
8559 my $cfg = PVE
::Storage
::config
();
8560 my $ids = $cfg->{ids
};
8563 foreach my $sid (keys %$ids) {
8564 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, undef, 1);
8565 next if !$ids->{$sid}->{content
}->{images
};
8572 sub complete_migration_storage
{
8573 my ($cmd, $param, $current_value, $all_args) = @_;
8575 my $targetnode = @$all_args[1];
8577 my $cfg = PVE
::Storage
::config
();
8578 my $ids = $cfg->{ids
};
8581 foreach my $sid (keys %$ids) {
8582 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, $targetnode, 1);
8583 next if !$ids->{$sid}->{content
}->{images
};
8592 my $qmpstatus = eval {
8593 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid);
8594 mon_cmd
($vmid, "query-status");
8597 return $qmpstatus && $qmpstatus->{status
} eq "paused";
8600 sub check_volume_storage_type
{
8601 my ($storecfg, $vol) = @_;
8603 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($vol);
8604 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8605 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $vol);
8607 die "storage '$storeid' does not support content-type '$vtype'\n"
8608 if !$scfg->{content
}->{$vtype};
8613 sub add_nets_bridge_fdb
{
8614 my ($conf, $vmid) = @_;
8616 for my $opt (keys %$conf) {
8617 next if $opt !~ m/^net(\d+)$/;
8618 my $iface = "tap${vmid}i$1";
8619 # NOTE: expect setups with learning off to *not* use auto-random-generation of MAC on start
8620 my $net = parse_net
($conf->{$opt}, 1) or next;
8622 my $mac = $net->{macaddr
};
8624 log_warn
("MAC learning disabled, but vNIC '$iface' has no static MAC to add to forwarding DB!")
8625 if !file_read_firstline
("/sys/class/net/$iface/brport/learning");
8629 my $bridge = $net->{bridge
};
8631 log_warn
("Interface '$iface' not attached to any bridge.");
8635 PVE
::Network
::SDN
::Zones
::add_bridge_fdb
($iface, $mac, $bridge, $net->{firewall
});
8636 } elsif (-d
"/sys/class/net/$bridge/bridge") { # avoid fdb management with OVS for now
8637 PVE
::Network
::add_bridge_fdb
($iface, $mac, $net->{firewall
});
8642 sub del_nets_bridge_fdb
{
8643 my ($conf, $vmid) = @_;
8645 for my $opt (keys %$conf) {
8646 next if $opt !~ m/^net(\d+)$/;
8647 my $iface = "tap${vmid}i$1";
8649 my $net = parse_net
($conf->{$opt}) or next;
8650 my $mac = $net->{macaddr
} or next;
8652 my $bridge = $net->{bridge
};
8654 PVE
::Network
::SDN
::Zones
::del_bridge_fdb
($iface, $mac, $bridge, $net->{firewall
});
8655 } elsif (-d
"/sys/class/net/$bridge/bridge") { # avoid fdb management with OVS for now
8656 PVE
::Network
::del_bridge_fdb
($iface, $mac, $net->{firewall
});