1 package PVE
::QemuServer
;
11 use File
::Copy
qw(copy);
22 use List
::Util
qw(first);
25 use Storable
qw(dclone);
26 use Time
::HiRes
qw(gettimeofday usleep);
30 use PVE
::Cluster
qw(cfs_register_file cfs_read_file cfs_write_file);
33 use PVE
::DataCenterConfig
;
34 use PVE
::Exception
qw(raise raise_param_exc);
35 use PVE
::Format
qw(render_duration render_bytes);
36 use PVE
::GuestHelpers
qw(safe_string_ne safe_num_ne safe_boolean_ne);
37 use PVE
::Mapping
::PCI
;
38 use PVE
::Mapping
::USB
;
40 use PVE
::JSONSchema
qw(get_standard_option parse_property_string);
43 use PVE
::RESTEnvironment
qw(log_warn);
44 use PVE
::RPCEnvironment
;
48 use PVE
::Tools
qw(run_command file_read_firstline file_get_contents dir_glob_foreach get_host_arch $IPV6RE);
52 use PVE
::QemuServer
::Helpers
qw(config_aware_timeout min_version windows_version);
53 use PVE
::QemuServer
::Cloudinit
;
54 use PVE
::QemuServer
::CGroup
;
55 use PVE
::QemuServer
::CPUConfig
qw(print_cpu_device get_cpu_options);
56 use PVE
::QemuServer
::Drive
qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom drive_is_read_only parse_drive print_drive);
57 use PVE
::QemuServer
::Machine
;
58 use PVE
::QemuServer
::Memory
qw(get_current_memory);
59 use PVE
::QemuServer
::Monitor
qw(mon_cmd);
60 use PVE
::QemuServer
::PCI
qw(print_pci_addr print_pcie_addr print_pcie_root_port parse_hostpci);
61 use PVE
::QemuServer
::QMPHelpers
qw(qemu_deviceadd qemu_devicedel qemu_objectadd qemu_objectdel);
62 use PVE
::QemuServer
::USB
;
66 require PVE
::Network
::SDN
::Zones
;
67 require PVE
::Network
::SDN
::Vnets
;
71 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
75 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
76 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
79 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
80 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
83 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
84 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
87 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
88 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
90 # FIXME: These are legacy 2MB-sized images that modern OVMF doesn't supports to build
91 # anymore. how can we deperacate this sanely without breaking existing instances, or using
92 # older backups and snapshot?
94 "$EDK2_FW_BASE/OVMF_CODE.fd",
95 "$EDK2_FW_BASE/OVMF_VARS.fd",
100 "$EDK2_FW_BASE/AAVMF_CODE.fd",
101 "$EDK2_FW_BASE/AAVMF_VARS.fd",
106 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
108 # Note about locking: we use flock on the config file protect against concurent actions.
109 # Aditionaly, we have a 'lock' setting in the config file. This can be set to 'migrate',
110 # 'backup', 'snapshot' or 'rollback'. Most actions are not allowed when such lock is set.
111 # But you can ignore this kind of lock with the --skiplock flag.
119 PVE
::JSONSchema
::register_standard_option
('pve-qm-stateuri', {
120 description
=> "Some command save/restore state from this location.",
126 PVE
::JSONSchema
::register_standard_option
('pve-qemu-machine', {
127 description
=> "Specifies the QEMU machine type.",
129 pattern
=> '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
134 # FIXME: remove in favor of just using the INotify one, it's cached there exactly the same way
137 $nodename_cache //= PVE
::INotify
::nodename
();
138 return $nodename_cache;
145 enum
=> [qw(i6300esb ib700)],
146 description
=> "Watchdog type to emulate.",
147 default => 'i6300esb',
152 enum
=> [qw(reset shutdown poweroff pause debug none)],
153 description
=> "The action to perform if after activation the guest fails to poll the watchdog in time.",
157 PVE
::JSONSchema
::register_format
('pve-qm-watchdog', $watchdog_fmt);
161 description
=> "Enable/disable communication with a QEMU Guest Agent (QGA) running in the VM.",
166 fstrim_cloned_disks
=> {
167 description
=> "Run fstrim after moving a disk or migrating the VM.",
172 'freeze-fs-on-backup' => {
173 description
=> "Freeze/thaw guest filesystems on backup for consistency.",
179 description
=> "Select the agent type",
183 enum
=> [qw(virtio isa)],
189 description
=> "Select the VGA type.",
194 enum
=> [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio virtio-gl vmware)],
197 description
=> "Sets the VGA memory (in MiB). Has no effect with serial display.",
204 description
=> 'Enable a specific clipboard. If not set, depending on'
205 .' the display type the SPICE one will be added.',
216 description
=> "The size of the file in MB.",
220 pattern
=> '[a-zA-Z0-9\-]+',
222 format_description
=> 'string',
223 description
=> "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
230 enum
=> [qw(ich9-intel-hda intel-hda AC97)],
231 description
=> "Configure an audio device."
235 enum
=> ['spice', 'none'],
238 description
=> "Driver backend for the audio device."
242 my $spice_enhancements_fmt = {
247 description
=> "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
251 enum
=> ['off', 'all', 'filter'],
254 description
=> "Enable video streaming. Uses compression for detected video streams."
261 enum
=> ['/dev/urandom', '/dev/random', '/dev/hwrng'],
263 description
=> "The file on the host to gather entropy from. In most cases '/dev/urandom'"
264 ." should be preferred over '/dev/random' to avoid entropy-starvation issues on the"
265 ." host. Using urandom does *not* decrease security in any meaningful way, as it's"
266 ." still seeded from real entropy, and the bytes provided will most likely be mixed"
267 ." with real entropy on the guest as well. '/dev/hwrng' can be used to pass through"
268 ." a hardware RNG from the host.",
272 description
=> "Maximum bytes of entropy allowed to get injected into the guest every"
273 ." 'period' milliseconds. Prefer a lower value when using '/dev/random' as source. Use"
274 ." `0` to disable limiting (potentially dangerous!).",
277 # default is 1 KiB/s, provides enough entropy to the guest to avoid boot-starvation issues
278 # (e.g. systemd etc...) while allowing no chance of overwhelming the host, provided we're
279 # reading from /dev/urandom
284 description
=> "Every 'period' milliseconds the entropy-injection quota is reset, allowing"
285 ." the guest to retrieve another 'max_bytes' of entropy.",
291 my $meta_info_fmt = {
294 description
=> "The guest creation timestamp as UNIX epoch time",
300 description
=> "The QEMU (machine) version from the time this VM was created.",
301 pattern
=> '\d+(\.\d+)+',
310 description
=> "Specifies whether a VM will be started during system bootup.",
316 description
=> "Automatic restart after crash (currently ignored).",
321 type
=> 'string', format
=> 'pve-hotplug-features',
322 description
=> "Selectively enable hotplug features. This is a comma separated list of"
323 ." hotplug features: 'network', 'disk', 'cpu', 'memory', 'usb' and 'cloudinit'. Use '0' to disable"
324 ." hotplug completely. Using '1' as value is an alias for the default `network,disk,usb`."
325 ." USB hotplugging is possible for guests with machine version >= 7.1 and ostype l26 or"
327 default => 'network,disk,usb',
332 description
=> "Allow reboot. If set to '0' the VM exit on reboot.",
338 description
=> "Lock/unlock the VM.",
339 enum
=> [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
344 description
=> "Limit of CPU usage.",
345 verbose_description
=> "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has"
346 ." total of '2' CPU time. Value '0' indicates no CPU limit.",
354 description
=> "CPU weight for a VM, will be clamped to [1, 10000] in cgroup v2.",
355 verbose_description
=> "CPU weight for a VM. Argument is used in the kernel fair scheduler."
356 ." The larger the number is, the more CPU time this VM gets. Number is relative to"
357 ." weights of all the other running VMs.",
360 default => 'cgroup v1: 1024, cgroup v2: 100',
365 description
=> "Memory properties.",
366 format
=> $PVE::QemuServer
::Memory
::memory_fmt
371 description
=> "Amount of target RAM for the VM in MiB. Using zero disables the ballon driver.",
377 description
=> "Amount of memory shares for auto-ballooning. The larger the number is, the"
378 ." more memory this VM gets. Number is relative to weights of all other running VMs."
379 ." Using zero disables auto-ballooning. Auto-ballooning is done by pvestatd.",
387 description
=> "Keyboard layout for VNC server. This option is generally not required and"
388 ." is often better handled from within the guest OS.",
389 enum
=> PVE
::Tools
::kvmkeymaplist
(),
394 type
=> 'string', format
=> 'dns-name',
395 description
=> "Set a name for the VM. Only used on the configuration web interface.",
400 description
=> "SCSI controller model",
401 enum
=> [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
407 description
=> "Description for the VM. Shown in the web-interface VM's summary."
408 ." This is saved as comment inside the configuration file.",
409 maxLength
=> 1024 * 8,
414 enum
=> [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 win11 l24 l26 solaris)],
415 description
=> "Specify guest operating system.",
416 verbose_description
=> <<EODESC,
417 Specify guest operating system. This is used to enable special
418 optimization/features for specific operating systems:
421 other;; unspecified OS
422 wxp;; Microsoft Windows XP
423 w2k;; Microsoft Windows 2000
424 w2k3;; Microsoft Windows 2003
425 w2k8;; Microsoft Windows 2008
426 wvista;; Microsoft Windows Vista
427 win7;; Microsoft Windows 7
428 win8;; Microsoft Windows 8/2012/2012r2
429 win10;; Microsoft Windows 10/2016/2019
430 win11;; Microsoft Windows 11/2022
431 l24;; Linux 2.4 Kernel
432 l26;; Linux 2.6 - 6.X Kernel
433 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
438 type
=> 'string', format
=> 'pve-qm-boot',
439 description
=> "Specify guest boot order. Use the 'order=' sub-property as usage with no"
440 ." key or 'legacy=' is deprecated.",
444 type
=> 'string', format
=> 'pve-qm-bootdisk',
445 description
=> "Enable booting from specified disk. Deprecated: Use 'boot: order=foo;bar' instead.",
446 pattern
=> '(ide|sata|scsi|virtio)\d+',
451 description
=> "The number of CPUs. Please use option -sockets instead.",
458 description
=> "The number of CPU sockets.",
465 description
=> "The number of cores per socket.",
472 description
=> "Enable/disable NUMA.",
478 description
=> "Enable/disable hugepages memory.",
479 enum
=> [qw(any 2 1024)],
485 description
=> "Use together with hugepages. If enabled, hugepages will not not be deleted"
486 ." after VM shutdown and can be used for subsequent starts.",
491 description
=> "Number of hotplugged vcpus.",
498 description
=> "Enable/disable ACPI.",
503 description
=> "Enable/disable communication with the QEMU Guest Agent and its properties.",
505 format
=> $agent_fmt,
510 description
=> "Enable/disable KVM hardware virtualization.",
516 description
=> "Enable/disable time drift fix.",
522 description
=> "Set the real time clock (RTC) to local time. This is enabled by default if"
523 ." the `ostype` indicates a Microsoft Windows OS.",
528 description
=> "Freeze CPU at startup (use 'c' monitor command to start execution).",
532 type
=> 'string', format
=> $vga_fmt,
533 description
=> "Configure the VGA hardware.",
534 verbose_description
=> "Configure the VGA Hardware. If you want to use high resolution"
535 ." modes (>= 1280x1024x16) you may need to increase the vga memory option. Since QEMU"
536 ." 2.9 the default VGA display type is 'std' for all OS types besides some Windows"
537 ." versions (XP and older) which use 'cirrus'. The 'qxl' option enables the SPICE"
538 ." display server. For win* OS you can select how many independent displays you want,"
539 ." Linux guests can add displays them self.\nYou can also run without any graphic card,"
540 ." using a serial device as terminal.",
544 type
=> 'string', format
=> 'pve-qm-watchdog',
545 description
=> "Create a virtual hardware watchdog device.",
546 verbose_description
=> "Create a virtual hardware watchdog device. Once enabled (by a guest"
547 ." action), the watchdog must be periodically polled by an agent inside the guest or"
548 ." else the watchdog will reset the guest (or execute the respective action specified)",
553 typetext
=> "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
554 description
=> "Set the initial date of the real time clock. Valid format for date are:"
555 ."'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
556 pattern
=> '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
559 startup
=> get_standard_option
('pve-startup-order'),
563 description
=> "Enable/disable Template.",
569 description
=> "Arbitrary arguments passed to kvm.",
570 verbose_description
=> <<EODESCR,
571 Arbitrary arguments passed to kvm, for example:
573 args: -no-reboot -smbios 'type=0,vendor=FOO'
575 NOTE: this option is for experts only.
582 description
=> "Enable/disable the USB tablet device.",
583 verbose_description
=> "Enable/disable the USB tablet device. This device is usually needed"
584 ." to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with"
585 ." normal VNC clients. If you're running lots of console-only guests on one host, you"
586 ." may consider disabling this to save some context switches. This is turned off by"
587 ." default if you use spice (`qm set <vmid> --vga qxl`).",
592 description
=> "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
596 migrate_downtime
=> {
599 description
=> "Set maximum tolerated downtime (in seconds) for migrations.",
605 type
=> 'string', format
=> 'pve-qm-ide',
606 typetext
=> '<volume>',
607 description
=> "This is an alias for option -ide2",
611 description
=> "Emulated CPU type.",
613 format
=> 'pve-vm-cpu-conf',
615 parent
=> get_standard_option
('pve-snapshot-name', {
617 description
=> "Parent snapshot name. This is used internally, and should not be modified.",
621 description
=> "Timestamp for snapshots.",
627 type
=> 'string', format
=> 'pve-volume-id',
628 description
=> "Reference to a volume which stores the VM state. This is used internally"
631 vmstatestorage
=> get_standard_option
('pve-storage-id', {
632 description
=> "Default storage for VM state volumes/files.",
635 runningmachine
=> get_standard_option
('pve-qemu-machine', {
636 description
=> "Specifies the QEMU machine type of the running vm. This is used internally"
640 description
=> "Specifies the QEMU '-cpu' parameter of the running vm. This is used"
641 ." internally for snapshots.",
644 pattern
=> $PVE::QemuServer
::CPUConfig
::qemu_cmdline_cpu_re
,
645 format_description
=> 'QEMU -cpu parameter'
647 machine
=> get_standard_option
('pve-qemu-machine'),
649 description
=> "Virtual processor architecture. Defaults to the host.",
652 enum
=> [qw(x86_64 aarch64)],
655 description
=> "Specify SMBIOS type 1 fields.",
656 type
=> 'string', format
=> 'pve-qm-smbios1',
663 description
=> "Sets the protection flag of the VM. This will disable the remove VM and"
664 ." remove disk operations.",
670 enum
=> [ qw(seabios ovmf) ],
671 description
=> "Select BIOS implementation.",
672 default => 'seabios',
676 pattern
=> '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
677 format_description
=> 'UUID',
678 description
=> "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0'"
679 ." to disable explicitly.",
680 verbose_description
=> "The VM generation ID (vmgenid) device exposes a 128-bit integer"
681 ." value identifier to the guest OS. This allows to notify the guest operating system"
682 ." when the virtual machine is executed with a different configuration (e.g. snapshot"
683 ." execution or creation from a template). The guest operating system notices the"
684 ." change, and is then able to react as appropriate by marking its copies of"
685 ." distributed databases as dirty, re-initializing its random number generator, etc.\n"
686 ."Note that auto-creation only works when done through API/CLI create or update methods"
687 .", but not when manually editing the config file.",
688 default => "1 (autogenerated)",
693 format
=> 'pve-volume-id',
695 description
=> "Script that will be executed during various steps in the vms lifetime.",
699 format
=> $ivshmem_fmt,
700 description
=> "Inter-VM shared memory. Useful for direct communication between VMs, or to"
706 format
=> $audio_fmt,
707 description
=> "Configure a audio device, useful in combination with QXL/Spice.",
710 spice_enhancements
=> {
712 format
=> $spice_enhancements_fmt,
713 description
=> "Configure additional enhancements for SPICE.",
717 type
=> 'string', format
=> 'pve-tag-list',
718 description
=> 'Tags of the VM. This is only meta information.',
724 description
=> "Configure a VirtIO-based Random Number Generator.",
729 format
=> $meta_info_fmt,
730 description
=> "Some (read-only) meta-information about this guest.",
734 type
=> 'string', format
=> 'pve-cpuset',
735 description
=> "List of host cores used to execute guest processes, for example: 0,5,8-11",
744 description
=> 'Specify a custom file containing all meta data passed to the VM via"
745 ." cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
746 format
=> 'pve-volume-id',
747 format_description
=> 'volume',
752 description
=> 'To pass a custom file containing all network data to the VM via cloud-init.',
753 format
=> 'pve-volume-id',
754 format_description
=> 'volume',
759 description
=> 'To pass a custom file containing all user data to the VM via cloud-init.',
760 format
=> 'pve-volume-id',
761 format_description
=> 'volume',
766 description
=> 'To pass a custom file containing all vendor data to the VM via cloud-init.',
767 format
=> 'pve-volume-id',
768 format_description
=> 'volume',
771 PVE
::JSONSchema
::register_format
('pve-qm-cicustom', $cicustom_fmt);
773 # any new option might need to be added to $cloudinitoptions in PVE::API2::Qemu
774 my $confdesc_cloudinit = {
778 description
=> 'Specifies the cloud-init configuration format. The default depends on the'
779 .' configured operating system type (`ostype`. We use the `nocloud` format for Linux,'
780 .' and `configdrive2` for windows.',
781 enum
=> ['configdrive2', 'nocloud', 'opennebula'],
786 description
=> "cloud-init: User name to change ssh keys and password for instead of the"
787 ." image's configured default user.",
792 description
=> 'cloud-init: Password to assign the user. Using this is generally not'
793 .' recommended. Use ssh keys instead. Also note that older cloud-init versions do not'
794 .' support hashed passwords.',
799 description
=> 'cloud-init: do an automatic package upgrade after the first boot.',
805 description
=> 'cloud-init: Specify custom files to replace the automatically generated'
807 format
=> 'pve-qm-cicustom',
812 description
=> 'cloud-init: Sets DNS search domains for a container. Create will'
813 .' automatically use the setting from the host if neither searchdomain nor nameserver'
818 type
=> 'string', format
=> 'address-list',
819 description
=> 'cloud-init: Sets DNS server IP address for a container. Create will'
820 .' automatically use the setting from the host if neither searchdomain nor nameserver'
826 format
=> 'urlencoded',
827 description
=> "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
831 # what about other qemu settings ?
833 #machine => 'string',
846 ##soundhw => 'string',
848 while (my ($k, $v) = each %$confdesc) {
849 PVE
::JSONSchema
::register_standard_option
("pve-qm-$k", $v);
853 my $MAX_SERIAL_PORTS = 4;
854 my $MAX_PARALLEL_PORTS = 3;
856 for (my $i = 0; $i < $PVE::QemuServer
::Memory
::MAX_NUMA
; $i++) {
857 $confdesc->{"numa$i"} = $PVE::QemuServer
::Memory
::numadesc
;
860 my $nic_model_list = [
876 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
878 my $net_fmt_bridge_descr = <<__EOD__;
879 Bridge to attach the network device to. The Proxmox VE standard bridge
882 If you do not specify a bridge, we create a kvm user (NATed) network
883 device, which provides DHCP and DNS services. The following addresses
890 The DHCP server assign addresses to the guest starting from 10.0.2.15.
894 macaddr
=> get_standard_option
('mac-addr', {
895 description
=> "MAC address. That address must be unique withing your network. This is"
896 ." automatically generated if not specified.",
900 description
=> "Network Card Model. The 'virtio' model provides the best performance with"
901 ." very low CPU overhead. If your guest does not support this driver, it is usually"
902 ." best to use 'e1000'.",
903 enum
=> $nic_model_list,
906 (map { $_ => { keyAlias
=> 'model', alias
=> 'macaddr' }} @$nic_model_list),
907 bridge
=> get_standard_option
('pve-bridge-id', {
908 description
=> $net_fmt_bridge_descr,
913 minimum
=> 0, maximum
=> 64,
914 description
=> 'Number of packet queues to be used on the device.',
920 description
=> "Rate limit in mbps (megabytes per second) as floating point number.",
925 minimum
=> 1, maximum
=> 4094,
926 description
=> 'VLAN tag to apply to packets on this interface.',
931 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
932 description
=> 'VLAN trunks to pass through this interface.',
933 format_description
=> 'vlanid[;vlanid...]',
938 description
=> 'Whether this interface should be protected by the firewall.',
943 description
=> 'Whether this interface should be disconnected (like pulling the plug).',
948 minimum
=> 1, maximum
=> 65520,
949 description
=> "Force MTU, for VirtIO only. Set to '1' to use the bridge MTU",
956 type
=> 'string', format
=> $net_fmt,
957 description
=> "Specify network devices.",
960 PVE
::JSONSchema
::register_standard_option
("pve-qm-net", $netdesc);
965 format
=> 'pve-ipv4-config',
966 format_description
=> 'IPv4Format/CIDR',
967 description
=> 'IPv4 address in CIDR format.',
974 format_description
=> 'GatewayIPv4',
975 description
=> 'Default gateway for IPv4 traffic.',
981 format
=> 'pve-ipv6-config',
982 format_description
=> 'IPv6Format/CIDR',
983 description
=> 'IPv6 address in CIDR format.',
990 format_description
=> 'GatewayIPv6',
991 description
=> 'Default gateway for IPv6 traffic.',
996 PVE
::JSONSchema
::register_format
('pve-qm-ipconfig', $ipconfig_fmt);
999 type
=> 'string', format
=> 'pve-qm-ipconfig',
1000 description
=> <<'EODESCR',
1001 cloud-init: Specify IP addresses and gateways for the corresponding interface.
1003 IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
1005 The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit
1006 gateway should be provided.
1007 For IPv6 the special string 'auto' can be used to use stateless autoconfiguration. This requires
1008 cloud-init 19.4 or newer.
1010 If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using
1014 PVE
::JSONSchema
::register_standard_option
("pve-qm-ipconfig", $netdesc);
1016 for (my $i = 0; $i < $MAX_NETS; $i++) {
1017 $confdesc->{"net$i"} = $netdesc;
1018 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
1021 foreach my $key (keys %$confdesc_cloudinit) {
1022 $confdesc->{$key} = $confdesc_cloudinit->{$key};
1025 PVE
::JSONSchema
::register_format
('pve-cpuset', \
&pve_verify_cpuset
);
1026 sub pve_verify_cpuset
{
1027 my ($set_text, $noerr) = @_;
1029 my ($count, $members) = eval { PVE
::CpuSet
::parse_cpuset
($set_text) };
1033 die "unable to parse cpuset option\n";
1036 return PVE
::CpuSet-
>new($members)->short_string();
1039 PVE
::JSONSchema
::register_format
('pve-volume-id-or-qm-path', \
&verify_volume_id_or_qm_path
);
1040 sub verify_volume_id_or_qm_path
{
1041 my ($volid, $noerr) = @_;
1043 return $volid if $volid eq 'none' || $volid eq 'cdrom';
1045 return verify_volume_id_or_absolute_path
($volid, $noerr);
1048 PVE
::JSONSchema
::register_format
('pve-volume-id-or-absolute-path', \
&verify_volume_id_or_absolute_path
);
1049 sub verify_volume_id_or_absolute_path
{
1050 my ($volid, $noerr) = @_;
1052 return $volid if $volid =~ m
|^/|;
1054 $volid = eval { PVE
::JSONSchema
::check_format
('pve-volume-id', $volid, '') };
1065 pattern
=> '(/dev/.+|socket)',
1066 description
=> "Create a serial device inside the VM (n is 0 to 3)",
1067 verbose_description
=> <<EODESCR,
1068 Create a serial device inside the VM (n is 0 to 3), and pass through a
1069 host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1070 host side (use 'qm terminal' to open a terminal connection).
1072 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines -
1073 use with special care.
1075 CAUTION: Experimental! User reported problems with this option.
1082 pattern
=> '/dev/parport\d+|/dev/usb/lp\d+',
1083 description
=> "Map host parallel devices (n is 0 to 2).",
1084 verbose_description
=> <<EODESCR,
1085 Map host parallel devices (n is 0 to 2).
1087 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1088 machines - use with special care.
1090 CAUTION: Experimental! User reported problems with this option.
1094 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1095 $confdesc->{"parallel$i"} = $paralleldesc;
1098 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1099 $confdesc->{"serial$i"} = $serialdesc;
1102 for (my $i = 0; $i < $PVE::QemuServer
::PCI
::MAX_HOSTPCI_DEVICES
; $i++) {
1103 $confdesc->{"hostpci$i"} = $PVE::QemuServer
::PCI
::hostpcidesc
;
1106 for my $key (keys %{$PVE::QemuServer
::Drive
::drivedesc_hash
}) {
1107 $confdesc->{$key} = $PVE::QemuServer
::Drive
::drivedesc_hash-
>{$key};
1110 for (my $i = 0; $i < $PVE::QemuServer
::USB
::MAX_USB_DEVICES
; $i++) {
1111 $confdesc->{"usb$i"} = $PVE::QemuServer
::USB
::usbdesc
;
1119 description
=> "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)."
1120 . " Deprecated, use 'order=' instead.",
1121 pattern
=> '[acdn]{1,4}',
1122 format_description
=> "[acdn]{1,4}",
1124 # note: this is also the fallback if boot: is not given at all
1130 format
=> 'pve-qm-bootdev-list',
1131 format_description
=> "device[;device...]",
1132 description
=> <<EODESC,
1133 The guest will attempt to boot from devices in the order they appear here.
1135 Disks, optical drives and passed-through storage USB devices will be directly
1136 booted from, NICs will load PXE, and PCIe devices will either behave like disks
1137 (e.g. NVMe) or load an option ROM (e.g. RAID controller, hardware NIC).
1139 Note that only devices in this list will be marked as bootable and thus loaded
1140 by the guest firmware (BIOS/UEFI). If you require multiple disks for booting
1141 (e.g. software-raid), you need to specify all of them here.
1143 Overrides the deprecated 'legacy=[acdn]*' value when given.
1147 PVE
::JSONSchema
::register_format
('pve-qm-boot', $boot_fmt);
1149 PVE
::JSONSchema
::register_format
('pve-qm-bootdev', \
&verify_bootdev
);
1150 sub verify_bootdev
{
1151 my ($dev, $noerr) = @_;
1153 my $special = $dev =~ m/^efidisk/ || $dev =~ m/^tpmstate/;
1154 return $dev if PVE
::QemuServer
::Drive
::is_valid_drivename
($dev) && !$special;
1158 return 0 if $dev !~ m/^$base\d+$/;
1159 return 0 if !$confdesc->{$dev};
1163 return $dev if $check->("net");
1164 return $dev if $check->("usb");
1165 return $dev if $check->("hostpci");
1168 die "invalid boot device '$dev'\n";
1171 sub print_bootorder
{
1173 return "" if !@$devs;
1174 my $data = { order
=> join(';', @$devs) };
1175 return PVE
::JSONSchema
::print_property_string
($data, $boot_fmt);
1178 my $kvm_api_version = 0;
1181 return $kvm_api_version if $kvm_api_version;
1183 open my $fh, '<', '/dev/kvm' or return;
1185 # 0xae00 => KVM_GET_API_VERSION
1186 $kvm_api_version = ioctl($fh, 0xae00, 0);
1189 return $kvm_api_version;
1192 my $kvm_user_version = {};
1195 sub kvm_user_version
{
1198 $binary //= get_command_for_arch
(get_host_arch
()); # get the native arch by default
1199 my $st = stat($binary);
1201 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1202 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1203 $cachedmtime == $st->mtime;
1205 $kvm_user_version->{$binary} = 'unknown';
1206 $kvm_mtime->{$binary} = $st->mtime;
1210 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1211 $kvm_user_version->{$binary} = $2;
1215 eval { run_command
([$binary, '--version'], outfunc
=> $code); };
1218 return $kvm_user_version->{$binary};
1221 my sub extract_version
{
1222 my ($machine_type, $version) = @_;
1223 $version = kvm_user_version
() if !defined($version);
1224 return PVE
::QemuServer
::Machine
::extract_version
($machine_type, $version)
1227 sub kernel_has_vhost_net
{
1228 return -c
'/dev/vhost-net';
1233 return defined($confdesc->{$key});
1237 sub get_cdrom_path
{
1239 return $cdrom_path if defined($cdrom_path);
1241 $cdrom_path = first
{ -l
$_ } map { "/dev/cdrom$_" } ('', '1', '2');
1243 if (!defined($cdrom_path)) {
1244 log_warn
("no physical CD-ROM available, ignoring");
1252 my ($storecfg, $vmid, $cdrom) = @_;
1254 if ($cdrom eq 'cdrom') {
1255 return get_cdrom_path
();
1256 } elsif ($cdrom eq 'none') {
1258 } elsif ($cdrom =~ m
|^/|) {
1261 return PVE
::Storage
::path
($storecfg, $cdrom);
1265 # try to convert old style file names to volume IDs
1266 sub filename_to_volume_id
{
1267 my ($vmid, $file, $media) = @_;
1269 if (!($file eq 'none' || $file eq 'cdrom' ||
1270 $file =~ m
|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1272 return if $file =~ m
|/|;
1274 if ($media && $media eq 'cdrom') {
1275 $file = "local:iso/$file";
1277 $file = "local:$vmid/$file";
1284 sub verify_media_type
{
1285 my ($opt, $vtype, $media) = @_;
1290 if ($media eq 'disk') {
1292 } elsif ($media eq 'cdrom') {
1295 die "internal error";
1298 return if ($vtype eq $etype);
1300 raise_param_exc
({ $opt => "unexpected media type ($vtype != $etype)" });
1303 sub cleanup_drive_path
{
1304 my ($opt, $storecfg, $drive) = @_;
1306 # try to convert filesystem paths to volume IDs
1308 if (($drive->{file
} !~ m/^(cdrom|none)$/) &&
1309 ($drive->{file
} !~ m
|^/dev/.+|) &&
1310 ($drive->{file
} !~ m/^([^:]+):(.+)$/) &&
1311 ($drive->{file
} !~ m/^\d+$/)) {
1312 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($storecfg, $drive->{file
});
1313 raise_param_exc
({ $opt => "unable to associate path '$drive->{file}' to any storage"})
1315 $drive->{media
} = 'cdrom' if !$drive->{media
} && $vtype eq 'iso';
1316 verify_media_type
($opt, $vtype, $drive->{media
});
1317 $drive->{file
} = $volid;
1320 $drive->{media
} = 'cdrom' if !$drive->{media
} && $drive->{file
} =~ m/^(cdrom|none)$/;
1323 sub parse_hotplug_features
{
1328 return $res if $data eq '0';
1330 $data = $confdesc->{hotplug
}->{default} if $data eq '1';
1332 foreach my $feature (PVE
::Tools
::split_list
($data)) {
1333 if ($feature =~ m/^(network|disk|cpu|memory|usb|cloudinit)$/) {
1336 die "invalid hotplug feature '$feature'\n";
1342 PVE
::JSONSchema
::register_format
('pve-hotplug-features', \
&pve_verify_hotplug_features
);
1343 sub pve_verify_hotplug_features
{
1344 my ($value, $noerr) = @_;
1346 return $value if parse_hotplug_features
($value);
1350 die "unable to parse hotplug option\n";
1353 sub assert_clipboard_config
{
1356 my $clipboard_regex = qr/^(std|cirrus|vmware|virtio|qxl)/;
1360 && $vga->{'clipboard'} eq 'vnc'
1362 && $vga->{type
} !~ $clipboard_regex
1364 die "vga type $vga->{type} is not compatible with VNC clipboard\n";
1369 my($fh, $noerr) = @_;
1372 my $SG_GET_VERSION_NUM = 0x2282;
1374 my $versionbuf = "\x00" x
8;
1375 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1377 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
1380 my $version = unpack("I", $versionbuf);
1381 if ($version < 30000) {
1382 die "scsi generic interface too old\n" if !$noerr;
1386 my $buf = "\x00" x
36;
1387 my $sensebuf = "\x00" x
8;
1388 my $cmd = pack("C x3 C x1", 0x12, 36);
1390 # see /usr/include/scsi/sg.h
1391 my $sg_io_hdr_t = "i i C C s I P P P I I i P C C C C S S i I I";
1394 $sg_io_hdr_t, ord('S'), -3, length($cmd), length($sensebuf), 0, length($buf), $buf, $cmd, $sensebuf, 6000
1397 $ret = ioctl($fh, $SG_IO, $packet);
1399 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
1403 my @res = unpack($sg_io_hdr_t, $packet);
1404 if ($res[17] || $res[18]) {
1405 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
1410 $res->@{qw(type removable vendor product revision)} = unpack("C C x6 A8 A16 A4", $buf);
1412 $res->{removable
} = $res->{removable
} & 128 ?
1 : 0;
1413 $res->{type
} &= 0x1F;
1421 my $fh = IO
::File-
>new("+<$path") || return;
1422 my $res = scsi_inquiry
($fh, 1);
1428 sub print_tabletdevice_full
{
1429 my ($conf, $arch) = @_;
1431 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1433 # we use uhci for old VMs because tablet driver was buggy in older qemu
1435 if ($q35 || $arch eq 'aarch64') {
1441 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1444 sub print_keyboarddevice_full
{
1445 my ($conf, $arch) = @_;
1447 return if $arch ne 'aarch64';
1449 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1452 my sub get_drive_id
{
1454 return "$drive->{interface}$drive->{index}";
1457 sub print_drivedevice_full
{
1458 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1463 my $drive_id = get_drive_id
($drive);
1464 if ($drive->{interface
} eq 'virtio') {
1465 my $pciaddr = print_pci_addr
("$drive_id", $bridges, $arch, $machine_type);
1466 $device = "virtio-blk-pci,drive=drive-$drive_id,id=${drive_id}${pciaddr}";
1467 $device .= ",iothread=iothread-$drive_id" if $drive->{iothread
};
1468 } elsif ($drive->{interface
} eq 'scsi') {
1470 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
1471 my $unit = $drive->{index} % $maxdev;
1472 my $devicetype = 'hd';
1474 if (drive_is_cdrom
($drive)) {
1477 if ($drive->{file
} =~ m
|^/|) {
1478 $path = $drive->{file
};
1479 if (my $info = path_is_scsi
($path)) {
1480 if ($info->{type
} == 0 && $drive->{scsiblock
}) {
1481 $devicetype = 'block';
1482 } elsif ($info->{type
} == 1) { # tape
1483 $devicetype = 'generic';
1487 $path = PVE
::Storage
::path
($storecfg, $drive->{file
});
1490 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1491 my $version = extract_version
($machine_type, kvm_user_version
());
1492 if ($path =~ m/^iscsi\:\/\
// &&
1493 !min_version
($version, 4, 1)) {
1494 $devicetype = 'generic';
1498 if (!$conf->{scsihw
} || $conf->{scsihw
} =~ m/^lsi/ || $conf->{scsihw
} eq 'pvscsi') {
1499 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
1501 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
1502 .",lun=$drive->{index}";
1504 $device .= ",drive=drive-$drive_id,id=$drive_id";
1506 if ($drive->{ssd
} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1507 $device .= ",rotation_rate=1";
1509 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1511 } elsif ($drive->{interface
} eq 'ide' || $drive->{interface
} eq 'sata') {
1512 my $maxdev = ($drive->{interface
} eq 'sata') ?
$PVE::QemuServer
::Drive
::MAX_SATA_DISKS
: 2;
1513 my $controller = int($drive->{index} / $maxdev);
1514 my $unit = $drive->{index} % $maxdev;
1516 # machine type q35 only supports unit=0 for IDE rather than 2 units. This wasn't handled
1517 # correctly before, so e.g. index=2 was mapped to controller=1,unit=0 rather than
1518 # controller=2,unit=0. Note that odd indices never worked, as they would be mapped to
1519 # unit=1, so to keep backwards compat for migration, it suffices to keep even ones as they
1520 # were before. Move odd ones up by 2 where they don't clash.
1521 if (PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf) && $drive->{interface
} eq 'ide') {
1522 $controller += 2 * ($unit % 2);
1526 my $devicetype = ($drive->{media
} && $drive->{media
} eq 'cdrom') ?
"cd" : "hd";
1528 $device = "ide-$devicetype";
1529 if ($drive->{interface
} eq 'ide') {
1530 $device .= ",bus=ide.$controller,unit=$unit";
1532 $device .= ",bus=ahci$controller.$unit";
1534 $device .= ",drive=drive-$drive_id,id=$drive_id";
1536 if ($devicetype eq 'hd') {
1537 if (my $model = $drive->{model
}) {
1538 $model = URI
::Escape
::uri_unescape
($model);
1539 $device .= ",model=$model";
1541 if ($drive->{ssd
}) {
1542 $device .= ",rotation_rate=1";
1545 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1546 } elsif ($drive->{interface
} eq 'usb') {
1548 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1550 die "unsupported interface type";
1553 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex
};
1555 if (my $serial = $drive->{serial
}) {
1556 $serial = URI
::Escape
::uri_unescape
($serial);
1557 $device .= ",serial=$serial";
1564 sub get_initiator_name
{
1567 my $fh = IO
::File-
>new('/etc/iscsi/initiatorname.iscsi') || return;
1568 while (defined(my $line = <$fh>)) {
1569 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1578 my sub storage_allows_io_uring_default
{
1579 my ($scfg, $cache_direct) = @_;
1581 # io_uring with cache mode writeback or writethrough on krbd will hang...
1582 return if $scfg && $scfg->{type
} eq 'rbd' && $scfg->{krbd
} && !$cache_direct;
1584 # io_uring with cache mode writeback or writethrough on LVM will hang, without cache only
1585 # sometimes, just plain disable...
1586 return if $scfg && $scfg->{type
} eq 'lvm';
1588 # io_uring causes problems when used with CIFS since kernel 5.15
1589 # Some discussion: https://www.spinics.net/lists/linux-cifs/msg26734.html
1590 return if $scfg && $scfg->{type
} eq 'cifs';
1595 my sub drive_uses_cache_direct
{
1596 my ($drive, $scfg) = @_;
1598 my $cache_direct = 0;
1600 if (my $cache = $drive->{cache
}) {
1601 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1602 } elsif (!drive_is_cdrom
($drive) && !($scfg && $scfg->{type
} eq 'btrfs' && !$scfg->{nocow
})) {
1606 return $cache_direct;
1609 sub print_drive_commandline_full
{
1610 my ($storecfg, $vmid, $drive, $pbs_name, $io_uring) = @_;
1613 my $volid = $drive->{file
};
1614 my $format = $drive->{format
};
1615 my $drive_id = get_drive_id
($drive);
1617 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1618 my $scfg = $storeid ? PVE
::Storage
::storage_config
($storecfg, $storeid) : undef;
1620 if (drive_is_cdrom
($drive)) {
1621 $path = get_iso_path
($storecfg, $vmid, $volid);
1622 die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
1625 $path = PVE
::Storage
::path
($storecfg, $volid);
1626 $format //= qemu_img_format
($scfg, $volname);
1633 my $is_rbd = $path =~ m/^rbd:/;
1636 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1637 foreach my $o (@qemu_drive_options) {
1638 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1641 # snapshot only accepts on|off
1642 if (defined($drive->{snapshot
})) {
1643 my $v = $drive->{snapshot
} ?
'on' : 'off';
1644 $opts .= ",snapshot=$v";
1647 if (defined($drive->{ro
})) { # ro maps to QEMUs `readonly`, which accepts `on` or `off` only
1648 $opts .= ",readonly=" . ($drive->{ro
} ?
'on' : 'off');
1651 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1652 my ($dir, $qmpname) = @$type;
1653 if (my $v = $drive->{"mbps$dir"}) {
1654 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1656 if (my $v = $drive->{"mbps${dir}_max"}) {
1657 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1659 if (my $v = $drive->{"bps${dir}_max_length"}) {
1660 $opts .= ",throttling.bps$qmpname-max-length=$v";
1662 if (my $v = $drive->{"iops${dir}"}) {
1663 $opts .= ",throttling.iops$qmpname=$v";
1665 if (my $v = $drive->{"iops${dir}_max"}) {
1666 $opts .= ",throttling.iops$qmpname-max=$v";
1668 if (my $v = $drive->{"iops${dir}_max_length"}) {
1669 $opts .= ",throttling.iops$qmpname-max-length=$v";
1674 $format = "rbd" if $is_rbd;
1675 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1677 $opts .= ",format=alloc-track,file.driver=$format";
1679 $opts .= ",format=$format";
1682 my $cache_direct = drive_uses_cache_direct
($drive, $scfg);
1684 $opts .= ",cache=none" if !$drive->{cache
} && $cache_direct;
1686 if (!$drive->{aio
}) {
1687 if ($io_uring && storage_allows_io_uring_default
($scfg, $cache_direct)) {
1688 # io_uring supports all cache modes
1689 $opts .= ",aio=io_uring";
1691 # aio native works only with O_DIRECT
1693 $opts .= ",aio=native";
1695 $opts .= ",aio=threads";
1700 if (!drive_is_cdrom
($drive)) {
1702 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
1703 $detectzeroes = 'off';
1704 } elsif ($drive->{discard
}) {
1705 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
1707 # This used to be our default with discard not being specified:
1708 $detectzeroes = 'on';
1711 # note: 'detect-zeroes' works per blockdev and we want it to persist
1712 # after the alloc-track is removed, so put it on 'file' directly
1713 my $dz_param = $pbs_name ?
"file.detect-zeroes" : "detect-zeroes";
1714 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
1718 $opts .= ",backing=$pbs_name";
1719 $opts .= ",auto-remove=on";
1722 # my $file_param = $pbs_name ? "file.file.filename" : "file";
1723 my $file_param = "file";
1725 # non-rbd drivers require the underlying file to be a seperate block
1726 # node, so add a second .file indirection
1727 $file_param .= ".file" if !$is_rbd;
1728 $file_param .= ".filename";
1730 my $pathinfo = $path ?
"$file_param=$path," : '';
1732 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1735 sub print_pbs_blockdev
{
1736 my ($pbs_conf, $pbs_name) = @_;
1737 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1738 $blockdev .= ",repository=$pbs_conf->{repository}";
1739 $blockdev .= ",namespace=$pbs_conf->{namespace}" if $pbs_conf->{namespace
};
1740 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1741 $blockdev .= ",archive=$pbs_conf->{archive}";
1742 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile
};
1746 sub print_netdevice_full
{
1747 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version) = @_;
1749 my $device = $net->{model
};
1750 if ($net->{model
} eq 'virtio') {
1751 $device = 'virtio-net-pci';
1754 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
1755 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
1756 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
1757 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1758 # and out of each queue plus one config interrupt and control vector queue
1759 my $vectors = $net->{queues
} * 2 + 2;
1760 $tmpstr .= ",vectors=$vectors,mq=on";
1761 if (min_version
($machine_version, 7, 1)) {
1762 $tmpstr .= ",packed=on";
1766 if (min_version
($machine_version, 7, 1) && $net->{model
} eq 'virtio'){
1767 $tmpstr .= ",rx_queue_size=1024,tx_queue_size=256";
1770 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
1772 if (my $mtu = $net->{mtu
}) {
1773 if ($net->{model
} eq 'virtio' && $net->{bridge
}) {
1774 my $bridge_mtu = PVE
::Network
::read_bridge_mtu
($net->{bridge
});
1777 } elsif ($mtu < 576) {
1778 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1779 } elsif ($mtu > $bridge_mtu) {
1780 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1782 $tmpstr .= ",host_mtu=$mtu";
1784 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
1788 if ($use_old_bios_files) {
1790 if ($device eq 'virtio-net-pci') {
1791 $romfile = 'pxe-virtio.rom';
1792 } elsif ($device eq 'e1000') {
1793 $romfile = 'pxe-e1000.rom';
1794 } elsif ($device eq 'e1000e') {
1795 $romfile = 'pxe-e1000e.rom';
1796 } elsif ($device eq 'ne2k') {
1797 $romfile = 'pxe-ne2k_pci.rom';
1798 } elsif ($device eq 'pcnet') {
1799 $romfile = 'pxe-pcnet.rom';
1800 } elsif ($device eq 'rtl8139') {
1801 $romfile = 'pxe-rtl8139.rom';
1803 $tmpstr .= ",romfile=$romfile" if $romfile;
1809 sub print_netdev_full
{
1810 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
1813 if ($netid =~ m/^net(\d+)$/) {
1817 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
1819 my $ifname = "tap${vmid}i$i";
1821 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1822 die "interface name '$ifname' is too long (max 15 character)\n"
1823 if length($ifname) >= 16;
1825 my $vhostparam = '';
1826 if (is_native
($arch)) {
1827 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
1830 my $vmname = $conf->{name
} || "vm$vmid";
1833 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
1835 if ($net->{bridge
}) {
1836 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1837 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
1839 $netdev = "type=user,id=$netid,hostname=$vmname";
1842 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
1848 'cirrus' => 'cirrus-vga',
1850 'vmware' => 'vmware-svga',
1851 'virtio' => 'virtio-vga',
1852 'virtio-gl' => 'virtio-vga-gl',
1855 sub print_vga_device
{
1856 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
1858 my $type = $vga_map->{$vga->{type
}};
1859 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
1860 $type = 'virtio-gpu';
1862 my $vgamem_mb = $vga->{memory
};
1864 my $max_outputs = '';
1866 $type = $id ?
'qxl' : 'qxl-vga';
1868 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
1869 # set max outputs so linux can have up to 4 qxl displays with one device
1870 if (min_version
($machine_version, 4, 1)) {
1871 $max_outputs = ",max_outputs=4";
1876 die "no devicetype for $vga->{type}\n" if !$type;
1880 if ($vga->{type
} =~ /^virtio/) {
1881 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
1882 $memory = ",max_hostmem=$bytes";
1884 # from https://www.spice-space.org/multiple-monitors.html
1885 $memory = ",vgamem_mb=$vga->{memory}";
1886 my $ram = $vgamem_mb * 4;
1887 my $vram = $vgamem_mb * 2;
1888 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1890 $memory = ",vgamem_mb=$vga->{memory}";
1892 } elsif ($qxlnum && $id) {
1893 $memory = ",ram_size=67108864,vram_size=33554432";
1897 if ($type eq 'VGA' && windows_version
($conf->{ostype
})) {
1898 $edidoff=",edid=off" if (!defined($conf->{bios
}) || $conf->{bios
} ne 'ovmf');
1901 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1902 my $vgaid = "vga" . ($id // '');
1904 if ($q35 && $vgaid eq 'vga') {
1905 # the first display uses pcie.0 bus on q35 machines
1906 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
1908 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
1911 if ($vga->{type
} eq 'virtio-gl') {
1912 my $base = '/usr/lib/x86_64-linux-gnu/lib';
1913 die "missing libraries for '$vga->{type}' detected! Please install 'libgl1' and 'libegl1'\n"
1914 if !-e
"${base}EGL.so.1" || !-e
"${base}GL.so.1";
1916 die "no DRM render node detected (/dev/dri/renderD*), no GPU? - needed for '$vga->{type}' display\n"
1917 if !PVE
::Tools
::dir_glob_regex
('/dev/dri/', "renderD.*");
1920 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
1923 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1925 my ($data, $disable_mac_autogen) = @_;
1927 my $res = eval { parse_property_string
($net_fmt, $data) };
1932 if (!defined($res->{macaddr
}) && !$disable_mac_autogen) {
1933 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1934 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1939 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1940 sub parse_ipconfig
{
1943 my $res = eval { parse_property_string
($ipconfig_fmt, $data) };
1949 if ($res->{gw
} && !$res->{ip
}) {
1950 warn 'gateway specified without specifying an IP address';
1953 if ($res->{gw6
} && !$res->{ip6
}) {
1954 warn 'IPv6 gateway specified without specifying an IPv6 address';
1957 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
1958 warn 'gateway specified together with DHCP';
1961 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
1963 warn "IPv6 gateway specified together with $res->{ip6} address";
1967 if (!$res->{ip
} && !$res->{ip6
}) {
1968 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
1977 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
1980 sub add_random_macs
{
1981 my ($settings) = @_;
1983 foreach my $opt (keys %$settings) {
1984 next if $opt !~ m/^net(\d+)$/;
1985 my $net = parse_net
($settings->{$opt});
1987 $settings->{$opt} = print_net
($net);
1991 sub vm_is_volid_owner
{
1992 my ($storecfg, $vmid, $volid) = @_;
1994 if ($volid !~ m
|^/|) {
1996 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
1997 if ($owner && ($owner == $vmid)) {
2005 sub vmconfig_register_unused_drive
{
2006 my ($storecfg, $vmid, $conf, $drive) = @_;
2008 if (drive_is_cloudinit
($drive)) {
2009 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
2011 delete $conf->{cloudinit
};
2012 } elsif (!drive_is_cdrom
($drive)) {
2013 my $volid = $drive->{file
};
2014 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
2015 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
2020 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
2024 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
2025 format_description
=> 'UUID',
2026 description
=> "Set SMBIOS1 UUID.",
2031 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2032 format_description
=> 'Base64 encoded string',
2033 description
=> "Set SMBIOS1 version.",
2038 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2039 format_description
=> 'Base64 encoded string',
2040 description
=> "Set SMBIOS1 serial number.",
2045 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2046 format_description
=> 'Base64 encoded string',
2047 description
=> "Set SMBIOS1 manufacturer.",
2052 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2053 format_description
=> 'Base64 encoded string',
2054 description
=> "Set SMBIOS1 product ID.",
2059 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2060 format_description
=> 'Base64 encoded string',
2061 description
=> "Set SMBIOS1 SKU string.",
2066 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2067 format_description
=> 'Base64 encoded string',
2068 description
=> "Set SMBIOS1 family string.",
2073 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
2081 my $res = eval { parse_property_string
($smbios1_fmt, $data) };
2088 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2091 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2093 sub parse_watchdog
{
2098 my $res = eval { parse_property_string
($watchdog_fmt, $value) };
2103 sub parse_guest_agent
{
2106 return {} if !defined($conf->{agent
});
2108 my $res = eval { parse_property_string
($agent_fmt, $conf->{agent
}) };
2111 # if the agent is disabled ignore the other potentially set properties
2112 return {} if !$res->{enabled
};
2117 my ($conf, $key) = @_;
2118 return undef if !defined($conf->{agent
});
2120 my $agent = parse_guest_agent
($conf);
2121 return $agent->{$key};
2127 return {} if !$value;
2128 my $res = eval { parse_property_string
($vga_fmt, $value) };
2138 my $res = eval { parse_property_string
($rng_fmt, $value) };
2143 sub parse_meta_info
{
2148 my $res = eval { parse_property_string
($meta_info_fmt, $value) };
2153 sub new_meta_info_string
{
2154 my () = @_; # for now do not allow to override any value
2156 return PVE
::JSONSchema
::print_property_string
(
2158 'creation-qemu' => kvm_user_version
(),
2159 ctime
=> "". int(time()),
2165 sub qemu_created_version_fixups
{
2166 my ($conf, $forcemachine, $kvmver) = @_;
2168 my $meta = parse_meta_info
($conf->{meta
}) // {};
2169 my $forced_vers = PVE
::QemuServer
::Machine
::extract_version
($forcemachine);
2171 # check if we need to apply some handling for VMs that always use the latest machine version but
2172 # had a machine version transition happen that affected HW such that, e.g., an OS config change
2173 # would be required (we do not want to pin machine version for non-windows OS type)
2175 (!defined($conf->{machine
}) || $conf->{machine
} =~ m/^(?:pc|q35|virt)$/) # non-versioned machine
2176 && (!defined($meta->{'creation-qemu'}) || !min_version
($meta->{'creation-qemu'}, 6, 1)) # created before 6.1
2177 && (!$forced_vers || min_version
($forced_vers, 6, 1)) # handle snapshot-rollback/migrations
2178 && min_version
($kvmver, 6, 1) # only need to apply the change since 6.1
2180 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
2181 if ($q35 && $conf->{ostype
} && $conf->{ostype
} eq 'l26') {
2182 # this changed to default-on in Q 6.1 for q35 machines, it will mess with PCI slot view
2183 # and thus with the predictable interface naming of systemd
2184 return ['-global', 'ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off'];
2190 # add JSON properties for create and set function
2191 sub json_config_properties
{
2192 my ($prop, $with_disk_alloc) = @_;
2194 my $skip_json_config_opts = {
2198 runningmachine
=> 1,
2203 foreach my $opt (keys %$confdesc) {
2204 next if $skip_json_config_opts->{$opt};
2206 if ($with_disk_alloc && is_valid_drivename
($opt)) {
2207 $prop->{$opt} = $PVE::QemuServer
::Drive
::drivedesc_hash_with_alloc-
>{$opt};
2209 $prop->{$opt} = $confdesc->{$opt};
2216 # Properties that we can read from an OVF file
2217 sub json_ovf_properties
{
2220 for my $device (PVE
::QemuServer
::Drive
::valid_drive_names
()) {
2221 $prop->{$device} = {
2223 format
=> 'pve-volume-id-or-absolute-path',
2224 description
=> "Disk image that gets imported to $device",
2231 description
=> "The number of CPU cores.",
2236 description
=> "Amount of RAM for the VM in MB.",
2241 description
=> "Name of the VM.",
2248 # return copy of $confdesc_cloudinit to generate documentation
2249 sub cloudinit_config_properties
{
2251 return dclone
($confdesc_cloudinit);
2254 sub cloudinit_pending_properties
{
2256 map { $_ => 1 } keys $confdesc_cloudinit->%*,
2259 $p->{"net$_"} = 1 for 0..($MAX_NETS-1);
2264 my ($key, $value) = @_;
2266 die "unknown setting '$key'\n" if !$confdesc->{$key};
2268 my $type = $confdesc->{$key}->{type
};
2270 if (!defined($value)) {
2271 die "got undefined value\n";
2274 if ($value =~ m/[\n\r]/) {
2275 die "property contains a line feed\n";
2278 if ($type eq 'boolean') {
2279 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2280 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2281 die "type check ('boolean') failed - got '$value'\n";
2282 } elsif ($type eq 'integer') {
2283 return int($1) if $value =~ m/^(\d+)$/;
2284 die "type check ('integer') failed - got '$value'\n";
2285 } elsif ($type eq 'number') {
2286 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2287 die "type check ('number') failed - got '$value'\n";
2288 } elsif ($type eq 'string') {
2289 if (my $fmt = $confdesc->{$key}->{format
}) {
2290 PVE
::JSONSchema
::check_format
($fmt, $value);
2293 $value =~ s/^\"(.*)\"$/$1/;
2296 die "internal error"
2301 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
2303 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2305 if (!$skiplock && !PVE
::QemuConfig-
>has_lock($conf, 'suspended')) {
2306 PVE
::QemuConfig-
>check_lock($conf);
2309 if ($conf->{template
}) {
2310 # check if any base image is still used by a linked clone
2311 PVE
::QemuConfig-
>foreach_volume_full($conf, { include_unused
=> 1 }, sub {
2312 my ($ds, $drive) = @_;
2313 return if drive_is_cdrom
($drive);
2315 my $volid = $drive->{file
};
2316 return if !$volid || $volid =~ m
|^/|;
2318 die "base volume '$volid' is still in use by linked cloned\n"
2319 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2325 my $remove_owned_drive = sub {
2326 my ($ds, $drive) = @_;
2327 return if drive_is_cdrom
($drive, 1);
2329 my $volid = $drive->{file
};
2330 return if !$volid || $volid =~ m
|^/|;
2331 return if $volids->{$volid};
2333 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2334 return if !$path || !$owner || ($owner != $vmid);
2336 $volids->{$volid} = 1;
2337 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2338 warn "Could not remove disk '$volid', check manually: $@" if $@;
2341 # only remove disks owned by this VM (referenced in the config)
2342 my $include_opts = {
2343 include_unused
=> 1,
2344 extra_keys
=> ['vmstate'],
2346 PVE
::QemuConfig-
>foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2348 for my $snap (values %{$conf->{snapshots
}}) {
2349 next if !defined($snap->{vmstate
});
2350 my $drive = PVE
::QemuConfig-
>parse_volume('vmstate', $snap->{vmstate
}, 1);
2351 next if !defined($drive);
2352 $remove_owned_drive->('vmstate', $drive);
2355 PVE
::QemuConfig-
>foreach_volume_full($conf->{pending
}, $include_opts, $remove_owned_drive);
2357 if ($purge_unreferenced) { # also remove unreferenced disk
2358 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid, undef, 'images');
2359 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2360 my ($volid, $sid, $volname, $d) = @_;
2361 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2366 eval { delete_ifaces_ipams_ips
($conf, $vmid)};
2369 if (defined $replacement_conf) {
2370 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2372 PVE
::QemuConfig-
>destroy_config($vmid);
2376 sub parse_vm_config
{
2377 my ($filename, $raw, $strict) = @_;
2379 return if !defined($raw);
2382 digest
=> Digest
::SHA
::sha1_hex
($raw),
2388 my $handle_error = sub {
2398 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2399 || die "got strange filename '$filename'";
2405 my $finish_description = sub {
2406 if (defined($descr)) {
2408 $conf->{description
} = $descr;
2414 my @lines = split(/\n/, $raw);
2415 foreach my $line (@lines) {
2416 next if $line =~ m/^\s*$/;
2418 if ($line =~ m/^\[PENDING\]\s*$/i) {
2419 $section = 'pending';
2420 $finish_description->();
2421 $conf = $res->{$section} = {};
2423 } elsif ($line =~ m/^\[special:cloudinit\]\s*$/i) {
2424 $section = 'cloudinit';
2425 $finish_description->();
2426 $conf = $res->{$section} = {};
2429 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2431 $finish_description->();
2432 $conf = $res->{snapshots
}->{$section} = {};
2436 if ($line =~ m/^\#(.*)$/) {
2437 $descr = '' if !defined($descr);
2438 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2442 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2443 $descr = '' if !defined($descr);
2444 $descr .= PVE
::Tools
::decode_text
($2);
2445 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2446 $conf->{snapstate
} = $1;
2447 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2450 $conf->{$key} = $value;
2451 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2453 if ($section eq 'pending') {
2454 $conf->{delete} = $value; # we parse this later
2456 $handle_error->("vm $vmid - property 'delete' is only allowed in [PENDING]\n");
2458 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2461 if ($section eq 'cloudinit') {
2462 # ignore validation only used for informative purpose
2463 $conf->{$key} = $value;
2466 eval { $value = check_type
($key, $value); };
2468 $handle_error->("vm $vmid - unable to parse value of '$key' - $@");
2470 $key = 'ide2' if $key eq 'cdrom';
2471 my $fmt = $confdesc->{$key}->{format
};
2472 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2473 my $v = parse_drive
($key, $value);
2474 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2475 $v->{file
} = $volid;
2476 $value = print_drive
($v);
2478 $handle_error->("vm $vmid - unable to parse value of '$key'\n");
2483 $conf->{$key} = $value;
2486 $handle_error->("vm $vmid - unable to parse config: $line\n");
2490 $finish_description->();
2491 delete $res->{snapstate
}; # just to be sure
2496 sub write_vm_config
{
2497 my ($filename, $conf) = @_;
2499 delete $conf->{snapstate
}; # just to be sure
2501 if ($conf->{cdrom
}) {
2502 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2503 $conf->{ide2
} = $conf->{cdrom
};
2504 delete $conf->{cdrom
};
2507 # we do not use 'smp' any longer
2508 if ($conf->{sockets
}) {
2509 delete $conf->{smp
};
2510 } elsif ($conf->{smp
}) {
2511 $conf->{sockets
} = $conf->{smp
};
2512 delete $conf->{cores
};
2513 delete $conf->{smp
};
2516 my $used_volids = {};
2518 my $cleanup_config = sub {
2519 my ($cref, $pending, $snapname) = @_;
2521 foreach my $key (keys %$cref) {
2522 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2523 $key eq 'snapstate' || $key eq 'pending' || $key eq 'cloudinit';
2524 my $value = $cref->{$key};
2525 if ($key eq 'delete') {
2526 die "propertry 'delete' is only allowed in [PENDING]\n"
2528 # fixme: check syntax?
2531 eval { $value = check_type
($key, $value); };
2532 die "unable to parse value of '$key' - $@" if $@;
2534 $cref->{$key} = $value;
2536 if (!$snapname && is_valid_drivename
($key)) {
2537 my $drive = parse_drive
($key, $value);
2538 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2543 &$cleanup_config($conf);
2545 &$cleanup_config($conf->{pending
}, 1);
2547 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2548 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2549 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2552 # remove 'unusedX' settings if we re-add a volume
2553 foreach my $key (keys %$conf) {
2554 my $value = $conf->{$key};
2555 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2556 delete $conf->{$key};
2560 my $generate_raw_config = sub {
2561 my ($conf, $pending) = @_;
2565 # add description as comment to top of file
2566 if (defined(my $descr = $conf->{description
})) {
2568 foreach my $cl (split(/\n/, $descr)) {
2569 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2572 $raw .= "#\n" if $pending;
2576 foreach my $key (sort keys %$conf) {
2577 next if $key =~ /^(digest|description|pending|cloudinit|snapshots)$/;
2578 $raw .= "$key: $conf->{$key}\n";
2583 my $raw = &$generate_raw_config($conf);
2585 if (scalar(keys %{$conf->{pending
}})){
2586 $raw .= "\n[PENDING]\n";
2587 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2590 if (scalar(keys %{$conf->{cloudinit
}}) && PVE
::QemuConfig-
>has_cloudinit($conf)){
2591 $raw .= "\n[special:cloudinit]\n";
2592 $raw .= &$generate_raw_config($conf->{cloudinit
});
2595 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2596 $raw .= "\n[$snapname]\n";
2597 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2607 # we use static defaults from our JSON schema configuration
2608 foreach my $key (keys %$confdesc) {
2609 if (defined(my $default = $confdesc->{$key}->{default})) {
2610 $res->{$key} = $default;
2618 my $vmlist = PVE
::Cluster
::get_vmlist
();
2620 return $res if !$vmlist || !$vmlist->{ids
};
2621 my $ids = $vmlist->{ids
};
2622 my $nodename = nodename
();
2624 foreach my $vmid (keys %$ids) {
2625 my $d = $ids->{$vmid};
2626 next if !$d->{node
} || $d->{node
} ne $nodename;
2627 next if !$d->{type
} || $d->{type
} ne 'qemu';
2628 $res->{$vmid}->{exists} = 1;
2633 # test if VM uses local resources (to prevent migration)
2634 sub check_local_resources
{
2635 my ($conf, $noerr) = @_;
2638 my $mapped_res = [];
2640 my $nodelist = PVE
::Cluster
::get_nodelist
();
2641 my $pci_map = PVE
::Mapping
::PCI
::config
();
2642 my $usb_map = PVE
::Mapping
::USB
::config
();
2644 my $missing_mappings_by_node = { map { $_ => [] } @$nodelist };
2646 my $add_missing_mapping = sub {
2647 my ($type, $key, $id) = @_;
2648 for my $node (@$nodelist) {
2650 if ($type eq 'pci') {
2651 $entry = PVE
::Mapping
::PCI
::get_node_mapping
($pci_map, $id, $node);
2652 } elsif ($type eq 'usb') {
2653 $entry = PVE
::Mapping
::USB
::get_node_mapping
($usb_map, $id, $node);
2655 if (!scalar($entry->@*)) {
2656 push @{$missing_mappings_by_node->{$node}}, $key;
2661 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2662 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2664 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2666 foreach my $k (keys %$conf) {
2667 if ($k =~ m/^usb/) {
2668 my $entry = parse_property_string
('pve-qm-usb', $conf->{$k});
2669 next if $entry->{host
} =~ m/^spice$/i;
2670 if ($entry->{mapping
}) {
2671 $add_missing_mapping->('usb', $k, $entry->{mapping
});
2672 push @$mapped_res, $k;
2675 if ($k =~ m/^hostpci/) {
2676 my $entry = parse_property_string
('pve-qm-hostpci', $conf->{$k});
2677 if ($entry->{mapping
}) {
2678 $add_missing_mapping->('pci', $k, $entry->{mapping
});
2679 push @$mapped_res, $k;
2682 # sockets are safe: they will recreated be on the target side post-migrate
2683 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2684 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2687 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2689 return wantarray ?
(\
@loc_res, $mapped_res, $missing_mappings_by_node) : \
@loc_res;
2692 # check if used storages are available on all nodes (use by migrate)
2693 sub check_storage_availability
{
2694 my ($storecfg, $conf, $node) = @_;
2696 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2697 my ($ds, $drive) = @_;
2699 my $volid = $drive->{file
};
2702 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2705 # check if storage is available on both nodes
2706 my $scfg = PVE
::Storage
::storage_check_enabled
($storecfg, $sid);
2707 PVE
::Storage
::storage_check_enabled
($storecfg, $sid, $node);
2709 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $volid);
2711 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2712 if !$scfg->{content
}->{$vtype};
2716 # list nodes where all VM images are available (used by has_feature API)
2718 my ($conf, $storecfg) = @_;
2720 my $nodelist = PVE
::Cluster
::get_nodelist
();
2721 my $nodehash = { map { $_ => 1 } @$nodelist };
2722 my $nodename = nodename
();
2724 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2725 my ($ds, $drive) = @_;
2727 my $volid = $drive->{file
};
2730 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2732 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2733 if ($scfg->{disable
}) {
2735 } elsif (my $avail = $scfg->{nodes
}) {
2736 foreach my $node (keys %$nodehash) {
2737 delete $nodehash->{$node} if !$avail->{$node};
2739 } elsif (!$scfg->{shared
}) {
2740 foreach my $node (keys %$nodehash) {
2741 delete $nodehash->{$node} if $node ne $nodename
2750 sub check_local_storage_availability
{
2751 my ($conf, $storecfg) = @_;
2753 my $nodelist = PVE
::Cluster
::get_nodelist
();
2754 my $nodehash = { map { $_ => {} } @$nodelist };
2756 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2757 my ($ds, $drive) = @_;
2759 my $volid = $drive->{file
};
2762 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2764 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2766 if ($scfg->{disable
}) {
2767 foreach my $node (keys %$nodehash) {
2768 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2770 } elsif (my $avail = $scfg->{nodes
}) {
2771 foreach my $node (keys %$nodehash) {
2772 if (!$avail->{$node}) {
2773 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2780 foreach my $node (values %$nodehash) {
2781 if (my $unavail = $node->{unavailable_storages
}) {
2782 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2789 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2791 my ($vmid, $nocheck, $node) = @_;
2793 # $nocheck is set when called during a migration, in which case the config
2794 # file might still or already reside on the *other* node
2795 # - because rename has already happened, and current node is source
2796 # - because rename hasn't happened yet, and current node is target
2797 # - because rename has happened, current node is target, but hasn't yet
2799 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2800 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2805 my $vzlist = config_list
();
2807 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2809 while (defined(my $de = $fd->read)) {
2810 next if $de !~ m/^(\d+)\.pid$/;
2812 next if !defined($vzlist->{$vmid});
2813 if (my $pid = check_running
($vmid)) {
2814 $vzlist->{$vmid}->{pid
} = $pid;
2821 our $vmstatus_return_properties = {
2822 vmid
=> get_standard_option
('pve-vmid'),
2824 description
=> "QEMU process status.",
2826 enum
=> ['stopped', 'running'],
2829 description
=> "Maximum memory in bytes.",
2832 renderer
=> 'bytes',
2835 description
=> "Root disk size in bytes.",
2838 renderer
=> 'bytes',
2841 description
=> "VM name.",
2846 description
=> "VM run state from the 'query-status' QMP monitor command.",
2851 description
=> "PID of running qemu process.",
2856 description
=> "Uptime.",
2859 renderer
=> 'duration',
2862 description
=> "Maximum usable CPUs.",
2867 description
=> "The current config lock, if any.",
2872 description
=> "The current configured tags, if any",
2876 'running-machine' => {
2877 description
=> "The currently running machine type (if running).",
2882 description
=> "The currently running QEMU version (if running).",
2888 my $last_proc_pid_stat;
2890 # get VM status information
2891 # This must be fast and should not block ($full == false)
2892 # We only query KVM using QMP if $full == true (this can be slow)
2894 my ($opt_vmid, $full) = @_;
2898 my $storecfg = PVE
::Storage
::config
();
2900 my $list = vzlist
();
2901 my $defaults = load_defaults
();
2903 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
(1);
2905 my $cpucount = $cpuinfo->{cpus
} || 1;
2907 foreach my $vmid (keys %$list) {
2908 next if $opt_vmid && ($vmid ne $opt_vmid);
2910 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2912 my $d = { vmid
=> int($vmid) };
2913 $d->{pid
} = int($list->{$vmid}->{pid
}) if $list->{$vmid}->{pid
};
2915 # fixme: better status?
2916 $d->{status
} = $list->{$vmid}->{pid
} ?
'running' : 'stopped';
2918 my $size = PVE
::QemuServer
::Drive
::bootdisk_size
($storecfg, $conf);
2919 if (defined($size)) {
2920 $d->{disk
} = 0; # no info available
2921 $d->{maxdisk
} = $size;
2927 $d->{cpus
} = ($conf->{sockets
} || $defaults->{sockets
})
2928 * ($conf->{cores
} || $defaults->{cores
});
2929 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
2930 $d->{cpus
} = $conf->{vcpus
} if $conf->{vcpus
};
2932 $d->{name
} = $conf->{name
} || "VM $vmid";
2933 $d->{maxmem
} = get_current_memory
($conf->{memory
})*(1024*1024);
2935 if ($conf->{balloon
}) {
2936 $d->{balloon_min
} = $conf->{balloon
}*(1024*1024);
2937 $d->{shares
} = defined($conf->{shares
}) ?
$conf->{shares
}
2938 : $defaults->{shares
};
2949 $d->{diskwrite
} = 0;
2951 $d->{template
} = 1 if PVE
::QemuConfig-
>is_template($conf);
2953 $d->{serial
} = 1 if conf_has_serial
($conf);
2954 $d->{lock} = $conf->{lock} if $conf->{lock};
2955 $d->{tags
} = $conf->{tags
} if defined($conf->{tags
});
2960 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
2961 foreach my $dev (keys %$netdev) {
2962 next if $dev !~ m/^tap([1-9]\d*)i/;
2964 my $d = $res->{$vmid};
2967 $d->{netout
} += $netdev->{$dev}->{receive
};
2968 $d->{netin
} += $netdev->{$dev}->{transmit
};
2971 $d->{nics
}->{$dev}->{netout
} = int($netdev->{$dev}->{receive
});
2972 $d->{nics
}->{$dev}->{netin
} = int($netdev->{$dev}->{transmit
});
2977 my $ctime = gettimeofday
;
2979 foreach my $vmid (keys %$list) {
2981 my $d = $res->{$vmid};
2982 my $pid = $d->{pid
};
2985 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
2986 next if !$pstat; # not running
2988 my $used = $pstat->{utime} + $pstat->{stime
};
2990 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
2992 if ($pstat->{vsize
}) {
2993 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
2996 my $old = $last_proc_pid_stat->{$pid};
2998 $last_proc_pid_stat->{$pid} = {
3006 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
3008 if ($dtime > 1000) {
3009 my $dutime = $used - $old->{used
};
3011 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
3012 $last_proc_pid_stat->{$pid} = {
3018 $d->{cpu
} = $old->{cpu
};
3022 return $res if !$full;
3024 my $qmpclient = PVE
::QMPClient-
>new();
3026 my $ballooncb = sub {
3027 my ($vmid, $resp) = @_;
3029 my $info = $resp->{'return'};
3030 return if !$info->{max_mem
};
3032 my $d = $res->{$vmid};
3034 # use memory assigned to VM
3035 $d->{maxmem
} = $info->{max_mem
};
3036 $d->{balloon
} = $info->{actual
};
3038 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
3039 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
3040 $d->{freemem
} = $info->{free_mem
};
3043 $d->{ballooninfo
} = $info;
3046 my $blockstatscb = sub {
3047 my ($vmid, $resp) = @_;
3048 my $data = $resp->{'return'} || [];
3049 my $totalrdbytes = 0;
3050 my $totalwrbytes = 0;
3052 for my $blockstat (@$data) {
3053 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
3054 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
3056 $blockstat->{device
} =~ s/drive-//;
3057 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
3059 $res->{$vmid}->{diskread
} = $totalrdbytes;
3060 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
3063 my $machinecb = sub {
3064 my ($vmid, $resp) = @_;
3065 my $data = $resp->{'return'} || [];
3067 $res->{$vmid}->{'running-machine'} =
3068 PVE
::QemuServer
::Machine
::current_from_query_machines
($data);
3071 my $versioncb = sub {
3072 my ($vmid, $resp) = @_;
3073 my $data = $resp->{'return'} // {};
3074 my $version = 'unknown';
3076 if (my $v = $data->{qemu
}) {
3077 $version = $v->{major
} . "." . $v->{minor
} . "." . $v->{micro
};
3080 $res->{$vmid}->{'running-qemu'} = $version;
3083 my $statuscb = sub {
3084 my ($vmid, $resp) = @_;
3086 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
3087 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
3088 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
3089 # this fails if ballon driver is not loaded, so this must be
3090 # the last commnand (following command are aborted if this fails).
3091 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');