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=256";
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 {