1 package PVE
::QemuServer
;
11 use File
::Copy
qw(copy);
24 use Storable
qw(dclone);
25 use Time
::HiRes
qw(gettimeofday usleep);
29 use PVE
::Cluster
qw(cfs_register_file cfs_read_file cfs_write_file);
32 use PVE
::DataCenterConfig
;
33 use PVE
::Exception
qw(raise raise_param_exc);
34 use PVE
::Format
qw(render_duration render_bytes);
35 use PVE
::GuestHelpers
qw(safe_string_ne safe_num_ne safe_boolean_ne);
37 use PVE
::JSONSchema
qw(get_standard_option parse_property_string);
40 use PVE
::RESTEnvironment
qw(log_warn);
41 use PVE
::RPCEnvironment
;
45 use PVE
::Tools
qw(run_command file_read_firstline file_get_contents dir_glob_foreach get_host_arch $IPV6RE);
49 use PVE
::QemuServer
::Helpers
qw(min_version config_aware_timeout windows_version);
50 use PVE
::QemuServer
::Cloudinit
;
51 use PVE
::QemuServer
::CGroup
;
52 use PVE
::QemuServer
::CPUConfig
qw(print_cpu_device get_cpu_options);
53 use PVE
::QemuServer
::Drive
qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom drive_is_read_only parse_drive print_drive);
54 use PVE
::QemuServer
::Machine
;
55 use PVE
::QemuServer
::Memory
;
56 use PVE
::QemuServer
::Monitor
qw(mon_cmd);
57 use PVE
::QemuServer
::PCI
qw(print_pci_addr print_pcie_addr print_pcie_root_port parse_hostpci);
58 use PVE
::QemuServer
::USB
qw(parse_usb_device);
62 require PVE
::Network
::SDN
::Zones
;
66 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
70 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
71 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
74 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
75 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
78 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
79 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
82 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
83 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
86 "$EDK2_FW_BASE/OVMF_CODE.fd",
87 "$EDK2_FW_BASE/OVMF_VARS.fd",
92 "$EDK2_FW_BASE/AAVMF_CODE.fd",
93 "$EDK2_FW_BASE/AAVMF_VARS.fd",
98 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
100 # Note about locking: we use flock on the config file protect against concurent actions.
101 # Aditionaly, we have a 'lock' setting in the config file. This can be set to 'migrate',
102 # 'backup', 'snapshot' or 'rollback'. Most actions are not allowed when such lock is set.
103 # But you can ignore this kind of lock with the --skiplock flag.
105 cfs_register_file
('/qemu-server/',
109 PVE
::JSONSchema
::register_standard_option
('pve-qm-stateuri', {
110 description
=> "Some command save/restore state from this location.",
116 PVE
::JSONSchema
::register_standard_option
('pve-qemu-machine', {
117 description
=> "Specifies the Qemu machine type.",
119 pattern
=> '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
124 #no warnings 'redefine';
128 $nodename_cache //= PVE
::INotify
::nodename
();
129 return $nodename_cache;
136 enum
=> [qw(i6300esb ib700)],
137 description
=> "Watchdog type to emulate.",
138 default => 'i6300esb',
143 enum
=> [qw(reset shutdown poweroff pause debug none)],
144 description
=> "The action to perform if after activation the guest fails to poll the watchdog in time.",
148 PVE
::JSONSchema
::register_format
('pve-qm-watchdog', $watchdog_fmt);
152 description
=> "Enable/disable communication with a Qemu Guest Agent (QGA) running in the VM.",
157 fstrim_cloned_disks
=> {
158 description
=> "Run fstrim after moving a disk or migrating the VM.",
164 description
=> "Select the agent type",
168 enum
=> [qw(virtio isa)],
174 description
=> "Select the VGA type.",
179 enum
=> [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio virtio-gl vmware)],
182 description
=> "Sets the VGA memory (in MiB). Has no effect with serial display.",
194 description
=> "The size of the file in MB.",
198 pattern
=> '[a-zA-Z0-9\-]+',
200 format_description
=> 'string',
201 description
=> "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
208 enum
=> [qw(ich9-intel-hda intel-hda AC97)],
209 description
=> "Configure an audio device."
213 enum
=> ['spice', 'none'],
216 description
=> "Driver backend for the audio device."
220 my $spice_enhancements_fmt = {
225 description
=> "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
229 enum
=> ['off', 'all', 'filter'],
232 description
=> "Enable video streaming. Uses compression for detected video streams."
239 enum
=> ['/dev/urandom', '/dev/random', '/dev/hwrng'],
241 description
=> "The file on the host to gather entropy from. In most cases '/dev/urandom'"
242 ." should be preferred over '/dev/random' to avoid entropy-starvation issues on the"
243 ." host. Using urandom does *not* decrease security in any meaningful way, as it's"
244 ." still seeded from real entropy, and the bytes provided will most likely be mixed"
245 ." with real entropy on the guest as well. '/dev/hwrng' can be used to pass through"
246 ." a hardware RNG from the host.",
250 description
=> "Maximum bytes of entropy allowed to get injected into the guest every"
251 ." 'period' milliseconds. Prefer a lower value when using '/dev/random' as source. Use"
252 ." `0` to disable limiting (potentially dangerous!).",
255 # default is 1 KiB/s, provides enough entropy to the guest to avoid boot-starvation issues
256 # (e.g. systemd etc...) while allowing no chance of overwhelming the host, provided we're
257 # reading from /dev/urandom
262 description
=> "Every 'period' milliseconds the entropy-injection quota is reset, allowing"
263 ." the guest to retrieve another 'max_bytes' of entropy.",
269 my $meta_info_fmt = {
272 description
=> "The guest creation timestamp as UNIX epoch time",
278 description
=> "The QEMU (machine) version from the time this VM was created.",
279 pattern
=> '\d+(\.\d+)+',
288 description
=> "Specifies whether a VM will be started during system bootup.",
294 description
=> "Automatic restart after crash (currently ignored).",
299 type
=> 'string', format
=> 'pve-hotplug-features',
300 description
=> "Selectively enable hotplug features. This is a comma separated list of"
301 ." hotplug features: 'network', 'disk', 'cpu', 'memory', 'usb' and 'cloudinit'. Use '0' to disable"
302 ." hotplug completely. Using '1' as value is an alias for the default `network,disk,usb`.",
303 default => 'network,disk,usb',
308 description
=> "Allow reboot. If set to '0' the VM exit on reboot.",
314 description
=> "Lock/unlock the VM.",
315 enum
=> [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
320 description
=> "Limit of CPU usage.",
321 verbose_description
=> "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has"
322 ." total of '2' CPU time. Value '0' indicates no CPU limit.",
330 description
=> "CPU weight for a VM, will be clamped to [1, 10000] in cgroup v2.",
331 verbose_description
=> "CPU weight for a VM. Argument is used in the kernel fair scheduler."
332 ." The larger the number is, the more CPU time this VM gets. Number is relative to"
333 ." weights of all the other running VMs.",
336 default => 'cgroup v1: 1024, cgroup v2: 100',
341 description
=> "Amount of RAM for the VM in MB. This is the maximum available memory when"
342 ." you use the balloon device.",
349 description
=> "Amount of target RAM for the VM in MB. Using zero disables the ballon driver.",
355 description
=> "Amount of memory shares for auto-ballooning. The larger the number is, the"
356 ." more memory this VM gets. Number is relative to weights of all other running VMs."
357 ." Using zero disables auto-ballooning. Auto-ballooning is done by pvestatd.",
365 description
=> "Keyboard layout for VNC server. This option is generally not required and"
366 ." is often better handled from within the guest OS.",
367 enum
=> PVE
::Tools
::kvmkeymaplist
(),
372 type
=> 'string', format
=> 'dns-name',
373 description
=> "Set a name for the VM. Only used on the configuration web interface.",
378 description
=> "SCSI controller model",
379 enum
=> [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
385 description
=> "Description for the VM. Shown in the web-interface VM's summary."
386 ." This is saved as comment inside the configuration file.",
387 maxLength
=> 1024 * 8,
392 enum
=> [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 win11 l24 l26 solaris)],
393 description
=> "Specify guest operating system.",
394 verbose_description
=> <<EODESC,
395 Specify guest operating system. This is used to enable special
396 optimization/features for specific operating systems:
399 other;; unspecified OS
400 wxp;; Microsoft Windows XP
401 w2k;; Microsoft Windows 2000
402 w2k3;; Microsoft Windows 2003
403 w2k8;; Microsoft Windows 2008
404 wvista;; Microsoft Windows Vista
405 win7;; Microsoft Windows 7
406 win8;; Microsoft Windows 8/2012/2012r2
407 win10;; Microsoft Windows 10/2016/2019
408 win11;; Microsoft Windows 11/2022
409 l24;; Linux 2.4 Kernel
410 l26;; Linux 2.6 - 5.X Kernel
411 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
416 type
=> 'string', format
=> 'pve-qm-boot',
417 description
=> "Specify guest boot order. Use the 'order=' sub-property as usage with no"
418 ." key or 'legacy=' is deprecated.",
422 type
=> 'string', format
=> 'pve-qm-bootdisk',
423 description
=> "Enable booting from specified disk. Deprecated: Use 'boot: order=foo;bar' instead.",
424 pattern
=> '(ide|sata|scsi|virtio)\d+',
429 description
=> "The number of CPUs. Please use option -sockets instead.",
436 description
=> "The number of CPU sockets.",
443 description
=> "The number of cores per socket.",
450 description
=> "Enable/disable NUMA.",
456 description
=> "Enable/disable hugepages memory.",
457 enum
=> [qw(any 2 1024)],
463 description
=> "Use together with hugepages. If enabled, hugepages will not not be deleted"
464 ." after VM shutdown and can be used for subsequent starts.",
469 description
=> "Number of hotplugged vcpus.",
476 description
=> "Enable/disable ACPI.",
481 description
=> "Enable/disable communication with the Qemu Guest Agent and its properties.",
483 format
=> $agent_fmt,
488 description
=> "Enable/disable KVM hardware virtualization.",
494 description
=> "Enable/disable time drift fix.",
500 description
=> "Set the real time clock (RTC) to local time. This is enabled by default if"
501 ." the `ostype` indicates a Microsoft Windows OS.",
506 description
=> "Freeze CPU at startup (use 'c' monitor command to start execution).",
510 type
=> 'string', format
=> $vga_fmt,
511 description
=> "Configure the VGA hardware.",
512 verbose_description
=> "Configure the VGA Hardware. If you want to use high resolution"
513 ." modes (>= 1280x1024x16) you may need to increase the vga memory option. Since QEMU"
514 ." 2.9 the default VGA display type is 'std' for all OS types besides some Windows"
515 ." versions (XP and older) which use 'cirrus'. The 'qxl' option enables the SPICE"
516 ." display server. For win* OS you can select how many independent displays you want,"
517 ." Linux guests can add displays them self.\nYou can also run without any graphic card,"
518 ." using a serial device as terminal.",
522 type
=> 'string', format
=> 'pve-qm-watchdog',
523 description
=> "Create a virtual hardware watchdog device.",
524 verbose_description
=> "Create a virtual hardware watchdog device. Once enabled (by a guest"
525 ." action), the watchdog must be periodically polled by an agent inside the guest or"
526 ." else the watchdog will reset the guest (or execute the respective action specified)",
531 typetext
=> "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
532 description
=> "Set the initial date of the real time clock. Valid format for date are:"
533 ."'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
534 pattern
=> '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
537 startup
=> get_standard_option
('pve-startup-order'),
541 description
=> "Enable/disable Template.",
547 description
=> "Arbitrary arguments passed to kvm.",
548 verbose_description
=> <<EODESCR,
549 Arbitrary arguments passed to kvm, for example:
551 args: -no-reboot -no-hpet
553 NOTE: this option is for experts only.
560 description
=> "Enable/disable the USB tablet device.",
561 verbose_description
=> "Enable/disable the USB tablet device. This device is usually needed"
562 ." to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with"
563 ." normal VNC clients. If you're running lots of console-only guests on one host, you"
564 ." may consider disabling this to save some context switches. This is turned off by"
565 ." default if you use spice (`qm set <vmid> --vga qxl`).",
570 description
=> "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
574 migrate_downtime
=> {
577 description
=> "Set maximum tolerated downtime (in seconds) for migrations.",
583 type
=> 'string', format
=> 'pve-qm-ide',
584 typetext
=> '<volume>',
585 description
=> "This is an alias for option -ide2",
589 description
=> "Emulated CPU type.",
591 format
=> 'pve-vm-cpu-conf',
593 parent
=> get_standard_option
('pve-snapshot-name', {
595 description
=> "Parent snapshot name. This is used internally, and should not be modified.",
599 description
=> "Timestamp for snapshots.",
605 type
=> 'string', format
=> 'pve-volume-id',
606 description
=> "Reference to a volume which stores the VM state. This is used internally"
609 vmstatestorage
=> get_standard_option
('pve-storage-id', {
610 description
=> "Default storage for VM state volumes/files.",
613 runningmachine
=> get_standard_option
('pve-qemu-machine', {
614 description
=> "Specifies the QEMU machine type of the running vm. This is used internally"
618 description
=> "Specifies the QEMU '-cpu' parameter of the running vm. This is used"
619 ." internally for snapshots.",
622 pattern
=> $PVE::QemuServer
::CPUConfig
::qemu_cmdline_cpu_re
,
623 format_description
=> 'QEMU -cpu parameter'
625 machine
=> get_standard_option
('pve-qemu-machine'),
627 description
=> "Virtual processor architecture. Defaults to the host.",
630 enum
=> [qw(x86_64 aarch64)],
633 description
=> "Specify SMBIOS type 1 fields.",
634 type
=> 'string', format
=> 'pve-qm-smbios1',
641 description
=> "Sets the protection flag of the VM. This will disable the remove VM and"
642 ." remove disk operations.",
648 enum
=> [ qw(seabios ovmf) ],
649 description
=> "Select BIOS implementation.",
650 default => 'seabios',
654 pattern
=> '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
655 format_description
=> 'UUID',
656 description
=> "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0'"
657 ." to disable explicitly.",
658 verbose_description
=> "The VM generation ID (vmgenid) device exposes a 128-bit integer"
659 ." value identifier to the guest OS. This allows to notify the guest operating system"
660 ." when the virtual machine is executed with a different configuration (e.g. snapshot"
661 ." execution or creation from a template). The guest operating system notices the"
662 ." change, and is then able to react as appropriate by marking its copies of"
663 ." distributed databases as dirty, re-initializing its random number generator, etc.\n"
664 ."Note that auto-creation only works when done through API/CLI create or update methods"
665 .", but not when manually editing the config file.",
666 default => "1 (autogenerated)",
671 format
=> 'pve-volume-id',
673 description
=> "Script that will be executed during various steps in the vms lifetime.",
677 format
=> $ivshmem_fmt,
678 description
=> "Inter-VM shared memory. Useful for direct communication between VMs, or to"
684 format
=> $audio_fmt,
685 description
=> "Configure a audio device, useful in combination with QXL/Spice.",
688 spice_enhancements
=> {
690 format
=> $spice_enhancements_fmt,
691 description
=> "Configure additional enhancements for SPICE.",
695 type
=> 'string', format
=> 'pve-tag-list',
696 description
=> 'Tags of the VM. This is only meta information.',
702 description
=> "Configure a VirtIO-based Random Number Generator.",
707 format
=> $meta_info_fmt,
708 description
=> "Some (read-only) meta-information about this guest.",
712 type
=> 'string', format
=> 'pve-cpuset',
713 description
=> "List of host cores used to execute guest processes.",
722 description
=> 'Specify a custom file containing all meta data passed to the VM via"
723 ." cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
724 format
=> 'pve-volume-id',
725 format_description
=> 'volume',
730 description
=> 'Specify a custom file containing all network data passed to the VM via'
732 format
=> 'pve-volume-id',
733 format_description
=> 'volume',
738 description
=> 'Specify a custom file containing all user data passed to the VM via'
740 format
=> 'pve-volume-id',
741 format_description
=> 'volume',
746 description
=> 'Specify a custom file containing all vendor data passed to the VM via'
748 format
=> 'pve-volume-id',
749 format_description
=> 'volume',
752 PVE
::JSONSchema
::register_format
('pve-qm-cicustom', $cicustom_fmt);
754 my $confdesc_cloudinit = {
758 description
=> 'Specifies the cloud-init configuration format. The default depends on the'
759 .' configured operating system type (`ostype`. We use the `nocloud` format for Linux,'
760 .' and `configdrive2` for windows.',
761 enum
=> ['configdrive2', 'nocloud', 'opennebula'],
766 description
=> "cloud-init: User name to change ssh keys and password for instead of the"
767 ." image's configured default user.",
772 description
=> 'cloud-init: Password to assign the user. Using this is generally not'
773 .' recommended. Use ssh keys instead. Also note that older cloud-init versions do not'
774 .' support hashed passwords.',
779 description
=> 'cloud-init: Specify custom files to replace the automatically generated'
781 format
=> 'pve-qm-cicustom',
786 description
=> 'cloud-init: Sets DNS search domains for a container. Create will'
787 .' automatically use the setting from the host if neither searchdomain nor nameserver'
792 type
=> 'string', format
=> 'address-list',
793 description
=> 'cloud-init: Sets DNS server IP address for a container. Create will'
794 .' automatically use the setting from the host if neither searchdomain nor nameserver'
800 format
=> 'urlencoded',
801 description
=> "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
805 # what about other qemu settings ?
807 #machine => 'string',
820 ##soundhw => 'string',
822 while (my ($k, $v) = each %$confdesc) {
823 PVE
::JSONSchema
::register_standard_option
("pve-qm-$k", $v);
826 my $MAX_USB_DEVICES = 14;
828 my $MAX_SERIAL_PORTS = 4;
829 my $MAX_PARALLEL_PORTS = 3;
835 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
836 description
=> "CPUs accessing this NUMA node.",
837 format_description
=> "id[-id];...",
841 description
=> "Amount of memory this NUMA node provides.",
846 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
847 description
=> "Host NUMA nodes to use.",
848 format_description
=> "id[-id];...",
853 enum
=> [qw(preferred bind interleave)],
854 description
=> "NUMA allocation policy.",
858 PVE
::JSONSchema
::register_format
('pve-qm-numanode', $numa_fmt);
861 type
=> 'string', format
=> $numa_fmt,
862 description
=> "NUMA topology.",
864 PVE
::JSONSchema
::register_standard_option
("pve-qm-numanode", $numadesc);
866 for (my $i = 0; $i < $MAX_NUMA; $i++) {
867 $confdesc->{"numa$i"} = $numadesc;
870 my $nic_model_list = [
886 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
888 my $net_fmt_bridge_descr = <<__EOD__;
889 Bridge to attach the network device to. The Proxmox VE standard bridge
892 If you do not specify a bridge, we create a kvm user (NATed) network
893 device, which provides DHCP and DNS services. The following addresses
900 The DHCP server assign addresses to the guest starting from 10.0.2.15.
904 macaddr
=> get_standard_option
('mac-addr', {
905 description
=> "MAC address. That address must be unique withing your network. This is"
906 ." automatically generated if not specified.",
910 description
=> "Network Card Model. The 'virtio' model provides the best performance with"
911 ." very low CPU overhead. If your guest does not support this driver, it is usually"
912 ." best to use 'e1000'.",
913 enum
=> $nic_model_list,
916 (map { $_ => { keyAlias
=> 'model', alias
=> 'macaddr' }} @$nic_model_list),
917 bridge
=> get_standard_option
('pve-bridge-id', {
918 description
=> $net_fmt_bridge_descr,
923 minimum
=> 0, maximum
=> 16,
924 description
=> 'Number of packet queues to be used on the device.',
930 description
=> "Rate limit in mbps (megabytes per second) as floating point number.",
935 minimum
=> 1, maximum
=> 4094,
936 description
=> 'VLAN tag to apply to packets on this interface.',
941 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
942 description
=> 'VLAN trunks to pass through this interface.',
943 format_description
=> 'vlanid[;vlanid...]',
948 description
=> 'Whether this interface should be protected by the firewall.',
953 description
=> 'Whether this interface should be disconnected (like pulling the plug).',
958 minimum
=> 1, maximum
=> 65520,
959 description
=> "Force MTU, for VirtIO only. Set to '1' to use the bridge MTU",
966 type
=> 'string', format
=> $net_fmt,
967 description
=> "Specify network devices.",
970 PVE
::JSONSchema
::register_standard_option
("pve-qm-net", $netdesc);
975 format
=> 'pve-ipv4-config',
976 format_description
=> 'IPv4Format/CIDR',
977 description
=> 'IPv4 address in CIDR format.',
984 format_description
=> 'GatewayIPv4',
985 description
=> 'Default gateway for IPv4 traffic.',
991 format
=> 'pve-ipv6-config',
992 format_description
=> 'IPv6Format/CIDR',
993 description
=> 'IPv6 address in CIDR format.',
1000 format_description
=> 'GatewayIPv6',
1001 description
=> 'Default gateway for IPv6 traffic.',
1006 PVE
::JSONSchema
::register_format
('pve-qm-ipconfig', $ipconfig_fmt);
1007 my $ipconfigdesc = {
1009 type
=> 'string', format
=> 'pve-qm-ipconfig',
1010 description
=> <<'EODESCR',
1011 cloud-init: Specify IP addresses and gateways for the corresponding interface.
1013 IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
1015 The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit
1016 gateway should be provided.
1017 For IPv6 the special string 'auto' can be used to use stateless autoconfiguration. This requires
1018 cloud-init 19.4 or newer.
1020 If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using
1024 PVE
::JSONSchema
::register_standard_option
("pve-qm-ipconfig", $netdesc);
1026 for (my $i = 0; $i < $MAX_NETS; $i++) {
1027 $confdesc->{"net$i"} = $netdesc;
1028 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
1031 foreach my $key (keys %$confdesc_cloudinit) {
1032 $confdesc->{$key} = $confdesc_cloudinit->{$key};
1035 PVE
::JSONSchema
::register_format
('pve-cpuset', \
&pve_verify_cpuset
);
1036 sub pve_verify_cpuset
{
1037 my ($set_text, $noerr) = @_;
1039 my ($count, $members) = eval { PVE
::CpuSet
::parse_cpuset
($set_text) };
1043 die "unable to parse cpuset option\n";
1046 return PVE
::CpuSet-
>new($members)->short_string();
1049 PVE
::JSONSchema
::register_format
('pve-volume-id-or-qm-path', \
&verify_volume_id_or_qm_path
);
1050 sub verify_volume_id_or_qm_path
{
1051 my ($volid, $noerr) = @_;
1053 return $volid if $volid eq 'none' || $volid eq 'cdrom';
1055 return verify_volume_id_or_absolute_path
($volid, $noerr);
1058 PVE
::JSONSchema
::register_format
('pve-volume-id-or-absolute-path', \
&verify_volume_id_or_absolute_path
);
1059 sub verify_volume_id_or_absolute_path
{
1060 my ($volid, $noerr) = @_;
1062 return $volid if $volid =~ m
|^/|;
1064 $volid = eval { PVE
::JSONSchema
::check_format
('pve-volume-id', $volid, '') };
1075 type
=> 'string', format
=> 'pve-qm-usb-device',
1076 format_description
=> 'HOSTUSBDEVICE|spice',
1077 description
=> <<EODESCR,
1078 The Host USB device or port or the value 'spice'. HOSTUSBDEVICE syntax is:
1080 'bus-port(.port)*' (decimal numbers) or
1081 'vendor_id:product_id' (hexadeciaml numbers) or
1084 You can use the 'lsusb -t' command to list existing usb devices.
1086 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1087 machines - use with special care.
1089 The value 'spice' can be used to add a usb redirection devices for spice.
1095 description
=> "Specifies whether if given host option is a USB3 device or port."
1096 ." For modern guests (machine version >= 7.1 and ostype l26 and windows > 7), this flag"
1097 ." is irrelevant (all devices are plugged into a xhci controller).",
1104 type
=> 'string', format
=> $usb_fmt,
1105 description
=> "Configure an USB device (n is 0 to 4, for machine version >= 7.1 and ostype"
1106 ." l26 or windows > 7, n can be up to 14).",
1108 PVE
::JSONSchema
::register_standard_option
("pve-qm-usb", $usbdesc);
1113 pattern
=> '(/dev/.+|socket)',
1114 description
=> "Create a serial device inside the VM (n is 0 to 3)",
1115 verbose_description
=> <<EODESCR,
1116 Create a serial device inside the VM (n is 0 to 3), and pass through a
1117 host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1118 host side (use 'qm terminal' to open a terminal connection).
1120 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines -
1121 use with special care.
1123 CAUTION: Experimental! User reported problems with this option.
1130 pattern
=> '/dev/parport\d+|/dev/usb/lp\d+',
1131 description
=> "Map host parallel devices (n is 0 to 2).",
1132 verbose_description
=> <<EODESCR,
1133 Map host parallel devices (n is 0 to 2).
1135 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1136 machines - use with special care.
1138 CAUTION: Experimental! User reported problems with this option.
1142 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1143 $confdesc->{"parallel$i"} = $paralleldesc;
1146 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1147 $confdesc->{"serial$i"} = $serialdesc;
1150 for (my $i = 0; $i < $PVE::QemuServer
::PCI
::MAX_HOSTPCI_DEVICES
; $i++) {
1151 $confdesc->{"hostpci$i"} = $PVE::QemuServer
::PCI
::hostpcidesc
;
1154 for my $key (keys %{$PVE::QemuServer
::Drive
::drivedesc_hash
}) {
1155 $confdesc->{$key} = $PVE::QemuServer
::Drive
::drivedesc_hash-
>{$key};
1158 for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
1159 $confdesc->{"usb$i"} = $usbdesc;
1167 description
=> "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)."
1168 . " Deprecated, use 'order=' instead.",
1169 pattern
=> '[acdn]{1,4}',
1170 format_description
=> "[acdn]{1,4}",
1172 # note: this is also the fallback if boot: is not given at all
1178 format
=> 'pve-qm-bootdev-list',
1179 format_description
=> "device[;device...]",
1180 description
=> <<EODESC,
1181 The guest will attempt to boot from devices in the order they appear here.
1183 Disks, optical drives and passed-through storage USB devices will be directly
1184 booted from, NICs will load PXE, and PCIe devices will either behave like disks
1185 (e.g. NVMe) or load an option ROM (e.g. RAID controller, hardware NIC).
1187 Note that only devices in this list will be marked as bootable and thus loaded
1188 by the guest firmware (BIOS/UEFI). If you require multiple disks for booting
1189 (e.g. software-raid), you need to specify all of them here.
1191 Overrides the deprecated 'legacy=[acdn]*' value when given.
1195 PVE
::JSONSchema
::register_format
('pve-qm-boot', $boot_fmt);
1197 PVE
::JSONSchema
::register_format
('pve-qm-bootdev', \
&verify_bootdev
);
1198 sub verify_bootdev
{
1199 my ($dev, $noerr) = @_;
1201 my $special = $dev =~ m/^efidisk/ || $dev =~ m/^tpmstate/;
1202 return $dev if PVE
::QemuServer
::Drive
::is_valid_drivename
($dev) && !$special;
1206 return 0 if $dev !~ m/^$base\d+$/;
1207 return 0 if !$confdesc->{$dev};
1211 return $dev if $check->("net");
1212 return $dev if $check->("usb");
1213 return $dev if $check->("hostpci");
1216 die "invalid boot device '$dev'\n";
1219 sub print_bootorder
{
1221 return "" if !@$devs;
1222 my $data = { order
=> join(';', @$devs) };
1223 return PVE
::JSONSchema
::print_property_string
($data, $boot_fmt);
1226 my $kvm_api_version = 0;
1229 return $kvm_api_version if $kvm_api_version;
1231 open my $fh, '<', '/dev/kvm' or return;
1233 # 0xae00 => KVM_GET_API_VERSION
1234 $kvm_api_version = ioctl($fh, 0xae00, 0);
1237 return $kvm_api_version;
1240 my $kvm_user_version = {};
1243 sub kvm_user_version
{
1246 $binary //= get_command_for_arch
(get_host_arch
()); # get the native arch by default
1247 my $st = stat($binary);
1249 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1250 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1251 $cachedmtime == $st->mtime;
1253 $kvm_user_version->{$binary} = 'unknown';
1254 $kvm_mtime->{$binary} = $st->mtime;
1258 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1259 $kvm_user_version->{$binary} = $2;
1263 eval { run_command
([$binary, '--version'], outfunc
=> $code); };
1266 return $kvm_user_version->{$binary};
1269 my sub extract_version
{
1270 my ($machine_type, $version) = @_;
1271 $version = kvm_user_version
() if !defined($version);
1272 return PVE
::QemuServer
::Machine
::extract_version
($machine_type, $version)
1275 sub kernel_has_vhost_net
{
1276 return -c
'/dev/vhost-net';
1281 return defined($confdesc->{$key});
1285 sub get_cdrom_path
{
1287 return $cdrom_path if $cdrom_path;
1289 return $cdrom_path = "/dev/cdrom" if -l
"/dev/cdrom";
1290 return $cdrom_path = "/dev/cdrom1" if -l
"/dev/cdrom1";
1291 return $cdrom_path = "/dev/cdrom2" if -l
"/dev/cdrom2";
1295 my ($storecfg, $vmid, $cdrom) = @_;
1297 if ($cdrom eq 'cdrom') {
1298 return get_cdrom_path
();
1299 } elsif ($cdrom eq 'none') {
1301 } elsif ($cdrom =~ m
|^/|) {
1304 return PVE
::Storage
::path
($storecfg, $cdrom);
1308 # try to convert old style file names to volume IDs
1309 sub filename_to_volume_id
{
1310 my ($vmid, $file, $media) = @_;
1312 if (!($file eq 'none' || $file eq 'cdrom' ||
1313 $file =~ m
|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1315 return if $file =~ m
|/|;
1317 if ($media && $media eq 'cdrom') {
1318 $file = "local:iso/$file";
1320 $file = "local:$vmid/$file";
1327 sub verify_media_type
{
1328 my ($opt, $vtype, $media) = @_;
1333 if ($media eq 'disk') {
1335 } elsif ($media eq 'cdrom') {
1338 die "internal error";
1341 return if ($vtype eq $etype);
1343 raise_param_exc
({ $opt => "unexpected media type ($vtype != $etype)" });
1346 sub cleanup_drive_path
{
1347 my ($opt, $storecfg, $drive) = @_;
1349 # try to convert filesystem paths to volume IDs
1351 if (($drive->{file
} !~ m/^(cdrom|none)$/) &&
1352 ($drive->{file
} !~ m
|^/dev/.+|) &&
1353 ($drive->{file
} !~ m/^([^:]+):(.+)$/) &&
1354 ($drive->{file
} !~ m/^\d+$/)) {
1355 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($storecfg, $drive->{file
});
1356 raise_param_exc
({ $opt => "unable to associate path '$drive->{file}' to any storage"})
1358 $drive->{media
} = 'cdrom' if !$drive->{media
} && $vtype eq 'iso';
1359 verify_media_type
($opt, $vtype, $drive->{media
});
1360 $drive->{file
} = $volid;
1363 $drive->{media
} = 'cdrom' if !$drive->{media
} && $drive->{file
} =~ m/^(cdrom|none)$/;
1366 sub parse_hotplug_features
{
1371 return $res if $data eq '0';
1373 $data = $confdesc->{hotplug
}->{default} if $data eq '1';
1375 foreach my $feature (PVE
::Tools
::split_list
($data)) {
1376 if ($feature =~ m/^(network|disk|cpu|memory|usb|cloudinit)$/) {
1379 die "invalid hotplug feature '$feature'\n";
1385 PVE
::JSONSchema
::register_format
('pve-hotplug-features', \
&pve_verify_hotplug_features
);
1386 sub pve_verify_hotplug_features
{
1387 my ($value, $noerr) = @_;
1389 return $value if parse_hotplug_features
($value);
1393 die "unable to parse hotplug option\n";
1397 my($fh, $noerr) = @_;
1400 my $SG_GET_VERSION_NUM = 0x2282;
1402 my $versionbuf = "\x00" x
8;
1403 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1405 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
1408 my $version = unpack("I", $versionbuf);
1409 if ($version < 30000) {
1410 die "scsi generic interface too old\n" if !$noerr;
1414 my $buf = "\x00" x
36;
1415 my $sensebuf = "\x00" x
8;
1416 my $cmd = pack("C x3 C x1", 0x12, 36);
1418 # see /usr/include/scsi/sg.h
1419 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";
1422 $sg_io_hdr_t, ord('S'), -3, length($cmd), length($sensebuf), 0, length($buf), $buf, $cmd, $sensebuf, 6000
1425 $ret = ioctl($fh, $SG_IO, $packet);
1427 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
1431 my @res = unpack($sg_io_hdr_t, $packet);
1432 if ($res[17] || $res[18]) {
1433 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
1438 $res->@{qw(type removable vendor product revision)} = unpack("C C x6 A8 A16 A4", $buf);
1440 $res->{removable
} = $res->{removable
} & 128 ?
1 : 0;
1441 $res->{type
} &= 0x1F;
1449 my $fh = IO
::File-
>new("+<$path") || return;
1450 my $res = scsi_inquiry
($fh, 1);
1456 sub print_tabletdevice_full
{
1457 my ($conf, $arch) = @_;
1459 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1461 # we use uhci for old VMs because tablet driver was buggy in older qemu
1463 if ($q35 || $arch eq 'aarch64') {
1469 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1472 sub print_keyboarddevice_full
{
1473 my ($conf, $arch) = @_;
1475 return if $arch ne 'aarch64';
1477 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1480 my sub get_drive_id
{
1482 return "$drive->{interface}$drive->{index}";
1485 sub print_drivedevice_full
{
1486 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1491 my $drive_id = get_drive_id
($drive);
1492 if ($drive->{interface
} eq 'virtio') {
1493 my $pciaddr = print_pci_addr
("$drive_id", $bridges, $arch, $machine_type);
1494 $device = "virtio-blk-pci,drive=drive-$drive_id,id=${drive_id}${pciaddr}";
1495 $device .= ",iothread=iothread-$drive_id" if $drive->{iothread
};
1496 } elsif ($drive->{interface
} eq 'scsi') {
1498 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
1499 my $unit = $drive->{index} % $maxdev;
1500 my $devicetype = 'hd';
1502 if (drive_is_cdrom
($drive)) {
1505 if ($drive->{file
} =~ m
|^/|) {
1506 $path = $drive->{file
};
1507 if (my $info = path_is_scsi
($path)) {
1508 if ($info->{type
} == 0 && $drive->{scsiblock
}) {
1509 $devicetype = 'block';
1510 } elsif ($info->{type
} == 1) { # tape
1511 $devicetype = 'generic';
1515 $path = PVE
::Storage
::path
($storecfg, $drive->{file
});
1518 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1519 my $version = extract_version
($machine_type, kvm_user_version
());
1520 if ($path =~ m/^iscsi\:\/\
// &&
1521 !min_version
($version, 4, 1)) {
1522 $devicetype = 'generic';
1526 if (!$conf->{scsihw
} || $conf->{scsihw
} =~ m/^lsi/ || $conf->{scsihw
} eq 'pvscsi') {
1527 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
1529 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
1530 .",lun=$drive->{index}";
1532 $device .= ",drive=drive-$drive_id,id=$drive_id";
1534 if ($drive->{ssd
} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1535 $device .= ",rotation_rate=1";
1537 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1539 } elsif ($drive->{interface
} eq 'ide' || $drive->{interface
} eq 'sata') {
1540 my $maxdev = ($drive->{interface
} eq 'sata') ?
$PVE::QemuServer
::Drive
::MAX_SATA_DISKS
: 2;
1541 my $controller = int($drive->{index} / $maxdev);
1542 my $unit = $drive->{index} % $maxdev;
1543 my $devicetype = ($drive->{media
} && $drive->{media
} eq 'cdrom') ?
"cd" : "hd";
1545 $device = "ide-$devicetype";
1546 if ($drive->{interface
} eq 'ide') {
1547 $device .= ",bus=ide.$controller,unit=$unit";
1549 $device .= ",bus=ahci$controller.$unit";
1551 $device .= ",drive=drive-$drive_id,id=$drive_id";
1553 if ($devicetype eq 'hd') {
1554 if (my $model = $drive->{model
}) {
1555 $model = URI
::Escape
::uri_unescape
($model);
1556 $device .= ",model=$model";
1558 if ($drive->{ssd
}) {
1559 $device .= ",rotation_rate=1";
1562 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1563 } elsif ($drive->{interface
} eq 'usb') {
1565 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1567 die "unsupported interface type";
1570 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex
};
1572 if (my $serial = $drive->{serial
}) {
1573 $serial = URI
::Escape
::uri_unescape
($serial);
1574 $device .= ",serial=$serial";
1581 sub get_initiator_name
{
1584 my $fh = IO
::File-
>new('/etc/iscsi/initiatorname.iscsi') || return;
1585 while (defined(my $line = <$fh>)) {
1586 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1595 sub print_drive_commandline_full
{
1596 my ($storecfg, $vmid, $drive, $pbs_name, $io_uring) = @_;
1599 my $volid = $drive->{file
};
1600 my $format = $drive->{format
};
1601 my $drive_id = get_drive_id
($drive);
1603 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1604 my $scfg = $storeid ? PVE
::Storage
::storage_config
($storecfg, $storeid) : undef;
1606 if (drive_is_cdrom
($drive)) {
1607 $path = get_iso_path
($storecfg, $vmid, $volid);
1608 die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
1611 $path = PVE
::Storage
::path
($storecfg, $volid);
1612 $format //= qemu_img_format
($scfg, $volname);
1619 my $is_rbd = $path =~ m/^rbd:/;
1622 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1623 foreach my $o (@qemu_drive_options) {
1624 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1627 # snapshot only accepts on|off
1628 if (defined($drive->{snapshot
})) {
1629 my $v = $drive->{snapshot
} ?
'on' : 'off';
1630 $opts .= ",snapshot=$v";
1633 if (defined($drive->{ro
})) { # ro maps to QEMUs `readonly`, which accepts `on` or `off` only
1634 $opts .= ",readonly=" . ($drive->{ro
} ?
'on' : 'off');
1637 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1638 my ($dir, $qmpname) = @$type;
1639 if (my $v = $drive->{"mbps$dir"}) {
1640 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1642 if (my $v = $drive->{"mbps${dir}_max"}) {
1643 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1645 if (my $v = $drive->{"bps${dir}_max_length"}) {
1646 $opts .= ",throttling.bps$qmpname-max-length=$v";
1648 if (my $v = $drive->{"iops${dir}"}) {
1649 $opts .= ",throttling.iops$qmpname=$v";
1651 if (my $v = $drive->{"iops${dir}_max"}) {
1652 $opts .= ",throttling.iops$qmpname-max=$v";
1654 if (my $v = $drive->{"iops${dir}_max_length"}) {
1655 $opts .= ",throttling.iops$qmpname-max-length=$v";
1660 $format = "rbd" if $is_rbd;
1661 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1663 $opts .= ",format=alloc-track,file.driver=$format";
1665 $opts .= ",format=$format";
1668 my $cache_direct = 0;
1670 if (my $cache = $drive->{cache
}) {
1671 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1672 } elsif (!drive_is_cdrom
($drive) && !($scfg && $scfg->{type
} eq 'btrfs' && !$scfg->{nocow
})) {
1673 $opts .= ",cache=none";
1677 # io_uring with cache mode writeback or writethrough on krbd will hang...
1678 my $rbd_no_io_uring = $scfg && $scfg->{type
} eq 'rbd' && $scfg->{krbd
} && !$cache_direct;
1680 # io_uring with cache mode writeback or writethrough on LVM will hang, without cache only
1681 # sometimes, just plain disable...
1682 my $lvm_no_io_uring = $scfg && $scfg->{type
} eq 'lvm';
1684 # io_uring causes problems when used with CIFS since kernel 5.15
1685 # Some discussion: https://www.spinics.net/lists/linux-cifs/msg26734.html
1686 my $cifs_no_io_uring = $scfg && $scfg->{type
} eq 'cifs';
1688 if (!$drive->{aio
}) {
1689 if ($io_uring && !$rbd_no_io_uring && !$lvm_no_io_uring && !$cifs_no_io_uring) {
1690 # io_uring supports all cache modes
1691 $opts .= ",aio=io_uring";
1693 # aio native works only with O_DIRECT
1695 $opts .= ",aio=native";
1697 $opts .= ",aio=threads";
1702 if (!drive_is_cdrom
($drive)) {
1704 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
1705 $detectzeroes = 'off';
1706 } elsif ($drive->{discard
}) {
1707 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
1709 # This used to be our default with discard not being specified:
1710 $detectzeroes = 'on';
1713 # note: 'detect-zeroes' works per blockdev and we want it to persist
1714 # after the alloc-track is removed, so put it on 'file' directly
1715 my $dz_param = $pbs_name ?
"file.detect-zeroes" : "detect-zeroes";
1716 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
1720 $opts .= ",backing=$pbs_name";
1721 $opts .= ",auto-remove=on";
1724 # my $file_param = $pbs_name ? "file.file.filename" : "file";
1725 my $file_param = "file";
1727 # non-rbd drivers require the underlying file to be a seperate block
1728 # node, so add a second .file indirection
1729 $file_param .= ".file" if !$is_rbd;
1730 $file_param .= ".filename";
1732 my $pathinfo = $path ?
"$file_param=$path," : '';
1734 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1737 sub print_pbs_blockdev
{
1738 my ($pbs_conf, $pbs_name) = @_;
1739 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1740 $blockdev .= ",repository=$pbs_conf->{repository}";
1741 $blockdev .= ",namespace=$pbs_conf->{namespace}" if $pbs_conf->{namespace
};
1742 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1743 $blockdev .= ",archive=$pbs_conf->{archive}";
1744 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile
};
1748 sub print_netdevice_full
{
1749 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type) = @_;
1751 my $device = $net->{model
};
1752 if ($net->{model
} eq 'virtio') {
1753 $device = 'virtio-net-pci';
1756 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
1757 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
1758 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
1759 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1760 # and out of each queue plus one config interrupt and control vector queue
1761 my $vectors = $net->{queues
} * 2 + 2;
1762 $tmpstr .= ",vectors=$vectors,mq=on";
1764 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
1766 if (my $mtu = $net->{mtu
}) {
1767 if ($net->{model
} eq 'virtio' && $net->{bridge
}) {
1768 my $bridge_mtu = PVE
::Network
::read_bridge_mtu
($net->{bridge
});
1771 } elsif ($mtu < 576) {
1772 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1773 } elsif ($mtu > $bridge_mtu) {
1774 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1776 $tmpstr .= ",host_mtu=$mtu";
1778 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
1782 if ($use_old_bios_files) {
1784 if ($device eq 'virtio-net-pci') {
1785 $romfile = 'pxe-virtio.rom';
1786 } elsif ($device eq 'e1000') {
1787 $romfile = 'pxe-e1000.rom';
1788 } elsif ($device eq 'e1000e') {
1789 $romfile = 'pxe-e1000e.rom';
1790 } elsif ($device eq 'ne2k') {
1791 $romfile = 'pxe-ne2k_pci.rom';
1792 } elsif ($device eq 'pcnet') {
1793 $romfile = 'pxe-pcnet.rom';
1794 } elsif ($device eq 'rtl8139') {
1795 $romfile = 'pxe-rtl8139.rom';
1797 $tmpstr .= ",romfile=$romfile" if $romfile;
1803 sub print_netdev_full
{
1804 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
1807 if ($netid =~ m/^net(\d+)$/) {
1811 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
1813 my $ifname = "tap${vmid}i$i";
1815 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1816 die "interface name '$ifname' is too long (max 15 character)\n"
1817 if length($ifname) >= 16;
1819 my $vhostparam = '';
1820 if (is_native
($arch)) {
1821 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
1824 my $vmname = $conf->{name
} || "vm$vmid";
1827 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
1829 if ($net->{bridge
}) {
1830 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1831 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
1833 $netdev = "type=user,id=$netid,hostname=$vmname";
1836 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
1842 'cirrus' => 'cirrus-vga',
1844 'vmware' => 'vmware-svga',
1845 'virtio' => 'virtio-vga',
1846 'virtio-gl' => 'virtio-vga-gl',
1849 sub print_vga_device
{
1850 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
1852 my $type = $vga_map->{$vga->{type
}};
1853 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
1854 $type = 'virtio-gpu';
1856 my $vgamem_mb = $vga->{memory
};
1858 my $max_outputs = '';
1860 $type = $id ?
'qxl' : 'qxl-vga';
1862 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
1863 # set max outputs so linux can have up to 4 qxl displays with one device
1864 if (min_version
($machine_version, 4, 1)) {
1865 $max_outputs = ",max_outputs=4";
1870 die "no devicetype for $vga->{type}\n" if !$type;
1874 if ($vga->{type
} =~ /^virtio/) {
1875 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
1876 $memory = ",max_hostmem=$bytes";
1878 # from https://www.spice-space.org/multiple-monitors.html
1879 $memory = ",vgamem_mb=$vga->{memory}";
1880 my $ram = $vgamem_mb * 4;
1881 my $vram = $vgamem_mb * 2;
1882 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1884 $memory = ",vgamem_mb=$vga->{memory}";
1886 } elsif ($qxlnum && $id) {
1887 $memory = ",ram_size=67108864,vram_size=33554432";
1891 if ($type eq 'VGA' && windows_version
($conf->{ostype
})) {
1892 $edidoff=",edid=off" if (!defined($conf->{bios
}) || $conf->{bios
} ne 'ovmf');
1895 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1896 my $vgaid = "vga" . ($id // '');
1898 if ($q35 && $vgaid eq 'vga') {
1899 # the first display uses pcie.0 bus on q35 machines
1900 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
1902 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
1905 if ($vga->{type
} eq 'virtio-gl') {
1906 my $base = '/usr/lib/x86_64-linux-gnu/lib';
1907 die "missing libraries for '$vga->{type}' detected! Please install 'libgl1' and 'libegl1'\n"
1908 if !-e
"${base}EGL.so.1" || !-e
"${base}GL.so.1";
1910 die "no DRM render node detected (/dev/dri/renderD*), no GPU? - needed for '$vga->{type}' display\n"
1911 if !PVE
::Tools
::dir_glob_regex
('/dev/dri/', "renderD.*");
1914 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
1917 sub parse_number_sets
{
1920 foreach my $part (split(/;/, $set)) {
1921 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
1922 die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1;
1923 push @$res, [ $1, $2 ];
1925 die "invalid range: $part\n";
1934 my $res = parse_property_string
($numa_fmt, $data);
1935 $res->{cpus
} = parse_number_sets
($res->{cpus
}) if defined($res->{cpus
});
1936 $res->{hostnodes
} = parse_number_sets
($res->{hostnodes
}) if defined($res->{hostnodes
});
1940 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1944 my $res = eval { parse_property_string
($net_fmt, $data) };
1949 if (!defined($res->{macaddr
})) {
1950 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1951 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1956 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1957 sub parse_ipconfig
{
1960 my $res = eval { parse_property_string
($ipconfig_fmt, $data) };
1966 if ($res->{gw
} && !$res->{ip
}) {
1967 warn 'gateway specified without specifying an IP address';
1970 if ($res->{gw6
} && !$res->{ip6
}) {
1971 warn 'IPv6 gateway specified without specifying an IPv6 address';
1974 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
1975 warn 'gateway specified together with DHCP';
1978 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
1980 warn "IPv6 gateway specified together with $res->{ip6} address";
1984 if (!$res->{ip
} && !$res->{ip6
}) {
1985 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
1994 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
1997 sub add_random_macs
{
1998 my ($settings) = @_;
2000 foreach my $opt (keys %$settings) {
2001 next if $opt !~ m/^net(\d+)$/;
2002 my $net = parse_net
($settings->{$opt});
2004 $settings->{$opt} = print_net
($net);
2008 sub vm_is_volid_owner
{
2009 my ($storecfg, $vmid, $volid) = @_;
2011 if ($volid !~ m
|^/|) {
2013 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
2014 if ($owner && ($owner == $vmid)) {
2022 sub vmconfig_register_unused_drive
{
2023 my ($storecfg, $vmid, $conf, $drive) = @_;
2025 if (drive_is_cloudinit
($drive)) {
2026 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
2028 delete $conf->{cloudinit
};
2029 } elsif (!drive_is_cdrom
($drive)) {
2030 my $volid = $drive->{file
};
2031 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
2032 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
2037 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
2041 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
2042 format_description
=> 'UUID',
2043 description
=> "Set SMBIOS1 UUID.",
2048 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2049 format_description
=> 'Base64 encoded string',
2050 description
=> "Set SMBIOS1 version.",
2055 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2056 format_description
=> 'Base64 encoded string',
2057 description
=> "Set SMBIOS1 serial number.",
2062 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2063 format_description
=> 'Base64 encoded string',
2064 description
=> "Set SMBIOS1 manufacturer.",
2069 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2070 format_description
=> 'Base64 encoded string',
2071 description
=> "Set SMBIOS1 product ID.",
2076 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2077 format_description
=> 'Base64 encoded string',
2078 description
=> "Set SMBIOS1 SKU string.",
2083 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2084 format_description
=> 'Base64 encoded string',
2085 description
=> "Set SMBIOS1 family string.",
2090 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
2098 my $res = eval { parse_property_string
($smbios1_fmt, $data) };
2105 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2108 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2110 sub parse_watchdog
{
2115 my $res = eval { parse_property_string
($watchdog_fmt, $value) };
2120 sub parse_guest_agent
{
2123 return {} if !defined($conf->{agent
});
2125 my $res = eval { parse_property_string
($agent_fmt, $conf->{agent
}) };
2128 # if the agent is disabled ignore the other potentially set properties
2129 return {} if !$res->{enabled
};
2134 my ($conf, $key) = @_;
2135 return undef if !defined($conf->{agent
});
2137 my $agent = parse_guest_agent
($conf);
2138 return $agent->{$key};
2144 return {} if !$value;
2145 my $res = eval { parse_property_string
($vga_fmt, $value) };
2155 my $res = eval { parse_property_string
($rng_fmt, $value) };
2160 sub parse_meta_info
{
2165 my $res = eval { parse_property_string
($meta_info_fmt, $value) };
2170 sub new_meta_info_string
{
2171 my () = @_; # for now do not allow to override any value
2173 return PVE
::JSONSchema
::print_property_string
(
2175 'creation-qemu' => kvm_user_version
(),
2176 ctime
=> "". int(time()),
2182 sub qemu_created_version_fixups
{
2183 my ($conf, $forcemachine, $kvmver) = @_;
2185 my $meta = parse_meta_info
($conf->{meta
}) // {};
2186 my $forced_vers = PVE
::QemuServer
::Machine
::extract_version
($forcemachine);
2188 # check if we need to apply some handling for VMs that always use the latest machine version but
2189 # had a machine version transition happen that affected HW such that, e.g., an OS config change
2190 # would be required (we do not want to pin machine version for non-windows OS type)
2192 (!defined($conf->{machine
}) || $conf->{machine
} =~ m/^(?:pc|q35|virt)$/) # non-versioned machine
2193 && (!defined($meta->{'creation-qemu'}) || !min_version
($meta->{'creation-qemu'}, 6, 1)) # created before 6.1
2194 && (!$forced_vers || min_version
($forced_vers, 6, 1)) # handle snapshot-rollback/migrations
2195 && min_version
($kvmver, 6, 1) # only need to apply the change since 6.1
2197 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
2198 if ($q35 && $conf->{ostype
} && $conf->{ostype
} eq 'l26') {
2199 # this changed to default-on in Q 6.1 for q35 machines, it will mess with PCI slot view
2200 # and thus with the predictable interface naming of systemd
2201 return ['-global', 'ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off'];
2207 PVE
::JSONSchema
::register_format
('pve-qm-usb-device', \
&verify_usb_device
);
2208 sub verify_usb_device
{
2209 my ($value, $noerr) = @_;
2211 return $value if parse_usb_device
($value);
2215 die "unable to parse usb device\n";
2218 # add JSON properties for create and set function
2219 sub json_config_properties
{
2220 my ($prop, $with_disk_alloc) = @_;
2222 my $skip_json_config_opts = {
2226 runningmachine
=> 1,
2231 foreach my $opt (keys %$confdesc) {
2232 next if $skip_json_config_opts->{$opt};
2234 if ($with_disk_alloc && is_valid_drivename
($opt)) {
2235 $prop->{$opt} = $PVE::QemuServer
::Drive
::drivedesc_hash_with_alloc-
>{$opt};
2237 $prop->{$opt} = $confdesc->{$opt};
2244 # Properties that we can read from an OVF file
2245 sub json_ovf_properties
{
2248 for my $device (PVE
::QemuServer
::Drive
::valid_drive_names
()) {
2249 $prop->{$device} = {
2251 format
=> 'pve-volume-id-or-absolute-path',
2252 description
=> "Disk image that gets imported to $device",
2259 description
=> "The number of CPU cores.",
2264 description
=> "Amount of RAM for the VM in MB.",
2269 description
=> "Name of the VM.",
2276 # return copy of $confdesc_cloudinit to generate documentation
2277 sub cloudinit_config_properties
{
2279 return dclone
($confdesc_cloudinit);
2283 my ($key, $value) = @_;
2285 die "unknown setting '$key'\n" if !$confdesc->{$key};
2287 my $type = $confdesc->{$key}->{type
};
2289 if (!defined($value)) {
2290 die "got undefined value\n";
2293 if ($value =~ m/[\n\r]/) {
2294 die "property contains a line feed\n";
2297 if ($type eq 'boolean') {
2298 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2299 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2300 die "type check ('boolean') failed - got '$value'\n";
2301 } elsif ($type eq 'integer') {
2302 return int($1) if $value =~ m/^(\d+)$/;
2303 die "type check ('integer') failed - got '$value'\n";
2304 } elsif ($type eq 'number') {
2305 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2306 die "type check ('number') failed - got '$value'\n";
2307 } elsif ($type eq 'string') {
2308 if (my $fmt = $confdesc->{$key}->{format
}) {
2309 PVE
::JSONSchema
::check_format
($fmt, $value);
2312 $value =~ s/^\"(.*)\"$/$1/;
2315 die "internal error"
2320 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
2322 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2324 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
2326 if ($conf->{template
}) {
2327 # check if any base image is still used by a linked clone
2328 PVE
::QemuConfig-
>foreach_volume_full($conf, { include_unused
=> 1 }, sub {
2329 my ($ds, $drive) = @_;
2330 return if drive_is_cdrom
($drive);
2332 my $volid = $drive->{file
};
2333 return if !$volid || $volid =~ m
|^/|;
2335 die "base volume '$volid' is still in use by linked cloned\n"
2336 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2342 my $remove_owned_drive = sub {
2343 my ($ds, $drive) = @_;
2344 return if drive_is_cdrom
($drive, 1);
2346 my $volid = $drive->{file
};
2347 return if !$volid || $volid =~ m
|^/|;
2348 return if $volids->{$volid};
2350 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2351 return if !$path || !$owner || ($owner != $vmid);
2353 $volids->{$volid} = 1;
2354 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2355 warn "Could not remove disk '$volid', check manually: $@" if $@;
2358 # only remove disks owned by this VM (referenced in the config)
2359 my $include_opts = {
2360 include_unused
=> 1,
2361 extra_keys
=> ['vmstate'],
2363 PVE
::QemuConfig-
>foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2365 for my $snap (values %{$conf->{snapshots
}}) {
2366 next if !defined($snap->{vmstate
});
2367 my $drive = PVE
::QemuConfig-
>parse_volume('vmstate', $snap->{vmstate
}, 1);
2368 next if !defined($drive);
2369 $remove_owned_drive->('vmstate', $drive);
2372 PVE
::QemuConfig-
>foreach_volume_full($conf->{pending
}, $include_opts, $remove_owned_drive);
2374 if ($purge_unreferenced) { # also remove unreferenced disk
2375 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid, undef, 'images');
2376 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2377 my ($volid, $sid, $volname, $d) = @_;
2378 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2383 if (defined $replacement_conf) {
2384 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2386 PVE
::QemuConfig-
>destroy_config($vmid);
2390 sub parse_vm_config
{
2391 my ($filename, $raw, $strict) = @_;
2393 return if !defined($raw);
2396 digest
=> Digest
::SHA
::sha1_hex
($raw),
2402 my $handle_error = sub {
2412 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2413 || die "got strange filename '$filename'";
2421 my @lines = split(/\n/, $raw);
2422 foreach my $line (@lines) {
2423 next if $line =~ m/^\s*$/;
2425 if ($line =~ m/^\[PENDING\]\s*$/i) {
2426 $section = 'pending';
2427 if (defined($descr)) {
2429 $conf->{description
} = $descr;
2432 $conf = $res->{$section} = {};
2434 } elsif ($line =~ m/^\[special:cloudinit\]\s*$/i) {
2435 $section = 'cloudinit';
2437 $conf = $res->{$section} = {};
2440 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2442 if (defined($descr)) {
2444 $conf->{description
} = $descr;
2447 $conf = $res->{snapshots
}->{$section} = {};
2451 if ($line =~ m/^\#(.*)$/) {
2452 $descr = '' if !defined($descr);
2453 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2457 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2458 $descr = '' if !defined($descr);
2459 $descr .= PVE
::Tools
::decode_text
($2);
2460 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2461 $conf->{snapstate
} = $1;
2462 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2465 $conf->{$key} = $value;
2466 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2468 if ($section eq 'pending') {
2469 $conf->{delete} = $value; # we parse this later
2471 $handle_error->("vm $vmid - property 'delete' is only allowed in [PENDING]\n");
2473 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2476 eval { $value = check_type
($key, $value); };
2478 $handle_error->("vm $vmid - unable to parse value of '$key' - $@");
2480 $key = 'ide2' if $key eq 'cdrom';
2481 my $fmt = $confdesc->{$key}->{format
};
2482 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2483 my $v = parse_drive
($key, $value);
2484 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2485 $v->{file
} = $volid;
2486 $value = print_drive
($v);
2488 $handle_error->("vm $vmid - unable to parse value of '$key'\n");
2493 $conf->{$key} = $value;
2496 $handle_error->("vm $vmid - unable to parse config: $line\n");
2500 if (defined($descr)) {
2502 $conf->{description
} = $descr;
2504 delete $res->{snapstate
}; # just to be sure
2509 sub write_vm_config
{
2510 my ($filename, $conf) = @_;
2512 delete $conf->{snapstate
}; # just to be sure
2514 if ($conf->{cdrom
}) {
2515 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2516 $conf->{ide2
} = $conf->{cdrom
};
2517 delete $conf->{cdrom
};
2520 # we do not use 'smp' any longer
2521 if ($conf->{sockets
}) {
2522 delete $conf->{smp
};
2523 } elsif ($conf->{smp
}) {
2524 $conf->{sockets
} = $conf->{smp
};
2525 delete $conf->{cores
};
2526 delete $conf->{smp
};
2529 my $used_volids = {};
2531 my $cleanup_config = sub {
2532 my ($cref, $pending, $snapname) = @_;
2534 foreach my $key (keys %$cref) {
2535 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2536 $key eq 'snapstate' || $key eq 'pending' || $key eq 'cloudinit';
2537 my $value = $cref->{$key};
2538 if ($key eq 'delete') {
2539 die "propertry 'delete' is only allowed in [PENDING]\n"
2541 # fixme: check syntax?
2544 eval { $value = check_type
($key, $value); };
2545 die "unable to parse value of '$key' - $@" if $@;
2547 $cref->{$key} = $value;
2549 if (!$snapname && is_valid_drivename
($key)) {
2550 my $drive = parse_drive
($key, $value);
2551 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2556 &$cleanup_config($conf);
2558 &$cleanup_config($conf->{pending
}, 1);
2560 &$cleanup_config($conf->{cloudinit
});
2562 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2563 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2564 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2567 # remove 'unusedX' settings if we re-add a volume
2568 foreach my $key (keys %$conf) {
2569 my $value = $conf->{$key};
2570 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2571 delete $conf->{$key};
2575 my $generate_raw_config = sub {
2576 my ($conf, $pending) = @_;
2580 # add description as comment to top of file
2581 if (defined(my $descr = $conf->{description
})) {
2583 foreach my $cl (split(/\n/, $descr)) {
2584 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2587 $raw .= "#\n" if $pending;
2591 foreach my $key (sort keys %$conf) {
2592 next if $key =~ /^(digest|description|pending|cloudinit|snapshots)$/;
2593 $raw .= "$key: $conf->{$key}\n";
2598 my $raw = &$generate_raw_config($conf);
2600 if (scalar(keys %{$conf->{pending
}})){
2601 $raw .= "\n[PENDING]\n";
2602 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2605 if (scalar(keys %{$conf->{cloudinit
}})){
2606 $raw .= "\n[special:cloudinit]\n";
2607 $raw .= &$generate_raw_config($conf->{cloudinit
});
2610 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2611 $raw .= "\n[$snapname]\n";
2612 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2622 # we use static defaults from our JSON schema configuration
2623 foreach my $key (keys %$confdesc) {
2624 if (defined(my $default = $confdesc->{$key}->{default})) {
2625 $res->{$key} = $default;
2633 my $vmlist = PVE
::Cluster
::get_vmlist
();
2635 return $res if !$vmlist || !$vmlist->{ids
};
2636 my $ids = $vmlist->{ids
};
2637 my $nodename = nodename
();
2639 foreach my $vmid (keys %$ids) {
2640 my $d = $ids->{$vmid};
2641 next if !$d->{node
} || $d->{node
} ne $nodename;
2642 next if !$d->{type
} || $d->{type
} ne 'qemu';
2643 $res->{$vmid}->{exists} = 1;
2648 # test if VM uses local resources (to prevent migration)
2649 sub check_local_resources
{
2650 my ($conf, $noerr) = @_;
2654 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2655 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2657 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2659 foreach my $k (keys %$conf) {
2660 next if $k =~ m/^usb/ && ($conf->{$k} =~ m/^spice(?![^,])/);
2661 # sockets are safe: they will recreated be on the target side post-migrate
2662 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2663 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2666 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2671 # check if used storages are available on all nodes (use by migrate)
2672 sub check_storage_availability
{
2673 my ($storecfg, $conf, $node) = @_;
2675 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2676 my ($ds, $drive) = @_;
2678 my $volid = $drive->{file
};
2681 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2684 # check if storage is available on both nodes
2685 my $scfg = PVE
::Storage
::storage_check_enabled
($storecfg, $sid);
2686 PVE
::Storage
::storage_check_enabled
($storecfg, $sid, $node);
2688 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $volid);
2690 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2691 if !$scfg->{content
}->{$vtype};
2695 # list nodes where all VM images are available (used by has_feature API)
2697 my ($conf, $storecfg) = @_;
2699 my $nodelist = PVE
::Cluster
::get_nodelist
();
2700 my $nodehash = { map { $_ => 1 } @$nodelist };
2701 my $nodename = nodename
();
2703 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2704 my ($ds, $drive) = @_;
2706 my $volid = $drive->{file
};
2709 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2711 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2712 if ($scfg->{disable
}) {
2714 } elsif (my $avail = $scfg->{nodes
}) {
2715 foreach my $node (keys %$nodehash) {
2716 delete $nodehash->{$node} if !$avail->{$node};
2718 } elsif (!$scfg->{shared
}) {
2719 foreach my $node (keys %$nodehash) {
2720 delete $nodehash->{$node} if $node ne $nodename
2729 sub check_local_storage_availability
{
2730 my ($conf, $storecfg) = @_;
2732 my $nodelist = PVE
::Cluster
::get_nodelist
();
2733 my $nodehash = { map { $_ => {} } @$nodelist };
2735 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2736 my ($ds, $drive) = @_;
2738 my $volid = $drive->{file
};
2741 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2743 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2745 if ($scfg->{disable
}) {
2746 foreach my $node (keys %$nodehash) {
2747 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2749 } elsif (my $avail = $scfg->{nodes
}) {
2750 foreach my $node (keys %$nodehash) {
2751 if (!$avail->{$node}) {
2752 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2759 foreach my $node (values %$nodehash) {
2760 if (my $unavail = $node->{unavailable_storages
}) {
2761 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2768 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2770 my ($vmid, $nocheck, $node) = @_;
2772 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2773 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2778 my $vzlist = config_list
();
2780 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2782 while (defined(my $de = $fd->read)) {
2783 next if $de !~ m/^(\d+)\.pid$/;
2785 next if !defined($vzlist->{$vmid});
2786 if (my $pid = check_running
($vmid)) {
2787 $vzlist->{$vmid}->{pid
} = $pid;
2794 our $vmstatus_return_properties = {
2795 vmid
=> get_standard_option
('pve-vmid'),
2797 description
=> "Qemu process status.",
2799 enum
=> ['stopped', 'running'],
2802 description
=> "Maximum memory in bytes.",
2805 renderer
=> 'bytes',
2808 description
=> "Root disk size in bytes.",
2811 renderer
=> 'bytes',
2814 description
=> "VM name.",
2819 description
=> "Qemu QMP agent status.",
2824 description
=> "PID of running qemu process.",
2829 description
=> "Uptime.",
2832 renderer
=> 'duration',
2835 description
=> "Maximum usable CPUs.",
2840 description
=> "The current config lock, if any.",
2845 description
=> "The current configured tags, if any",
2849 'running-machine' => {
2850 description
=> "The currently running machine type (if running).",
2855 description
=> "The currently running QEMU version (if running).",
2861 my $last_proc_pid_stat;
2863 # get VM status information
2864 # This must be fast and should not block ($full == false)
2865 # We only query KVM using QMP if $full == true (this can be slow)
2867 my ($opt_vmid, $full) = @_;
2871 my $storecfg = PVE
::Storage
::config
();
2873 my $list = vzlist
();
2874 my $defaults = load_defaults
();
2876 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
(1);
2878 my $cpucount = $cpuinfo->{cpus
} || 1;
2880 foreach my $vmid (keys %$list) {
2881 next if $opt_vmid && ($vmid ne $opt_vmid);
2883 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2885 my $d = { vmid
=> int($vmid) };
2886 $d->{pid
} = int($list->{$vmid}->{pid
}) if $list->{$vmid}->{pid
};
2888 # fixme: better status?
2889 $d->{status
} = $list->{$vmid}->{pid
} ?
'running' : 'stopped';
2891 my $size = PVE
::QemuServer
::Drive
::bootdisk_size
($storecfg, $conf);
2892 if (defined($size)) {
2893 $d->{disk
} = 0; # no info available
2894 $d->{maxdisk
} = $size;
2900 $d->{cpus
} = ($conf->{sockets
} || $defaults->{sockets
})
2901 * ($conf->{cores
} || $defaults->{cores
});
2902 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
2903 $d->{cpus
} = $conf->{vcpus
} if $conf->{vcpus
};
2905 $d->{name
} = $conf->{name
} || "VM $vmid";
2906 $d->{maxmem
} = $conf->{memory
} ?
$conf->{memory
}*(1024*1024)
2907 : $defaults->{memory
}*(1024*1024);
2909 if ($conf->{balloon
}) {
2910 $d->{balloon_min
} = $conf->{balloon
}*(1024*1024);
2911 $d->{shares
} = defined($conf->{shares
}) ?
$conf->{shares
}
2912 : $defaults->{shares
};
2923 $d->{diskwrite
} = 0;
2925 $d->{template
} = 1 if PVE
::QemuConfig-
>is_template($conf);
2927 $d->{serial
} = 1 if conf_has_serial
($conf);
2928 $d->{lock} = $conf->{lock} if $conf->{lock};
2929 $d->{tags
} = $conf->{tags
} if defined($conf->{tags
});
2934 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
2935 foreach my $dev (keys %$netdev) {
2936 next if $dev !~ m/^tap([1-9]\d*)i/;
2938 my $d = $res->{$vmid};
2941 $d->{netout
} += $netdev->{$dev}->{receive
};
2942 $d->{netin
} += $netdev->{$dev}->{transmit
};
2945 $d->{nics
}->{$dev}->{netout
} = int($netdev->{$dev}->{receive
});
2946 $d->{nics
}->{$dev}->{netin
} = int($netdev->{$dev}->{transmit
});
2951 my $ctime = gettimeofday
;
2953 foreach my $vmid (keys %$list) {
2955 my $d = $res->{$vmid};
2956 my $pid = $d->{pid
};
2959 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
2960 next if !$pstat; # not running
2962 my $used = $pstat->{utime} + $pstat->{stime
};
2964 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
2966 if ($pstat->{vsize
}) {
2967 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
2970 my $old = $last_proc_pid_stat->{$pid};
2972 $last_proc_pid_stat->{$pid} = {
2980 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
2982 if ($dtime > 1000) {
2983 my $dutime = $used - $old->{used
};
2985 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
2986 $last_proc_pid_stat->{$pid} = {
2992 $d->{cpu
} = $old->{cpu
};
2996 return $res if !$full;
2998 my $qmpclient = PVE
::QMPClient-
>new();
3000 my $ballooncb = sub {
3001 my ($vmid, $resp) = @_;
3003 my $info = $resp->{'return'};
3004 return if !$info->{max_mem
};
3006 my $d = $res->{$vmid};
3008 # use memory assigned to VM
3009 $d->{maxmem
} = $info->{max_mem
};
3010 $d->{balloon
} = $info->{actual
};
3012 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
3013 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
3014 $d->{freemem
} = $info->{free_mem
};
3017 $d->{ballooninfo
} = $info;
3020 my $blockstatscb = sub {
3021 my ($vmid, $resp) = @_;
3022 my $data = $resp->{'return'} || [];
3023 my $totalrdbytes = 0;
3024 my $totalwrbytes = 0;
3026 for my $blockstat (@$data) {
3027 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
3028 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
3030 $blockstat->{device
} =~ s/drive-//;
3031 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
3033 $res->{$vmid}->{diskread
} = $totalrdbytes;
3034 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
3037 my $machinecb = sub {
3038 my ($vmid, $resp) = @_;
3039 my $data = $resp->{'return'} || [];
3041 $res->{$vmid}->{'running-machine'} =
3042 PVE
::QemuServer
::Machine
::current_from_query_machines
($data);
3045 my $versioncb = sub {
3046 my ($vmid, $resp) = @_;
3047 my $data = $resp->{'return'} // {};
3048 my $version = 'unknown';
3050 if (my $v = $data->{qemu
}) {
3051 $version = $v->{major
} . "." . $v->{minor
} . "." . $v->{micro
};
3054 $res->{$vmid}->{'running-qemu'} = $version;
3057 my $statuscb = sub {
3058 my ($vmid, $resp) = @_;
3060 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
3061 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
3062 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
3063 # this fails if ballon driver is not loaded, so this must be
3064 # the last commnand (following command are aborted if this fails).
3065 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
3067 my $status = 'unknown';
3068 if (!defined($status = $resp->{'return'}->{status
})) {
3069 warn "unable to get VM status\n";
3073 $res->{$vmid}->{qmpstatus
} = $resp->{'return'}->{status
};
3076 foreach my $vmid (keys %$list) {
3077 next if $opt_vmid && ($vmid ne $opt_vmid);
3078 next if !$res->{$vmid}->{pid
}; # not running
3079 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
3082 $qmpclient->queue_execute(undef, 2);
3084 foreach my $vmid (keys %$list) {
3085 next if $opt_vmid && ($vmid ne $opt_vmid);
3086 next if !$res->{$vmid}->{pid
}; #not running
3088 # we can't use the $qmpclient since it might have already aborted on
3089 # 'query-balloon', but this might also fail for older versions...
3090 my $qemu_support = eval { mon_cmd
($vmid, "query-proxmox-support") };
3091 $res->{$vmid}->{'proxmox-support'} = $qemu_support // {};
3094 foreach my $vmid (keys %$list) {
3095 next if $opt_vmid && ($vmid ne $opt_vmid);
3096 $res->{$vmid}->{qmpstatus
} = $res->{$vmid}->{status
} if !$res->{$vmid}->{qmpstatus
};
3102 sub conf_has_serial
{
3105 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3106 if ($conf->{"serial$i"}) {
3114 sub conf_has_audio
{
3115 my ($conf, $id) = @_;
3118 my $audio = $conf->{"audio$id"};
3119 return if !defined($audio);
3121 my $audioproperties = parse_property_string
($audio_fmt, $audio);
3122 my $audiodriver = $audioproperties->{driver
} // 'spice';
3125 dev
=> $audioproperties->{device
},
3126 dev_id
=> "audiodev$id",
3127 backend
=> $audiodriver,
3128 backend_id
=> "$audiodriver-backend${id}",
3133 my ($audio, $audiopciaddr, $machine_version) = @_;
3137 my $id = $audio->{dev_id
};
3139 if (min_version
($machine_version, 4, 2)) {
3140 $audiodev = ",audiodev=$audio->{backend_id}";
3143 if ($audio->{dev
} eq 'AC97') {
3144 push @$devs, '-device', "AC97,id=${id}${audiopciaddr}$audiodev";
3145 } elsif ($audio->{dev
} =~ /intel\-hda$/) {
3146 push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
3147 push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0$audiodev";
3148 push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1$audiodev";
3150 die "unkown audio device '$audio->{dev}', implement me!";
3153 push @$devs, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
3161 socket => "/var/run/qemu-server/$vmid.swtpm",
3162 pid
=> "/var/run/qemu-server/$vmid.swtpm.pid",
3166 sub add_tpm_device
{
3167 my ($vmid, $devices, $conf) = @_;
3169 return if !$conf->{tpmstate0
};
3171 my $paths = get_tpm_paths
($vmid);
3173 push @$devices, "-chardev", "socket,id=tpmchar,path=$paths->{socket}";
3174 push @$devices, "-tpmdev", "emulator,id=tpmdev,chardev=tpmchar";
3175 push @$devices, "-device", "tpm-tis,tpmdev=tpmdev";
3179 my ($storecfg, $vmid, $tpmdrive, $migration) = @_;
3181 return if !$tpmdrive;
3184 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
3185 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
3187 $state = PVE
::Storage
::map_volume
($storecfg, $tpm->{file
});
3189 $state = $tpm->{file
};
3192 my $paths = get_tpm_paths
($vmid);
3194 # during migration, we will get state from remote
3197 # run swtpm_setup to create a new TPM state if it doesn't exist yet
3204 "--create-platform-cert",
3207 "/etc/swtpm_setup.conf", # do not use XDG configs
3209 "0", # force creation as root, error if not possible
3210 "--not-overwrite", # ignore existing state, do not modify
3213 push @$setup_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3214 # TPM 2.0 supports ECC crypto, use if possible
3215 push @$setup_cmd, "--ecc" if $tpm->{version
} eq 'v2.0';
3217 run_command
($setup_cmd, outfunc
=> sub {
3218 print "swtpm_setup: $1\n";
3222 my $emulator_cmd = [
3226 "backend-uri=file://$state,mode=0600",
3228 "type=unixio,path=$paths->{socket},mode=0600",
3230 "file=$paths->{pid}",
3231 "--terminate", # terminate on QEMU disconnect
3234 push @$emulator_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3235 run_command
($emulator_cmd, outfunc
=> sub { print $1; });
3237 my $tries = 100; # swtpm may take a bit to start before daemonizing, wait up to 5s for pid
3238 while (! -e
$paths->{pid
}) {
3239 die "failed to start swtpm: pid file '$paths->{pid}' wasn't created.\n" if --$tries == 0;
3243 # return untainted PID of swtpm daemon so it can be killed on error
3244 file_read_firstline
($paths->{pid
}) =~ m/(\d+)/;
3248 sub vga_conf_has_spice
{
3251 my $vgaconf = parse_vga
($vga);
3252 my $vgatype = $vgaconf->{type
};
3253 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
3260 return get_host_arch
() eq $arch;
3265 return $conf->{arch
} // get_host_arch
();
3268 my $default_machines = {
3273 sub get_installed_machine_version
{
3274 my ($kvmversion) = @_;
3275 $kvmversion = kvm_user_version
() if !defined($kvmversion);
3276 $kvmversion =~ m/^(\d+\.\d+)/;
3280 sub windows_get_pinned_machine_version
{
3281 my ($machine, $base_version, $kvmversion) = @_;
3283 my $pin_version = $base_version;
3284 if (!defined($base_version) ||
3285 !PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($base_version, $kvmversion)
3287 $pin_version = get_installed_machine_version
($kvmversion);
3289 if (!$machine || $machine eq 'pc') {
3290 $machine = "pc-i440fx-$pin_version";
3291 } elsif ($machine eq 'q35') {
3292 $machine = "pc-q35-$pin_version";
3293 } elsif ($machine eq 'virt') {
3294 $machine = "virt-$pin_version";
3296 warn "unknown machine type '$machine', not touching that!\n";
3302 sub get_vm_machine
{
3303 my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
3305 my $machine = $forcemachine || $conf->{machine
};
3307 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
3308 $kvmversion //= kvm_user_version
();
3309 # we must pin Windows VMs without a specific version to 5.1, as 5.2 fixed a bug in ACPI
3310 # layout which confuses windows quite a bit and may result in various regressions..
3311 # see: https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg08484.html
3312 if (windows_version
($conf->{ostype
})) {
3313 $machine = windows_get_pinned_machine_version
($machine, '5.1', $kvmversion);
3316 $machine ||= $default_machines->{$arch};
3317 if ($add_pve_version) {
3318 my $pvever = PVE
::QemuServer
::Machine
::get_pve_version
($kvmversion);
3319 $machine .= "+pve$pvever";
3323 if ($add_pve_version && $machine !~ m/\+pve\d+?(?:\.pxe)?$/) {
3324 my $is_pxe = $machine =~ m/^(.*?)\.pxe$/;
3325 $machine = $1 if $is_pxe;
3327 # for version-pinned machines that do not include a pve-version (e.g.
3328 # pc-q35-4.1), we assume 0 to keep them stable in case we bump
3329 $machine .= '+pve0';
3331 $machine .= '.pxe' if $is_pxe;
3337 sub get_ovmf_files
($$$) {
3338 my ($arch, $efidisk, $smm) = @_;
3340 my $types = $OVMF->{$arch}
3341 or die "no OVMF images known for architecture '$arch'\n";
3343 my $type = 'default';
3344 if (defined($efidisk->{efitype
}) && $efidisk->{efitype
} eq '4m') {
3345 $type = $smm ?
"4m" : "4m-no-smm";
3346 $type .= '-ms' if $efidisk->{'pre-enrolled-keys'};
3349 return $types->{$type}->@*;
3353 aarch64
=> '/usr/bin/qemu-system-aarch64',
3354 x86_64
=> '/usr/bin/qemu-system-x86_64',
3356 sub get_command_for_arch
($) {
3358 return '/usr/bin/kvm' if is_native
($arch);
3360 my $cmd = $Arch2Qemu->{$arch}
3361 or die "don't know how to emulate architecture '$arch'\n";
3365 # To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
3366 # to use in a QEMU command line (-cpu element), first array_intersect the result
3367 # of query_supported_ with query_understood_. This is necessary because:
3369 # a) query_understood_ returns flags the host cannot use and
3370 # b) query_supported_ (rather the QMP call) doesn't actually return CPU
3371 # flags, but CPU settings - with most of them being flags. Those settings
3372 # (and some flags, curiously) cannot be specified as a "-cpu" argument.
3374 # query_supported_ needs to start up to 2 temporary VMs and is therefore rather
3375 # expensive. If you need the value returned from this, you can get it much
3376 # cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags-$accel') with
3377 # $accel being 'kvm' or 'tcg'.
3379 # pvestatd calls this function on startup and whenever the QEMU/KVM version
3380 # changes, automatically populating pmxcfs.
3382 # Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
3383 # since kvm and tcg machines support different flags
3385 sub query_supported_cpu_flags
{
3388 $arch //= get_host_arch
();
3389 my $default_machine = $default_machines->{$arch};
3393 # FIXME: Once this is merged, the code below should work for ARM as well:
3394 # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
3395 die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
3398 my $kvm_supported = defined(kvm_version
());
3399 my $qemu_cmd = get_command_for_arch
($arch);
3401 my $pidfile = PVE
::QemuServer
::Helpers
::pidfile_name
($fakevmid);
3403 # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
3404 my $query_supported_run_qemu = sub {
3410 '-machine', $default_machine,
3412 '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
3413 '-mon', 'chardev=qmp,mode=control',
3414 '-pidfile', $pidfile,
3419 push @$cmd, '-accel', 'tcg';
3422 my $rc = run_command
($cmd, noerr
=> 1, quiet
=> 0);
3423 die "QEMU flag querying VM exited with code " . $rc if $rc;
3426 my $cmd_result = mon_cmd
(
3428 'query-cpu-model-expansion',
3430 model
=> { name
=> 'host' }
3433 my $props = $cmd_result->{model
}->{props
};
3434 foreach my $prop (keys %$props) {
3435 next if $props->{$prop} ne '1';
3436 # QEMU returns some flags multiple times, with '_', '.' or '-'
3437 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
3438 # We only keep those with underscores, to match /proc/cpuinfo
3439 $prop =~ s/\.|-/_/g;
3440 $flags->{$prop} = 1;
3445 # force stop with 10 sec timeout and 'nocheck', always stop, even if QMP failed
3446 vm_stop
(undef, $fakevmid, 1, 1, 10, 0, 1);
3450 return [ sort keys %$flags ];
3453 # We need to query QEMU twice, since KVM and TCG have different supported flags
3454 PVE
::QemuConfig-
>lock_config($fakevmid, sub {
3455 $flags->{tcg
} = eval { $query_supported_run_qemu->(0) };
3456 warn "warning: failed querying supported tcg flags: $@\n" if $@;
3458 if ($kvm_supported) {
3459 $flags->{kvm
} = eval { $query_supported_run_qemu->(1) };
3460 warn "warning: failed querying supported kvm flags: $@\n" if $@;
3467 # Understood CPU flags are written to a file at 'pve-qemu' compile time
3468 my $understood_cpu_flag_dir = "/usr/share/kvm";
3469 sub query_understood_cpu_flags
{
3470 my $arch = get_host_arch
();
3471 my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
3473 die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
3476 my $raw = file_get_contents
($filepath);
3477 $raw =~ s/^\s+|\s+$//g;
3478 my @flags = split(/\s+/, $raw);
3483 # Since commit 277d33454f77ec1d1e0bc04e37621e4dd2424b67 in pve-qemu, smm is not off by default
3484 # anymore. But smm=off seems to be required when using SeaBIOS and serial display.
3485 my sub should_disable_smm
{
3486 my ($conf, $vga) = @_;
3488 return (!defined($conf->{bios
}) || $conf->{bios
} eq 'seabios') &&
3489 $vga->{type
} && $vga->{type
} =~ m/^(serial\d+|none)$/;
3492 sub config_to_command
{
3493 my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
3497 my ($globalFlags, $machineFlags, $rtcFlags) = ([], [], []);
3500 my $ostype = $conf->{ostype
};
3501 my $winversion = windows_version
($ostype);
3502 my $kvm = $conf->{kvm
};
3503 my $nodename = nodename
();
3505 my $arch = get_vm_arch
($conf);
3506 my $kvm_binary = get_command_for_arch
($arch);
3507 my $kvmver = kvm_user_version
($kvm_binary);
3509 if (!$kvmver || $kvmver !~ m/^(\d+)\.(\d+)/ || $1 < 3) {
3510 $kvmver //= "undefined";
3511 die "Detected old QEMU binary ('$kvmver', at least 3.0 is required)\n";
3514 my $add_pve_version = min_version
($kvmver, 4, 1);
3516 my $machine_type = get_vm_machine
($conf, $forcemachine, $arch, $add_pve_version);
3517 my $machine_version = extract_version
($machine_type, $kvmver);
3518 $kvm //= 1 if is_native
($arch);
3520 $machine_version =~ m/(\d+)\.(\d+)/;
3521 my ($machine_major, $machine_minor) = ($1, $2);
3523 if ($kvmver =~ m/^\d+\.\d+\.(\d+)/ && $1 >= 90) {
3524 warn "warning: Installed QEMU version ($kvmver) is a release candidate, ignoring version checks\n";
3525 } elsif (!min_version
($kvmver, $machine_major, $machine_minor)) {
3526 die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type',"
3527 ." please upgrade node '$nodename'\n"
3528 } elsif (!PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($machine_version, $kvmver)) {
3529 my $max_pve_version = PVE
::QemuServer
::Machine
::get_pve_version
($machine_version);
3530 die "Installed qemu-server (max feature level for $machine_major.$machine_minor is"
3531 ." pve$max_pve_version) is too old to run machine type '$machine_type', please upgrade"
3532 ." node '$nodename'\n";
3535 # if a specific +pve version is required for a feature, use $version_guard
3536 # instead of min_version to allow machines to be run with the minimum
3538 my $required_pve_version = 0;
3539 my $version_guard = sub {
3540 my ($major, $minor, $pve) = @_;
3541 return 0 if !min_version
($machine_version, $major, $minor, $pve);
3542 my $max_pve = PVE
::QemuServer
::Machine
::get_pve_version
("$major.$minor");
3543 return 1 if min_version
($machine_version, $major, $minor, $max_pve+1);
3544 $required_pve_version = $pve if $pve && $pve > $required_pve_version;
3548 if ($kvm && !defined kvm_version
()) {
3549 die "KVM virtualisation configured, but not available. Either disable in VM configuration"
3550 ." or enable in BIOS.\n";
3553 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3554 my $hotplug_features = parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
3555 my $use_old_bios_files = undef;
3556 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3558 if ($conf->{affinity
}) {
3559 push @$cmd, "/usr/bin/taskset";
3560 push @$cmd, "--cpu-list";
3561 push @$cmd, "--all-tasks";
3562 push @$cmd, $conf->{affinity
};
3565 push @$cmd, $kvm_binary;
3567 push @$cmd, '-id', $vmid;
3569 my $vmname = $conf->{name
} || "vm$vmid";
3571 push @$cmd, '-name', "$vmname,debug-threads=on";
3573 push @$cmd, '-no-shutdown';
3577 my $qmpsocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid);
3578 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server=on,wait=off";
3579 push @$cmd, '-mon', "chardev=qmp,mode=control";
3581 if (min_version
($machine_version, 2, 12)) {
3582 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3583 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3586 push @$cmd, '-pidfile' , PVE
::QemuServer
::Helpers
::pidfile_name
($vmid);
3588 push @$cmd, '-daemonize';
3590 if ($conf->{smbios1
}) {
3591 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
3592 if ($smbios_conf->{base64
}) {
3593 # Do not pass base64 flag to qemu
3594 delete $smbios_conf->{base64
};
3595 my $smbios_string = "";
3596 foreach my $key (keys %$smbios_conf) {
3598 if ($key eq "uuid") {
3599 $value = $smbios_conf->{uuid
}
3601 $value = decode_base64
($smbios_conf->{$key});
3603 # qemu accepts any binary data, only commas need escaping by double comma
3605 $smbios_string .= "," . $key . "=" . $value if $value;
3607 push @$cmd, '-smbios', "type=1" . $smbios_string;
3609 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3613 if ($conf->{bios
} && $conf->{bios
} eq 'ovmf') {
3615 if (my $efidisk = $conf->{efidisk0
}) {
3616 $d = parse_drive
('efidisk0', $efidisk);
3619 my ($ovmf_code, $ovmf_vars) = get_ovmf_files
($arch, $d, $q35);
3620 die "uefi base image '$ovmf_code' not found\n" if ! -f
$ovmf_code;
3622 my ($path, $format);
3623 my $read_only_str = '';
3625 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($d->{file
}, 1);
3626 $format = $d->{format
};
3628 $path = PVE
::Storage
::path
($storecfg, $d->{file
});
3629 if (!defined($format)) {
3630 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
3631 $format = qemu_img_format
($scfg, $volname);
3635 die "efidisk format must be specified\n"
3636 if !defined($format);
3639 $read_only_str = ',readonly=on' if drive_is_read_only
($conf, $d);
3641 log_warn
("no efidisk configured! Using temporary efivars disk.");
3642 $path = "/tmp/$vmid-ovmf.fd";
3643 PVE
::Tools
::file_copy
($ovmf_vars, $path, -s
$ovmf_vars);
3649 if ($format eq 'raw' && $version_guard->(4, 1, 2)) {
3650 $size_str = ",size=" . (-s
$ovmf_vars);
3653 # SPI flash does lots of read-modify-write OPs, without writeback this gets really slow #3329
3655 if ($path =~ m/^rbd:/) {
3656 $cache = ',cache=writeback';
3657 $path .= ':rbd_cache_policy=writeback'; # avoid write-around, we *need* to cache writes too
3660 push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code";
3661 push @$cmd, '-drive', "if=pflash,unit=1$cache,format=$format,id=drive-efidisk0$size_str,file=${path}${read_only_str}";
3664 if ($q35) { # tell QEMU to load q35 config early
3665 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3666 if (min_version
($machine_version, 4, 0)) {
3667 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3669 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3673 if (defined(my $fixups = qemu_created_version_fixups
($conf, $forcemachine, $kvmver))) {
3674 push @$cmd, $fixups->@*;
3677 if ($conf->{vmgenid
}) {
3678 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid
};
3681 # add usb controllers
3682 my @usbcontrollers = PVE
::QemuServer
::USB
::get_usb_controllers
(
3683 $conf, $bridges, $arch, $machine_type, $usbdesc->{format
}, $MAX_USB_DEVICES, $machine_version);
3684 push @$devices, @usbcontrollers if @usbcontrollers;
3685 my $vga = parse_vga
($conf->{vga
});
3687 my $qxlnum = vga_conf_has_spice
($conf->{vga
});
3688 $vga->{type
} = 'qxl' if $qxlnum;
3690 if (!$vga->{type
}) {
3691 if ($arch eq 'aarch64') {
3692 $vga->{type
} = 'virtio';
3693 } elsif (min_version
($machine_version, 2, 9)) {
3694 $vga->{type
} = (!$winversion || $winversion >= 6) ?
'std' : 'cirrus';
3696 $vga->{type
} = ($winversion >= 6) ?
'std' : 'cirrus';
3700 # enable absolute mouse coordinates (needed by vnc)
3701 my $tablet = $conf->{tablet
};
3702 if (!defined($tablet)) {
3703 $tablet = $defaults->{tablet
};
3704 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3705 $tablet = 0 if $vga->{type
} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3709 push @$devices, '-device', print_tabletdevice_full
($conf, $arch) if $tablet;
3710 my $kbd = print_keyboarddevice_full
($conf, $arch);
3711 push @$devices, '-device', $kbd if defined($kbd);
3714 my $bootorder = device_bootorder
($conf);
3716 # host pci device passthrough
3717 my ($kvm_off, $gpu_passthrough, $legacy_igd) = PVE
::QemuServer
::PCI
::print_hostpci_devices
(
3718 $vmid, $conf, $devices, $vga, $winversion, $q35, $bridges, $arch, $machine_type, $bootorder);
3721 my $usb_dev_features = {};
3722 $usb_dev_features->{spice_usb3
} = 1 if min_version
($machine_version, 4, 0);
3724 my @usbdevices = PVE
::QemuServer
::USB
::get_usb_devices
(
3725 $conf, $usbdesc->{format
}, $MAX_USB_DEVICES, $usb_dev_features, $bootorder, $machine_version);
3726 push @$devices, @usbdevices if @usbdevices;
3729 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3730 my $path = $conf->{"serial$i"} or next;
3731 if ($path eq 'socket') {
3732 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3733 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
3734 # On aarch64, serial0 is the UART device. Qemu only allows
3735 # connecting UART devices via the '-serial' command line, as
3736 # the device has a fixed slot on the hardware...
3737 if ($arch eq 'aarch64' && $i == 0) {
3738 push @$devices, '-serial', "chardev:serial$i";
3740 push @$devices, '-device', "isa-serial,chardev=serial$i";
3743 die "no such serial device\n" if ! -c
$path;
3744 push @$devices, '-chardev', "tty,id=serial$i,path=$path";
3745 push @$devices, '-device', "isa-serial,chardev=serial$i";
3750 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3751 if (my $path = $conf->{"parallel$i"}) {
3752 die "no such parallel device\n" if ! -c
$path;
3753 my $devtype = $path =~ m!^/dev/usb/lp! ?
'tty' : 'parport';
3754 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3755 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3759 if (min_version
($machine_version, 4, 0) && (my $audio = conf_has_audio
($conf))) {
3760 my $audiopciaddr = print_pci_addr
("audio0", $bridges, $arch, $machine_type);
3761 my $audio_devs = audio_devs
($audio, $audiopciaddr, $machine_version);
3762 push @$devices, @$audio_devs;
3765 add_tpm_device
($vmid, $devices, $conf);
3768 $sockets = $conf->{smp
} if $conf->{smp
}; # old style - no longer iused
3769 $sockets = $conf->{sockets
} if $conf->{sockets
};
3771 my $cores = $conf->{cores
} || 1;
3773 my $maxcpus = $sockets * $cores;
3775 my $vcpus = $conf->{vcpus
} ?
$conf->{vcpus
} : $maxcpus;
3777 my $allowed_vcpus = $cpuinfo->{cpus
};
3779 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n" if ($allowed_vcpus < $maxcpus);
3781 if ($hotplug_features->{cpu
} && min_version
($machine_version, 2, 7)) {
3782 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3783 for (my $i = 2; $i <= $vcpus; $i++) {
3784 my $cpustr = print_cpu_device
($conf,$i);
3785 push @$cmd, '-device', $cpustr;
3790 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3792 push @$cmd, '-nodefaults';
3794 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3796 push @$cmd, '-no-acpi' if defined($conf->{acpi
}) && $conf->{acpi
} == 0;
3798 push @$cmd, '-no-reboot' if defined($conf->{reboot
}) && $conf->{reboot
} == 0;
3800 if ($vga->{type
} && $vga->{type
} !~ m/^serial\d+$/ && $vga->{type
} ne 'none'){
3801 push @$devices, '-device', print_vga_device
(
3802 $conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
3804 push @$cmd, '-display', 'egl-headless,gl=core' if $vga->{type
} eq 'virtio-gl'; # VIRGL
3806 my $socket = PVE
::QemuServer
::Helpers
::vnc_socket
($vmid);
3807 push @$cmd, '-vnc', "unix:$socket,password=on";
3809 push @$cmd, '-vga', 'none' if $vga->{type
} eq 'none';
3810 push @$cmd, '-nographic';
3814 my $tdf = defined($conf->{tdf
}) ?
$conf->{tdf
} : $defaults->{tdf
};
3815 my $useLocaltime = $conf->{localtime};
3817 if ($winversion >= 5) { # windows
3818 $useLocaltime = 1 if !defined($conf->{localtime});
3820 # use time drift fix when acpi is enabled
3821 if (!(defined($conf->{acpi
}) && $conf->{acpi
} == 0)) {
3822 $tdf = 1 if !defined($conf->{tdf
});
3826 if ($winversion >= 6) {
3827 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3828 push @$cmd, '-no-hpet';
3831 push @$rtcFlags, 'driftfix=slew' if $tdf;
3833 if ($conf->{startdate
} && $conf->{startdate
} ne 'now') {
3834 push @$rtcFlags, "base=$conf->{startdate}";
3835 } elsif ($useLocaltime) {
3836 push @$rtcFlags, 'base=localtime';
3840 push @$cmd, '-cpu', $forcecpu;
3842 push @$cmd, get_cpu_options
($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
3845 PVE
::QemuServer
::Memory
::config
($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
3847 push @$cmd, '-S' if $conf->{freeze
};
3849 push @$cmd, '-k', $conf->{keyboard
} if defined($conf->{keyboard
});
3851 my $guest_agent = parse_guest_agent
($conf);
3853 if ($guest_agent->{enabled
}) {
3854 my $qgasocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid, 1);
3855 push @$devices, '-chardev', "socket,path=$qgasocket,server=on,wait=off,id=qga0";
3857 if (!$guest_agent->{type
} || $guest_agent->{type
} eq 'virtio') {
3858 my $pciaddr = print_pci_addr
("qga0", $bridges, $arch, $machine_type);
3859 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3860 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3861 } elsif ($guest_agent->{type
} eq 'isa') {
3862 push @$devices, '-device', "isa-serial,chardev=qga0";
3866 my $rng = $conf->{rng0
} ? parse_rng
($conf->{rng0
}) : undef;
3867 if ($rng && $version_guard->(4, 1, 2)) {
3868 check_rng_source
($rng->{source
});
3870 my $max_bytes = $rng->{max_bytes
} // $rng_fmt->{max_bytes
}->{default};
3871 my $period = $rng->{period
} // $rng_fmt->{period
}->{default};
3872 my $limiter_str = "";
3874 $limiter_str = ",max-bytes=$max_bytes,period=$period";
3877 my $rng_addr = print_pci_addr
("rng0", $bridges, $arch, $machine_type);
3878 push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
3879 push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
3884 if ($qxlnum || $vga->{type
} =~ /^virtio/) {
3887 for (my $i = 1; $i < $qxlnum; $i++){
3888 push @$devices, '-device', print_vga_device
(
3889 $conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
3892 # assume other OS works like Linux
3893 my ($ram, $vram) = ("134217728", "67108864");
3894 if ($vga->{memory
}) {
3895 $ram = PVE
::Tools
::convert_size
($qxlnum*4*$vga->{memory
}, 'mb' => 'b');
3896 $vram = PVE
::Tools
::convert_size
($qxlnum*2*$vga->{memory
}, 'mb' => 'b');
3898 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3899 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3903 my $pciaddr = print_pci_addr
("spice", $bridges, $arch, $machine_type);
3905 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
3906 my @nodeaddrs = PVE
::Tools
::getaddrinfo_all
('localhost', family
=> $pfamily);
3907 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3909 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3910 push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
3911 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3913 my $localhost = PVE
::Network
::addr_to_ip
($nodeaddrs[0]->{addr
});
3914 $spice_port = PVE
::Tools
::next_spice_port
($pfamily, $localhost);
3916 my $spice_enhancement_str = $conf->{spice_enhancements
} // '';
3917 my $spice_enhancement = parse_property_string
($spice_enhancements_fmt, $spice_enhancement_str);
3918 if ($spice_enhancement->{foldersharing
}) {
3919 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3920 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3923 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3924 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}"
3925 if $spice_enhancement->{videostreaming
};
3927 push @$devices, '-spice', "$spice_opts";
3930 # enable balloon by default, unless explicitly disabled
3931 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
3932 my $pciaddr = print_pci_addr
("balloon0", $bridges, $arch, $machine_type);
3933 my $ballooncmd = "virtio-balloon-pci,id=balloon0$pciaddr";
3934 $ballooncmd .= ",free-page-reporting=on" if min_version
($machine_version, 6, 2);
3935 push @$devices, '-device', $ballooncmd;
3938 if ($conf->{watchdog
}) {
3939 my $wdopts = parse_watchdog
($conf->{watchdog
});
3940 my $pciaddr = print_pci_addr
("watchdog", $bridges, $arch, $machine_type);
3941 my $watchdog = $wdopts->{model
} || 'i6300esb';
3942 push @$devices, '-device', "$watchdog$pciaddr";
3943 push @$devices, '-watchdog-action', $wdopts->{action
} if $wdopts->{action
};
3947 my $scsicontroller = {};
3948 my $ahcicontroller = {};
3949 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : $defaults->{scsihw
};
3951 # Add iscsi initiator name if available
3952 if (my $initiator = get_initiator_name
()) {
3953 push @$devices, '-iscsi', "initiator-name=$initiator";
3956 PVE
::QemuConfig-
>foreach_volume($conf, sub {
3957 my ($ds, $drive) = @_;
3959 if (PVE
::Storage
::parse_volume_id
($drive->{file
}, 1)) {
3960 check_volume_storage_type
($storecfg, $drive->{file
});
3961 push @$vollist, $drive->{file
};
3964 # ignore efidisk here, already added in bios/fw handling code above
3965 return if $drive->{interface
} eq 'efidisk';
3967 return if $drive->{interface
} eq 'tpmstate';
3969 $use_virtio = 1 if $ds =~ m/^virtio/;
3971 $drive->{bootindex
} = $bootorder->{$ds} if $bootorder->{$ds};
3973 if ($drive->{interface
} eq 'virtio'){
3974 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread
};
3977 if ($drive->{interface
} eq 'scsi') {
3979 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
3981 die "scsi$drive->{index}: machine version 4.1~pve2 or higher is required to use more than 14 SCSI disks\n"
3982 if $drive->{index} > 13 && !&$version_guard(4, 1, 2);
3984 my $pciaddr = print_pci_addr
("$controller_prefix$controller", $bridges, $arch, $machine_type);
3985 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ?
"virtio-scsi-pci" : $scsihw;
3988 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{iothread
}){
3989 $iothread .= ",iothread=iothread-$controller_prefix$controller";
3990 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
3991 } elsif ($drive->{iothread
}) {
3993 "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n"
3998 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{queues
}){
3999 $queues = ",num_queues=$drive->{queues}";
4002 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues"
4003 if !$scsicontroller->{$controller};
4004 $scsicontroller->{$controller}=1;
4007 if ($drive->{interface
} eq 'sata') {
4008 my $controller = int($drive->{index} / $PVE::QemuServer
::Drive
::MAX_SATA_DISKS
);
4009 my $pciaddr = print_pci_addr
("ahci$controller", $bridges, $arch, $machine_type);
4010 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr"
4011 if !$ahcicontroller->{$controller};
4012 $ahcicontroller->{$controller}=1;
4015 my $pbs_conf = $pbs_backing->{$ds};
4016 my $pbs_name = undef;
4018 $pbs_name = "drive-$ds-pbs";
4019 push @$devices, '-blockdev', print_pbs_blockdev
($pbs_conf, $pbs_name);
4022 my $drive_cmd = print_drive_commandline_full
(
4023 $storecfg, $vmid, $drive, $pbs_name, min_version
($kvmver, 6, 0));
4025 # extra protection for templates, but SATA and IDE don't support it..
4026 $drive_cmd .= ',readonly=on' if drive_is_read_only
($conf, $drive);
4028 push @$devices, '-drive',$drive_cmd;
4029 push @$devices, '-device', print_drivedevice_full
(
4030 $storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
4033 for (my $i = 0; $i < $MAX_NETS; $i++) {
4034 my $netname = "net$i";
4036 next if !$conf->{$netname};
4037 my $d = parse_net
($conf->{$netname});
4040 $use_virtio = 1 if $d->{model
} eq 'virtio';
4042 $d->{bootindex
} = $bootorder->{$netname} if $bootorder->{$netname};
4044 my $netdevfull = print_netdev_full
($vmid, $conf, $arch, $d, $netname);
4045 push @$devices, '-netdev', $netdevfull;
4047 my $netdevicefull = print_netdevice_full
(
4048 $vmid, $conf, $d, $netname, $bridges, $use_old_bios_files, $arch, $machine_type);
4050 push @$devices, '-device', $netdevicefull;
4053 if ($conf->{ivshmem
}) {
4054 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
4058 $bus = print_pcie_addr
("ivshmem");
4060 $bus = print_pci_addr
("ivshmem", $bridges, $arch, $machine_type);
4063 my $ivshmem_name = $ivshmem->{name
} // $vmid;
4064 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
4066 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
4067 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path"
4068 .",size=$ivshmem->{size}M";
4071 # pci.4 is nested in pci.1
4072 $bridges->{1} = 1 if $bridges->{4};
4074 if (!$q35) { # add pci bridges
4075 if (min_version
($machine_version, 2, 3)) {
4079 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
4082 for my $k (sort {$b cmp $a} keys %$bridges) {
4083 next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
4086 if ($k == 2 && $legacy_igd) {
4089 my $pciaddr = print_pci_addr
("pci.$k_name", undef, $arch, $machine_type);
4090 my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
4092 if ($q35) { # add after -readconfig pve-q35.cfg
4093 splice @$devices, 2, 0, '-device', $devstr;
4095 unshift @$devices, '-device', $devstr if $k > 0;
4100 push @$machineFlags, 'accel=tcg';
4103 push @$machineFlags, 'smm=off' if should_disable_smm
($conf, $vga);
4105 my $machine_type_min = $machine_type;
4106 if ($add_pve_version) {
4107 $machine_type_min =~ s/\+pve\d+$//;
4108 $machine_type_min .= "+pve$required_pve_version";
4110 push @$machineFlags, "type=${machine_type_min}";
4112 push @$cmd, @$devices;
4113 push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
4114 push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
4115 push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags);
4117 if (my $vmstate = $conf->{vmstate
}) {
4118 my $statepath = PVE
::Storage
::path
($storecfg, $vmstate);
4119 push @$vollist, $vmstate;
4120 push @$cmd, '-loadstate', $statepath;
4121 print "activating and using '$vmstate' as vmstate\n";
4124 if (PVE
::QemuConfig-
>is_template($conf)) {
4125 # needed to workaround base volumes being read-only
4126 push @$cmd, '-snapshot';
4130 if ($conf->{args
}) {
4131 my $aa = PVE
::Tools
::split_args
($conf->{args
});
4135 return wantarray ?
($cmd, $vollist, $spice_port) : $cmd;
4138 sub check_rng_source
{
4141 # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
4142 die "cannot create VirtIO RNG device: source file '$source' doesn't exist\n"
4145 my $rng_current = '/sys/devices/virtual/misc/hw_random/rng_current';
4146 if ($source eq '/dev/hwrng' && file_read_firstline
($rng_current) eq 'none') {
4147 # Needs to abort, otherwise QEMU crashes on first rng access. Note that rng_current cannot
4148 # be changed to 'none' manually, so once the VM is past this point, it's no longer an issue.
4149 die "Cannot start VM with passed-through RNG device: '/dev/hwrng' exists, but"
4150 ." '$rng_current' is set to 'none'. Ensure that a compatible hardware-RNG is attached"
4158 my $res = mon_cmd
($vmid, 'query-spice');
4160 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
4163 sub vm_devices_list
{
4166 my $res = mon_cmd
($vmid, 'query-pci');
4167 my $devices_to_check = [];
4169 foreach my $pcibus (@$res) {
4170 push @$devices_to_check, @{$pcibus->{devices
}},
4173 while (@$devices_to_check) {
4175 for my $d (@$devices_to_check) {
4176 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
4177 next if !$d->{'pci_bridge'};
4179 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices
}});
4180 push @$to_check, @{$d->{'pci_bridge'}->{devices
}};
4182 $devices_to_check = $to_check;
4185 my $resblock = mon_cmd
($vmid, 'query-block');
4186 foreach my $block (@$resblock) {
4187 if($block->{device
} =~ m/^drive-(\S+)/){
4192 my $resmice = mon_cmd
($vmid, 'query-mice');
4193 foreach my $mice (@$resmice) {
4194 if ($mice->{name
} eq 'QEMU HID Tablet') {
4195 $devices->{tablet
} = 1;
4200 # for usb devices there is no query-usb
4201 # but we can iterate over the entries in
4202 # qom-list path=/machine/peripheral
4203 my $resperipheral = mon_cmd
($vmid, 'qom-list', path
=> '/machine/peripheral');
4204 foreach my $per (@$resperipheral) {
4205 if ($per->{name
} =~ m/^usb\d+$/) {
4206 $devices->{$per->{name
}} = 1;
4214 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4216 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
4218 my $devices_list = vm_devices_list
($vmid);
4219 return 1 if defined($devices_list->{$deviceid});
4221 # add PCI bridge if we need it for the device
4222 qemu_add_pci_bridge
($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type);
4224 if ($deviceid eq 'tablet') {
4225 qemu_deviceadd
($vmid, print_tabletdevice_full
($conf, $arch));
4226 } elsif ($deviceid eq 'keyboard') {
4227 qemu_deviceadd
($vmid, print_keyboarddevice_full
($conf, $arch));
4228 } elsif ($deviceid =~ m/^usb(\d+)$/) {
4229 die "usb hotplug currently not reliable\n";
4230 # since we can't reliably hot unplug all added usb devices and usb
4231 # passthrough breaks live migration we disable usb hotplugging for now
4232 #qemu_deviceadd($vmid, PVE::QemuServer::USB::print_usbdevice_full($conf, $deviceid, $device));
4233 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4234 qemu_iothread_add
($vmid, $deviceid, $device);
4236 qemu_driveadd
($storecfg, $vmid, $device);
4237 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4239 qemu_deviceadd
($vmid, $devicefull);
4240 eval { qemu_deviceaddverify
($vmid, $deviceid); };
4242 eval { qemu_drivedel
($vmid, $deviceid); };
4246 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4247 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : "lsi";
4248 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4249 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ?
"virtio-scsi-pci" : $scsihw;
4251 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
4253 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread
}) {
4254 qemu_iothread_add
($vmid, $deviceid, $device);
4255 $devicefull .= ",iothread=iothread-$deviceid";
4258 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues
}) {
4259 $devicefull .= ",num_queues=$device->{queues}";
4262 qemu_deviceadd
($vmid, $devicefull);
4263 qemu_deviceaddverify
($vmid, $deviceid);
4264 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4265 qemu_findorcreatescsihw
($storecfg,$conf, $vmid, $device, $arch, $machine_type);
4266 qemu_driveadd
($storecfg, $vmid, $device);
4268 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4269 eval { qemu_deviceadd
($vmid, $devicefull); };
4271 eval { qemu_drivedel
($vmid, $deviceid); };
4275 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4276 return if !qemu_netdevadd
($vmid, $conf, $arch, $device, $deviceid);
4278 my $machine_type = PVE
::QemuServer
::Machine
::qemu_machine_pxe
($vmid, $conf);
4279 my $use_old_bios_files = undef;
4280 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
4282 my $netdevicefull = print_netdevice_full
(
4283 $vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type);
4284 qemu_deviceadd
($vmid, $netdevicefull);
4286 qemu_deviceaddverify
($vmid, $deviceid);
4287 qemu_set_link_status
($vmid, $deviceid, !$device->{link_down
});
4290 eval { qemu_netdevdel
($vmid, $deviceid); };
4294 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
4296 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4297 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
4299 qemu_deviceadd
($vmid, $devicefull);
4300 qemu_deviceaddverify
($vmid, $deviceid);
4302 die "can't hotplug device '$deviceid'\n";
4308 # fixme: this should raise exceptions on error!
4309 sub vm_deviceunplug
{
4310 my ($vmid, $conf, $deviceid) = @_;
4312 my $devices_list = vm_devices_list
($vmid);
4313 return 1 if !defined($devices_list->{$deviceid});
4315 my $bootdisks = PVE
::QemuServer
::Drive
::get_bootdisks
($conf);
4316 die "can't unplug bootdisk '$deviceid'\n" if grep {$_ eq $deviceid} @$bootdisks;
4318 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard') {
4319 qemu_devicedel
($vmid, $deviceid);
4320 } elsif ($deviceid =~ m/^usb\d+$/) {
4321 die "usb hotplug currently not reliable\n";
4322 # when unplugging usb devices this way, there may be remaining usb
4323 # controllers/hubs so we disable it for now
4324 #qemu_devicedel($vmid, $deviceid);
4325 #qemu_devicedelverify($vmid, $deviceid);
4326 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4327 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4329 qemu_devicedel
($vmid, $deviceid);
4330 qemu_devicedelverify
($vmid, $deviceid);
4331 qemu_drivedel
($vmid, $deviceid);
4332 qemu_iothread_del
($vmid, $deviceid, $device);
4333 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4334 qemu_devicedel
($vmid, $deviceid);
4335 qemu_devicedelverify
($vmid, $deviceid);
4336 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4337 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4339 qemu_devicedel
($vmid, $deviceid);
4340 qemu_devicedelverify
($vmid, $deviceid);
4341 qemu_drivedel
($vmid, $deviceid);
4342 qemu_deletescsihw
($conf, $vmid, $deviceid);
4344 qemu_iothread_del
($vmid, "virtioscsi$device->{index}", $device)
4345 if $conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single');
4346 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4347 qemu_devicedel
($vmid, $deviceid);
4348 qemu_devicedelverify
($vmid, $deviceid);
4349 qemu_netdevdel
($vmid, $deviceid);
4351 die "can't unplug device '$deviceid'\n";
4357 sub qemu_deviceadd
{
4358 my ($vmid, $devicefull) = @_;
4360 $devicefull = "driver=".$devicefull;
4361 my %options = split(/[=,]/, $devicefull);
4363 mon_cmd
($vmid, "device_add" , %options);
4366 sub qemu_devicedel
{
4367 my ($vmid, $deviceid) = @_;
4369 my $ret = mon_cmd
($vmid, "device_del", id
=> $deviceid);
4372 sub qemu_iothread_add
{
4373 my ($vmid, $deviceid, $device) = @_;
4375 if ($device->{iothread
}) {
4376 my $iothreads = vm_iothreads_list
($vmid);
4377 qemu_objectadd
($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4381 sub qemu_iothread_del
{
4382 my ($vmid, $deviceid, $device) = @_;
4384 if ($device->{iothread
}) {
4385 my $iothreads = vm_iothreads_list
($vmid);
4386 qemu_objectdel
($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4390 sub qemu_objectadd
{
4391 my ($vmid, $objectid, $qomtype) = @_;
4393 mon_cmd
($vmid, "object-add", id
=> $objectid, "qom-type" => $qomtype);
4398 sub qemu_objectdel
{
4399 my ($vmid, $objectid) = @_;
4401 mon_cmd
($vmid, "object-del", id
=> $objectid);
4407 my ($storecfg, $vmid, $device) = @_;
4409 my $kvmver = get_running_qemu_version
($vmid);
4410 my $io_uring = min_version
($kvmver, 6, 0);
4411 my $drive = print_drive_commandline_full
($storecfg, $vmid, $device, undef, $io_uring);
4412 $drive =~ s/\\/\\\\/g;
4413 my $ret = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "drive_add auto \"$drive\"");
4415 # If the command succeeds qemu prints: "OK
"
4416 return 1 if $ret =~ m/OK/s;
4418 die "adding drive failed
: $ret\n";
4422 my ($vmid, $deviceid) = @_;
4424 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-
$deviceid");
4427 return 1 if $ret eq "";
4429 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4430 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4432 die "deleting drive
$deviceid failed
: $ret\n";
4435 sub qemu_deviceaddverify {
4436 my ($vmid, $deviceid) = @_;
4438 for (my $i = 0; $i <= 5; $i++) {
4439 my $devices_list = vm_devices_list($vmid);
4440 return 1 if defined($devices_list->{$deviceid});
4444 die "error on hotplug device
'$deviceid'\n";
4448 sub qemu_devicedelverify {
4449 my ($vmid, $deviceid) = @_;
4451 # need to verify that the device is correctly removed as device_del
4452 # is async and empty return is not reliable
4454 for (my $i = 0; $i <= 5; $i++) {
4455 my $devices_list = vm_devices_list($vmid);
4456 return 1 if !defined($devices_list->{$deviceid});
4460 die "error on hot-unplugging device
'$deviceid'\n";
4463 sub qemu_findorcreatescsihw {
4464 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4466 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4468 my $scsihwid="$controller_prefix$controller";
4469 my $devices_list = vm_devices_list($vmid);
4471 if (!defined($devices_list->{$scsihwid})) {
4472 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4478 sub qemu_deletescsihw {
4479 my ($conf, $vmid, $opt) = @_;
4481 my $device = parse_drive($opt, $conf->{$opt});
4483 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4484 vm_deviceunplug($vmid, $conf, "virtioscsi
$device->{index}");
4488 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4490 my $devices_list = vm_devices_list($vmid);
4491 foreach my $opt (keys %{$devices_list}) {
4492 if (is_valid_drivename($opt)) {
4493 my $drive = parse_drive($opt, $conf->{$opt});
4494 if ($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4500 my $scsihwid="scsihw
$controller";
4502 vm_deviceunplug($vmid, $conf, $scsihwid);
4507 sub qemu_add_pci_bridge {
4508 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4514 print_pci_addr($device, $bridges, $arch, $machine_type);
4516 while (my ($k, $v) = each %$bridges) {
4519 return 1 if !defined($bridgeid) || $bridgeid < 1;
4521 my $bridge = "pci
.$bridgeid";
4522 my $devices_list = vm_devices_list($vmid);
4524 if (!defined($devices_list->{$bridge})) {
4525 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4531 sub qemu_set_link_status {
4532 my ($vmid, $device, $up) = @_;
4534 mon_cmd($vmid, "set_link
", name => $device,
4535 up => $up ? JSON::true : JSON::false);
4538 sub qemu_netdevadd {
4539 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4541 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4542 my %options = split(/[=,]/, $netdev);
4544 if (defined(my $vhost = $options{vhost})) {
4545 $options{vhost} = JSON::boolean(PVE::JSONSchema::parse_boolean($vhost));
4548 if (defined(my $queues = $options{queues})) {
4549 $options{queues} = $queues + 0;
4552 mon_cmd($vmid, "netdev_add
", %options);
4556 sub qemu_netdevdel {
4557 my ($vmid, $deviceid) = @_;
4559 mon_cmd($vmid, "netdev_del
", id => $deviceid);
4562 sub qemu_usb_hotplug {
4563 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4567 # remove the old one first
4568 vm_deviceunplug($vmid, $conf, $deviceid);
4570 # check if xhci controller is necessary and available
4571 if ($device->{usb3}) {
4573 my $devicelist = vm_devices_list($vmid);
4575 if (!$devicelist->{xhci}) {
4576 my $pciaddr = print_pci_addr("xhci
", undef, $arch, $machine_type);
4577 qemu_deviceadd($vmid, "nec-usb-xhci
,id
=xhci
$pciaddr");
4580 my $d = parse_usb_device($device->{host});
4581 $d->{usb3} = $device->{usb3};
4584 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d, $arch, $machine_type);
4587 sub qemu_cpu_hotplug {
4588 my ($vmid, $conf, $vcpus) = @_;
4590 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
4593 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4594 $sockets = $conf->{sockets} if $conf->{sockets};
4595 my $cores = $conf->{cores} || 1;
4596 my $maxcpus = $sockets * $cores;
4598 $vcpus = $maxcpus if !$vcpus;
4600 die "you can
't add more vcpus than maxcpus\n"
4601 if $vcpus > $maxcpus;
4603 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4605 if ($vcpus < $currentvcpus) {
4607 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4609 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4610 qemu_devicedel($vmid, "cpu$i");
4612 my $currentrunningvcpus = undef;
4614 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4615 last if scalar(@{$currentrunningvcpus}) == $i-1;
4616 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4620 #update conf after each succesfull cpu unplug
4621 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4622 PVE::QemuConfig->write_config($vmid, $conf);
4625 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4631 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4632 die "vcpus in running vm does not match its configuration\n"
4633 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4635 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4637 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4638 my $cpustr = print_cpu_device($conf, $i);
4639 qemu_deviceadd($vmid, $cpustr);
4642 my $currentrunningvcpus = undef;
4644 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4645 last if scalar(@{$currentrunningvcpus}) == $i;
4646 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4650 #update conf after each succesfull cpu hotplug
4651 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4652 PVE::QemuConfig->write_config($vmid, $conf);
4656 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4657 mon_cmd($vmid, "cpu-add", id => int($i));
4662 sub qemu_block_set_io_throttle {
4663 my ($vmid, $deviceid,
4664 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4665 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4666 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4667 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4669 return if !check_running($vmid) ;
4671 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4673 bps_rd => int($bps_rd),
4674 bps_wr => int($bps_wr),
4676 iops_rd => int($iops_rd),
4677 iops_wr => int($iops_wr),
4678 bps_max => int($bps_max),
4679 bps_rd_max => int($bps_rd_max),
4680 bps_wr_max => int($bps_wr_max),
4681 iops_max => int($iops_max),
4682 iops_rd_max => int($iops_rd_max),
4683 iops_wr_max => int($iops_wr_max),
4684 bps_max_length => int($bps_max_length),
4685 bps_rd_max_length => int($bps_rd_max_length),
4686 bps_wr_max_length => int($bps_wr_max_length),
4687 iops_max_length => int($iops_max_length),
4688 iops_rd_max_length => int($iops_rd_max_length),
4689 iops_wr_max_length => int($iops_wr_max_length),
4694 sub qemu_block_resize {
4695 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4697 my $running = check_running($vmid);
4699 $size = 0 if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4701 return if !$running;
4703 my $padding = (1024 - $size % 1024) % 1024;
4704 $size = $size + $padding;
4709 device => $deviceid,
4715 sub qemu_volume_snapshot {
4716 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4718 my $running = check_running($vmid);
4720 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4721 mon_cmd($vmid, 'blockdev-snapshot-internal-sync
', device => $deviceid, name => $snap);
4723 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4727 sub qemu_volume_snapshot_delete {
4728 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4730 my $running = check_running($vmid);
4735 my $conf = PVE::QemuConfig->load_config($vmid);
4736 PVE::QemuConfig->foreach_volume($conf, sub {
4737 my ($ds, $drive) = @_;
4738 $running = 1 if $drive->{file} eq $volid;
4742 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4743 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync
', device => $deviceid, name => $snap);
4745 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4749 sub set_migration_caps {
4750 my ($vmid, $savevm) = @_;
4752 my $qemu_support = eval { mon_cmd($vmid, "query-proxmox-support") };
4754 my $bitmap_prop = $savevm ? 'pbs-dirty-bitmap-savevm
' : 'pbs-dirty-bitmap-migration
';
4755 my $dirty_bitmaps = $qemu_support->{$bitmap_prop} ? 1 : 0;
4760 "auto-converge" => 1,
4762 "x-rdma-pin-all" => 0,
4765 "dirty-bitmaps" => $dirty_bitmaps,
4768 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4770 for my $supported_capability (@$supported_capabilities) {
4772 capability => $supported_capability->{capability},
4773 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4777 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4781 my ($conf, $func, @param) = @_;
4785 my $test_volid = sub {
4786 my ($key, $drive, $snapname) = @_;
4788 my $volid = $drive->{file};
4791 $volhash->{$volid}->{cdrom} //= 1;
4792 $volhash->{$volid}->{cdrom} = 0 if !drive_is_cdrom($drive);
4794 my $replicate = $drive->{replicate} // 1;
4795 $volhash->{$volid}->{replicate} //= 0;
4796 $volhash->{$volid}->{replicate} = 1 if $replicate;
4798 $volhash->{$volid}->{shared} //= 0;
4799 $volhash->{$volid}->{shared} = 1 if $drive->{shared};
4801 $volhash->{$volid}->{referenced_in_config} //= 0;
4802 $volhash->{$volid}->{referenced_in_config} = 1 if !defined($snapname);
4804 $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
4805 if defined($snapname);
4807 my $size = $drive->{size};
4808 $volhash->{$volid}->{size} //= $size if $size;
4810 $volhash->{$volid}->{is_vmstate} //= 0;
4811 $volhash->{$volid}->{is_vmstate} = 1 if $key eq 'vmstate
';
4813 $volhash->{$volid}->{is_tpmstate} //= 0;
4814 $volhash->{$volid}->{is_tpmstate} = 1 if $key eq 'tpmstate0
';
4816 $volhash->{$volid}->{is_unused} //= 0;
4817 $volhash->{$volid}->{is_unused} = 1 if $key =~ /^unused\d+$/;
4819 $volhash->{$volid}->{drivename} = $key if is_valid_drivename($key);
4822 my $include_opts = {
4823 extra_keys => ['vmstate
'],
4824 include_unused => 1,
4827 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $test_volid);
4828 foreach my $snapname (keys %{$conf->{snapshots}}) {
4829 my $snap = $conf->{snapshots}->{$snapname};
4830 PVE::QemuConfig->foreach_volume_full($snap, $include_opts, $test_volid, $snapname);
4833 foreach my $volid (keys %$volhash) {
4834 &$func($volid, $volhash->{$volid}, @param);
4838 my $fast_plug_option = {
4846 'vmstatestorage
' => 1,
4851 for my $opt (keys %$confdesc_cloudinit) {
4852 $fast_plug_option->{$opt} = 1;
4855 # hotplug changes in [PENDING]
4856 # $selection hash can be used to only apply specified options, for
4857 # example: { cores => 1 } (only apply changed 'cores
')
4858 # $errors ref is used to return error messages
4859 sub vmconfig_hotplug_pending {
4860 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4862 my $defaults = load_defaults();
4863 my $arch = get_vm_arch($conf);
4864 my $machine_type = get_vm_machine($conf, undef, $arch);
4866 # commit values which do not have any impact on running VM first
4867 # Note: those option cannot raise errors, we we do not care about
4868 # $selection and always apply them.
4870 my $add_error = sub {
4871 my ($opt, $msg) = @_;
4872 $errors->{$opt} = "hotplug problem - $msg";
4876 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4877 if ($fast_plug_option->{$opt}) {
4878 $conf->{$opt} = $conf->{pending}->{$opt};
4879 delete $conf->{pending}->{$opt};
4885 PVE::QemuConfig->write_config($vmid, $conf);
4888 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
4890 my $cgroup = PVE::QemuServer::CGroup->new($vmid);
4891 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4892 foreach my $opt (sort keys %$pending_delete_hash) {
4893 next if $selection && !$selection->{$opt};
4894 my $force = $pending_delete_hash->{$opt}->{force};
4896 if ($opt eq 'hotplug
') {
4897 die "skip\n" if ($conf->{hotplug} =~ /memory/);
4898 } elsif ($opt eq 'tablet
') {
4899 die "skip\n" if !$hotplug_features->{usb};
4900 if ($defaults->{tablet}) {
4901 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4902 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4903 if $arch eq 'aarch64
';
4905 vm_deviceunplug($vmid, $conf, 'tablet
');
4906 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4908 } elsif ($opt =~ m/^usb\d+/) {
4910 # since we cannot reliably hot unplug usb devices we are disabling it
4911 #die "skip\n" if !$hotplug_features->{usb} || $conf->{$opt} =~ m/spice/i;
4912 #vm_deviceunplug($vmid, $conf, $opt);
4913 } elsif ($opt eq 'vcpus
') {
4914 die "skip\n" if !$hotplug_features->{cpu};
4915 qemu_cpu_hotplug($vmid, $conf, undef);
4916 } elsif ($opt eq 'balloon
') {
4917 # enable balloon device is not hotpluggable
4918 die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
4919 # here we reset the ballooning value to memory
4920 my $balloon = $conf->{memory} || $defaults->{memory};
4921 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4922 } elsif ($fast_plug_option->{$opt}) {
4924 } elsif ($opt =~ m/^net(\d+)$/) {
4925 die "skip\n" if !$hotplug_features->{network};
4926 vm_deviceunplug($vmid, $conf, $opt);
4927 } elsif (is_valid_drivename($opt)) {
4928 die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
4929 vm_deviceunplug($vmid, $conf, $opt);
4930 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4931 } elsif ($opt =~ m/^memory$/) {
4932 die "skip\n" if !$hotplug_features->{memory};
4933 PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
4934 } elsif ($opt eq 'cpuunits
') {
4935 $cgroup->change_cpu_shares(undef);
4936 } elsif ($opt eq 'cpulimit
') {
4937 $cgroup->change_cpu_quota(undef, undef); # reset, cgroup module can better decide values
4943 &$add_error($opt, $err) if $err ne "skip\n";
4945 delete $conf->{$opt};
4946 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4950 foreach my $opt (keys %{$conf->{pending}}) {
4951 next if $selection && !$selection->{$opt};
4952 my $value = $conf->{pending}->{$opt};
4954 if ($opt eq 'hotplug
') {
4955 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/);
4956 } elsif ($opt eq 'tablet
') {
4957 die "skip\n" if !$hotplug_features->{usb};
4959 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4960 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4961 if $arch eq 'aarch64
';
4962 } elsif ($value == 0) {
4963 vm_deviceunplug($vmid, $conf, 'tablet
');
4964 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4966 } elsif ($opt =~ m/^usb\d+$/) {
4968 # since we cannot reliably hot unplug usb devices we disable it for now
4969 #die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
4970 #my $d = eval { parse_property_string($usbdesc->{format}, $value) };
4971 #die "skip\n" if !$d;
4972 #qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d, $arch, $machine_type);
4973 } elsif ($opt eq 'vcpus
') {
4974 die "skip\n" if !$hotplug_features->{cpu};
4975 qemu_cpu_hotplug($vmid, $conf, $value);
4976 } elsif ($opt eq 'balloon
') {
4977 # enable/disable balloning device is not hotpluggable
4978 my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon});
4979 my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon});
4980 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
4982 # allow manual ballooning if shares is set to zero
4983 if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
4984 my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
4985 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4987 } elsif ($opt =~ m/^net(\d+)$/) {
4988 # some changes can be done without hotplug
4989 vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
4990 $vmid, $opt, $value, $arch, $machine_type);
4991 } elsif (is_valid_drivename($opt)) {
4992 die "skip\n" if $opt eq 'efidisk0
' || $opt eq 'tpmstate0
';
4993 # some changes can be done without hotplug
4994 my $drive = parse_drive($opt, $value);
4995 if (drive_is_cloudinit($drive)) {
4996 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
4998 vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
4999 $vmid, $opt, $value, $arch, $machine_type);
5000 } elsif ($opt =~ m/^memory$/) { #dimms
5001 die "skip\n" if !$hotplug_features->{memory};
5002 $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
5003 } elsif ($opt eq 'cpuunits
') {
5004 my $new_cpuunits = PVE::CGroup::clamp_cpu_shares($conf->{pending}->{$opt}); #clamp
5005 $cgroup->change_cpu_shares($new_cpuunits);
5006 } elsif ($opt eq 'cpulimit
') {
5007 my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
5008 $cgroup->change_cpu_quota($cpulimit, 100000);
5009 } elsif ($opt eq 'agent
') {
5010 vmconfig_update_agent($conf, $opt, $value);
5012 die "skip\n"; # skip non-hot-pluggable options
5016 &$add_error($opt, $err) if $err ne "skip\n";
5018 $conf->{$opt} = $value;
5019 delete $conf->{pending}->{$opt};
5022 PVE::QemuConfig->write_config($vmid, $conf);
5024 if($hotplug_features->{cloudinit}) {
5025 my $pending = PVE::QemuServer::Cloudinit::get_pending_config($conf, $vmid);
5026 my $regenerate = undef;
5027 for my $item (@$pending) {
5028 $regenerate = 1 if defined($item->{delete}) or defined($item->{pending});
5030 PVE::QemuServer::vmconfig_update_cloudinit_drive($storecfg, $conf, $vmid) if $regenerate;
5034 sub try_deallocate_drive {
5035 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
5037 if (($force || $key =~ /^unused/) && !drive_is_cdrom($drive, 1)) {
5038 my $volid = $drive->{file};
5039 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
5040 my $sid = PVE::Storage::parse_volume_id($volid);
5041 $rpcenv->check($authuser, "/storage/$sid", ['Datastore
.AllocateSpace
']);
5043 # check if the disk is really unused
5044 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
5045 if PVE::QemuServer::Drive::is_volume_in_use($storecfg, $conf, $key, $volid);
5046 PVE::Storage::vdisk_free($storecfg, $volid);
5049 # If vm is not owner of this disk remove from config
5057 sub vmconfig_delete_or_detach_drive {
5058 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
5060 my $drive = parse_drive($opt, $conf->{$opt});
5062 my $rpcenv = PVE::RPCEnvironment::get();
5063 my $authuser = $rpcenv->get_user();
5066 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM
.Config
.Disk
']);
5067 try_deallocate_drive($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
5069 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $drive);
5075 sub vmconfig_apply_pending {
5076 my ($vmid, $conf, $storecfg, $errors) = @_;
5078 return if !scalar(keys %{$conf->{pending}});
5080 my $add_apply_error = sub {
5081 my ($opt, $msg) = @_;
5082 my $err_msg = "unable to apply pending change $opt : $msg";
5083 $errors->{$opt} = $err_msg;
5089 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
5090 foreach my $opt (sort keys %$pending_delete_hash) {
5091 my $force = $pending_delete_hash->{$opt}->{force};
5093 if ($opt =~ m/^unused/) {
5094 die "internal error";
5095 } elsif (defined($conf->{$opt}) && is_valid_drivename($opt)) {
5096 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
5100 $add_apply_error->($opt, $err);
5102 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
5103 delete $conf->{$opt};
5107 PVE::QemuConfig->cleanup_pending($conf);
5109 my $generate_cloudnit = undef;
5111 foreach my $opt (keys %{$conf->{pending}}) { # add/change
5112 next if $opt eq 'delete'; # just to be sure
5114 if (defined($conf->{$opt}) && is_valid_drivename($opt)) {
5115 vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
5119 $add_apply_error->($opt, $err);
5122 if (is_valid_drivename($opt)) {
5123 my $drive = parse_drive($opt, $conf->{pending}->{$opt});
5124 $generate_cloudnit = 1 if drive_is_cloudinit($drive);
5127 $conf->{$opt} = delete $conf->{pending}->{$opt};
5131 # write all changes at once to avoid unnecessary i/o
5132 PVE::QemuConfig->write_config($vmid, $conf);
5133 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid) if $generate_cloudnit;
5136 sub vmconfig_update_net {
5137 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5139 my $newnet = parse_net($value);
5141 if ($conf->{$opt}) {
5142 my $oldnet = parse_net($conf->{$opt});
5144 if (safe_string_ne($oldnet->{model}, $newnet->{model}) ||
5145 safe_string_ne($oldnet->{macaddr}, $newnet->{macaddr}) ||
5146 safe_num_ne($oldnet->{queues}, $newnet->{queues}) ||
5147 !($newnet->{bridge} && $oldnet->{bridge})) { # bridge/nat mode change
5149 # for non online change, we try to hot-unplug
5150 die "skip\n" if !$hotplug;
5151 vm_deviceunplug($vmid, $conf, $opt);
5154 die "internal error" if $opt !~ m/net(\d+)/;
5155 my $iface = "tap${vmid}i$1";
5157 if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
5158 safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
5159 safe_string_ne($oldnet->{trunks}, $newnet->{trunks}) ||
5160 safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
5161 PVE::Network::tap_unplug($iface);
5164 PVE::Network::SDN::Zones::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
5166 PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
5168 } elsif (safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
5169 # Rate can be applied on its own but any change above needs to
5170 # include the rate in tap_plug since OVS resets everything.
5171 PVE::Network::tap_rate_limit($iface, $newnet->{rate});
5174 if (safe_string_ne($oldnet->{link_down}, $newnet->{link_down})) {
5175 qemu_set_link_status($vmid, $opt, !$newnet->{link_down});
5183 vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
5189 sub vmconfig_update_agent {
5190 my ($conf, $opt, $value) = @_;
5192 die "skip\n" if !$conf->{$opt};
5194 my $hotplug_options = { fstrim_cloned_disks => 1 };
5196 my $old_agent = parse_guest_agent($conf);
5197 my $agent = parse_guest_agent({$opt => $value});
5199 for my $option (keys %$agent) { # added/changed options
5200 next if defined($hotplug_options->{$option});
5201 die "skip\n" if safe_string_ne($agent->{$option}, $old_agent->{$option});
5204 for my $option (keys %$old_agent) { # removed options
5205 next if defined($hotplug_options->{$option});
5206 die "skip\n" if safe_string_ne($old_agent->{$option}, $agent->{$option});
5209 return; # either no actual change (e.g., format string reordered) or just hotpluggable changes
5212 sub vmconfig_update_disk {
5213 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5215 my $drive = parse_drive($opt, $value);
5217 if ($conf->{$opt} && (my $old_drive = parse_drive($opt, $conf->{$opt}))) {
5218 my $media = $drive->{media} || 'disk
';
5219 my $oldmedia = $old_drive->{media} || 'disk
';
5220 die "unable to change media type\n" if $media ne $oldmedia;
5222 if (!drive_is_cdrom($old_drive)) {
5224 if ($drive->{file} ne $old_drive->{file}) {
5226 die "skip\n" if !$hotplug;
5228 # unplug and register as unused
5229 vm_deviceunplug($vmid, $conf, $opt);
5230 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive)
5233 # update existing disk
5235 # skip non hotpluggable value
5236 if (safe_string_ne($drive->{discard}, $old_drive->{discard}) ||
5237 safe_string_ne($drive->{iothread}, $old_drive->{iothread}) ||
5238 safe_string_ne($drive->{queues}, $old_drive->{queues}) ||
5239 safe_string_ne($drive->{cache}, $old_drive->{cache}) ||
5240 safe_string_ne($drive->{ssd}, $old_drive->{ssd})) {
5245 if (safe_num_ne($drive->{mbps}, $old_drive->{mbps}) ||
5246 safe_num_ne($drive->{mbps_rd}, $old_drive->{mbps_rd}) ||
5247 safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) ||
5248 safe_num_ne($drive->{iops}, $old_drive->{iops}) ||
5249 safe_num_ne($drive->{iops_rd}, $old_drive->{iops_rd}) ||
5250 safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr}) ||
5251 safe_num_ne($drive->{mbps_max}, $old_drive->{mbps_max}) ||
5252 safe_num_ne($drive->{mbps_rd_max}, $old_drive->{mbps_rd_max}) ||
5253 safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) ||
5254 safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) ||
5255 safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) ||
5256 safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max}) ||
5257 safe_num_ne($drive->{bps_max_length}, $old_drive->{bps_max_length}) ||
5258 safe_num_ne($drive->{bps_rd_max_length}, $old_drive->{bps_rd_max_length}) ||
5259 safe_num_ne($drive->{bps_wr_max_length}, $old_drive->{bps_wr_max_length}) ||
5260 safe_num_ne($drive->{iops_max_length}, $old_drive->{iops_max_length}) ||
5261 safe_num_ne($drive->{iops_rd_max_length}, $old_drive->{iops_rd_max_length}) ||
5262 safe_num_ne($drive->{iops_wr_max_length}, $old_drive->{iops_wr_max_length})) {
5264 qemu_block_set_io_throttle(
5266 ($drive->{mbps} || 0)*1024*1024,
5267 ($drive->{mbps_rd} || 0)*1024*1024,
5268 ($drive->{mbps_wr} || 0)*1024*1024,
5269 $drive->{iops} || 0,
5270 $drive->{iops_rd} || 0,
5271 $drive->{iops_wr} || 0,
5272 ($drive->{mbps_max} || 0)*1024*1024,
5273 ($drive->{mbps_rd_max} || 0)*1024*1024,
5274 ($drive->{mbps_wr_max} || 0)*1024*1024,
5275 $drive->{iops_max} || 0,
5276 $drive->{iops_rd_max} || 0,
5277 $drive->{iops_wr_max} || 0,
5278 $drive->{bps_max_length} || 1,
5279 $drive->{bps_rd_max_length} || 1,
5280 $drive->{bps_wr_max_length} || 1,
5281 $drive->{iops_max_length} || 1,
5282 $drive->{iops_rd_max_length} || 1,
5283 $drive->{iops_wr_max_length} || 1,
5293 if ($drive->{file} eq 'none
') {
5294 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
5295 if (drive_is_cloudinit($old_drive)) {
5296 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive);
5299 my $path = get_iso_path($storecfg, $vmid, $drive->{file});
5301 # force eject if locked
5302 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
5305 mon_cmd($vmid, "blockdev-change-medium",
5306 id => "$opt", filename => "$path");
5314 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
5316 PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
5317 vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
5320 sub vmconfig_update_cloudinit_drive {
5321 my ($storecfg, $conf, $vmid) = @_;
5323 my $cloudinit_ds = undef;
5324 my $cloudinit_drive = undef;
5326 PVE::QemuConfig->foreach_volume($conf, sub {
5327 my ($ds, $drive) = @_;
5328 if (PVE::QemuServer::drive_is_cloudinit($drive)) {
5329 $cloudinit_ds = $ds;
5330 $cloudinit_drive = $drive;
5334 return if !$cloudinit_drive;
5336 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
5337 my $running = PVE::QemuServer::check_running($vmid);
5340 my $path = PVE::Storage::path($storecfg, $cloudinit_drive->{file});
5342 mon_cmd($vmid, "eject", force => JSON::true, id => "$cloudinit_ds");
5343 mon_cmd($vmid, "blockdev-change-medium", id => "$cloudinit_ds", filename => "$path");
5348 # called in locked context by incoming migration
5349 sub vm_migrate_get_nbd_disks {
5350 my ($storecfg, $conf, $replicated_volumes) = @_;
5352 my $local_volumes = {};
5353 PVE::QemuConfig->foreach_volume($conf, sub {
5354 my ($ds, $drive) = @_;
5356 return if drive_is_cdrom($drive);
5357 return if $ds eq 'tpmstate0
';
5359 my $volid = $drive->{file};
5363 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
5365 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5366 return if $scfg->{shared};
5368 # replicated disks re-use existing state via bitmap
5369 my $use_existing = $replicated_volumes->{$volid} ? 1 : 0;
5370 $local_volumes->{$ds} = [$volid, $storeid, $volname, $drive, $use_existing];
5372 return $local_volumes;
5375 # called in locked context by incoming migration
5376 sub vm_migrate_alloc_nbd_disks {
5377 my ($storecfg, $vmid, $source_volumes, $storagemap) = @_;
5380 foreach my $opt (sort keys %$source_volumes) {
5381 my ($volid, $storeid, $volname, $drive, $use_existing, $format) = @{$source_volumes->{$opt}};
5383 if ($use_existing) {
5384 $nbd->{$opt}->{drivestr} = print_drive($drive);
5385 $nbd->{$opt}->{volid} = $volid;
5386 $nbd->{$opt}->{replicated} = 1;
5390 # storage mapping + volname = regular migration
5391 # storage mapping + format = remote migration
5392 # order of precedence, filtered by whether storage supports it:
5393 # 1. explicit requested format
5394 # 2. format of current volume
5395 # 3. default format of storage
5396 if (!$storagemap->{identity}) {
5397 $storeid = PVE::JSONSchema::map_id($storagemap, $storeid);
5398 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
5399 if (!$format || !grep { $format eq $_ } @$validFormats) {
5401 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5402 my $fileFormat = qemu_img_format($scfg, $volname);
5403 $format = $fileFormat
5404 if grep { $fileFormat eq $_ } @$validFormats;
5406 $format //= $defFormat;
5409 # can't happen
for remote migration
, so
$volname is always
defined
5410 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
5411 $format = qemu_img_format
($scfg, $volname);
5414 my $size = $drive->{size
} / 1024;
5415 my $newvolid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $format, undef, $size);
5416 my $newdrive = $drive;
5417 $newdrive->{format
} = $format;
5418 $newdrive->{file
} = $newvolid;
5419 my $drivestr = print_drive
($newdrive);
5420 $nbd->{$opt}->{drivestr
} = $drivestr;
5421 $nbd->{$opt}->{volid
} = $newvolid;
5427 # see vm_start_nolock for parameters, additionally:
5429 # storagemap = parsed storage map for allocating NBD disks
5431 my ($storecfg, $vmid, $params, $migrate_opts) = @_;
5433 return PVE
::QemuConfig-
>lock_config($vmid, sub {
5434 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migrate_opts->{migratedfrom
});
5436 die "you can't start a vm if it's a template\n"
5437 if !$params->{skiptemplate
} && PVE
::QemuConfig-
>is_template($conf);
5439 my $has_suspended_lock = PVE
::QemuConfig-
>has_lock($conf, 'suspended');
5440 my $has_backup_lock = PVE
::QemuConfig-
>has_lock($conf, 'backup');
5442 my $running = check_running
($vmid, undef, $migrate_opts->{migratedfrom
});
5444 if ($has_backup_lock && $running) {
5445 # a backup is currently running, attempt to start the guest in the
5446 # existing QEMU instance
5447 return vm_resume
($vmid);
5450 PVE
::QemuConfig-
>check_lock($conf)
5451 if !($params->{skiplock
} || $has_suspended_lock);
5453 $params->{resume
} = $has_suspended_lock || defined($conf->{vmstate
});
5455 die "VM $vmid already running\n" if $running;
5457 if (my $storagemap = $migrate_opts->{storagemap
}) {
5458 my $replicated = $migrate_opts->{replicated_volumes
};
5459 my $disks = vm_migrate_get_nbd_disks
($storecfg, $conf, $replicated);
5460 $migrate_opts->{nbd
} = vm_migrate_alloc_nbd_disks
($storecfg, $vmid, $disks, $storagemap);
5462 foreach my $opt (keys %{$migrate_opts->{nbd
}}) {
5463 $conf->{$opt} = $migrate_opts->{nbd
}->{$opt}->{drivestr
};
5467 return vm_start_nolock
($storecfg, $vmid, $conf, $params, $migrate_opts);
5473 # statefile => 'tcp', 'unix' for migration or path/volid for RAM state
5474 # skiplock => 0/1, skip checking for config lock
5475 # skiptemplate => 0/1, skip checking whether VM is template
5476 # forcemachine => to force Qemu machine (rollback/migration)
5477 # forcecpu => a QEMU '-cpu' argument string to override get_cpu_options
5478 # timeout => in seconds
5479 # paused => start VM in paused state (backup)
5480 # resume => resume from hibernation
5491 # nbd => volumes for NBD exports (vm_migrate_alloc_nbd_disks)
5492 # migratedfrom => source node
5493 # spice_ticket => used for spice migration, passed via tunnel/stdin
5494 # network => CIDR of migration network
5495 # type => secure/insecure - tunnel over encrypted connection or plain-text
5496 # nbd_proto_version => int, 0 for TCP, 1 for UNIX
5497 # replicated_volumes => which volids should be re-used with bitmaps for nbd migration
5498 # offline_volumes => new volids of offline migrated disks like tpmstate and cloudinit, not yet
5499 # contained in config
5500 sub vm_start_nolock
{
5501 my ($storecfg, $vmid, $conf, $params, $migrate_opts) = @_;
5503 my $statefile = $params->{statefile
};
5504 my $resume = $params->{resume
};
5506 my $migratedfrom = $migrate_opts->{migratedfrom
};
5507 my $migration_type = $migrate_opts->{type
};
5511 # clean up leftover reboot request files
5512 eval { clear_reboot_request
($vmid); };
5515 if (!$statefile && scalar(keys %{$conf->{pending
}})) {
5516 vmconfig_apply_pending
($vmid, $conf, $storecfg);
5517 $conf = PVE
::QemuConfig-
>load_config($vmid); # update/reload
5520 # don't regenerate the ISO if the VM is started as part of a live migration
5521 # this way we can reuse the old ISO with the correct config
5522 PVE
::QemuServer
::Cloudinit
::generate_cloudinitconfig
($conf, $vmid) if !$migratedfrom;
5524 # override offline migrated volumes, conf is out of date still
5525 if (my $offline_volumes = $migrate_opts->{offline_volumes
}) {
5526 for my $key (sort keys $offline_volumes->%*) {
5527 my $parsed = parse_drive
($key, $conf->{$key});
5528 $parsed->{file
} = $offline_volumes->{$key};
5529 $conf->{$key} = print_drive
($parsed);
5533 my $defaults = load_defaults
();
5535 # set environment variable useful inside network script
5536 $ENV{PVE_MIGRATED_FROM
} = $migratedfrom if $migratedfrom;
5538 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-start', 1);
5540 my $forcemachine = $params->{forcemachine
};
5541 my $forcecpu = $params->{forcecpu
};
5543 # enforce machine and CPU type on suspended vm to ensure HW compatibility
5544 $forcemachine = $conf->{runningmachine
};
5545 $forcecpu = $conf->{runningcpu
};
5546 print "Resuming suspended VM\n";
5549 my ($cmd, $vollist, $spice_port) = config_to_command
($storecfg, $vmid,
5550 $conf, $defaults, $forcemachine, $forcecpu, $params->{'pbs-backing'});
5553 my $get_migration_ip = sub {
5554 my ($nodename) = @_;
5556 return $migration_ip if defined($migration_ip);
5558 my $cidr = $migrate_opts->{network
};
5560 if (!defined($cidr)) {
5561 my $dc_conf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5562 $cidr = $dc_conf->{migration
}->{network
};
5565 if (defined($cidr)) {
5566 my $ips = PVE
::Network
::get_local_ip_from_cidr
($cidr);
5568 die "could not get IP: no address configured on local " .
5569 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5571 die "could not get IP: multiple addresses configured on local " .
5572 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5574 $migration_ip = @$ips[0];
5577 $migration_ip = PVE
::Cluster
::remote_node_ip
($nodename, 1)
5578 if !defined($migration_ip);
5580 return $migration_ip;
5585 if ($statefile eq 'tcp') {
5586 my $localip = "localhost";
5587 my $datacenterconf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5588 my $nodename = nodename
();
5590 if (!defined($migration_type)) {
5591 if (defined($datacenterconf->{migration
}->{type
})) {
5592 $migration_type = $datacenterconf->{migration
}->{type
};
5594 $migration_type = 'secure';
5598 if ($migration_type eq 'insecure') {
5599 $localip = $get_migration_ip->($nodename);
5600 $localip = "[$localip]" if Net
::IP
::ip_is_ipv6
($localip);
5603 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
5604 my $migrate_port = PVE
::Tools
::next_migrate_port
($pfamily);
5605 $migrate_uri = "tcp:${localip}:${migrate_port}";
5606 push @$cmd, '-incoming', $migrate_uri;
5609 } elsif ($statefile eq 'unix') {
5610 # should be default for secure migrations as a ssh TCP forward
5611 # tunnel is not deterministic reliable ready and fails regurarly
5612 # to set up in time, so use UNIX socket forwards
5613 my $socket_addr = "/run/qemu-server/$vmid.migrate";
5614 unlink $socket_addr;
5616 $migrate_uri = "unix:$socket_addr";
5618 push @$cmd, '-incoming', $migrate_uri;
5621 } elsif (-e
$statefile) {
5622 push @$cmd, '-loadstate', $statefile;
5624 my $statepath = PVE
::Storage
::path
($storecfg, $statefile);
5625 push @$vollist, $statefile;
5626 push @$cmd, '-loadstate', $statepath;
5628 } elsif ($params->{paused
}) {
5632 my $start_timeout = $params->{timeout
} // config_aware_timeout
($conf, $resume);
5634 my $pci_devices = {}; # host pci devices
5635 for (my $i = 0; $i < $PVE::QemuServer
::PCI
::MAX_HOSTPCI_DEVICES
; $i++) {
5636 my $dev = $conf->{"hostpci$i"} or next;
5637 $pci_devices->{$i} = parse_hostpci
($dev);
5640 # do not reserve pciid for mediated devices, sysfs will error out for duplicate assignment
5641 my $real_pci_devices = [ grep { !(defined($_->{mdev
}) && scalar($_->{pciid
}->@*) == 1) } values $pci_devices->%* ];
5643 # map to a flat list of pci ids
5644 my $pci_id_list = [ map { $_->{id
} } map { $_->{pciid
}->@* } $real_pci_devices->@* ];
5646 # reserve all PCI IDs before actually doing anything with them
5647 PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_id_list, $vmid, $start_timeout);
5651 for my $id (sort keys %$pci_devices) {
5652 my $d = $pci_devices->{$id};
5653 for my $dev ($d->{pciid
}->@*) {
5654 my $info = PVE
::QemuServer
::PCI
::prepare_pci_device
($vmid, $dev->{id
}, $id, $d->{mdev
});
5656 # nvidia grid needs the uuid of the mdev as qemu parameter
5657 if ($d->{mdev
} && !defined($uuid) && $info->{vendor
} eq '10de') {
5658 $uuid = PVE
::QemuServer
::PCI
::generate_mdev_uuid
($vmid, $id);
5662 push @$cmd, '-uuid', $uuid if defined($uuid);
5665 eval { cleanup_pci_devices
($vmid, $conf) };
5670 PVE
::Storage
::activate_volumes
($storecfg, $vollist);
5673 run_command
(['/bin/systemctl', 'stop', "$vmid.scope"], outfunc
=> sub{}, errfunc
=> sub{});
5675 # Issues with the above 'stop' not being fully completed are extremely rare, a very low
5676 # timeout should be more than enough here...
5677 PVE
::Systemd
::wait_for_unit_removed
("$vmid.scope", 20);
5679 my $cpuunits = PVE
::CGroup
::clamp_cpu_shares
($conf->{cpuunits
});
5682 timeout
=> $statefile ?
undef : $start_timeout,
5687 # when migrating, prefix QEMU output so other side can pick up any
5688 # errors that might occur and show the user
5689 if ($migratedfrom) {
5690 $run_params{quiet
} = 1;
5691 $run_params{logfunc
} = sub { print "QEMU: $_[0]\n" };
5694 my %systemd_properties = (
5695 Slice
=> 'qemu.slice',
5696 KillMode
=> 'process',
5698 TimeoutStopUSec
=> ULONG_MAX
, # infinity
5701 if (PVE
::CGroup
::cgroup_mode
() == 2) {
5702 $systemd_properties{CPUWeight
} = $cpuunits;
5704 $systemd_properties{CPUShares
} = $cpuunits;
5707 if (my $cpulimit = $conf->{cpulimit
}) {
5708 $systemd_properties{CPUQuota
} = int($cpulimit * 100);
5710 $systemd_properties{timeout
} = 10 if $statefile; # setting up the scope shoul be quick
5712 my $run_qemu = sub {
5713 PVE
::Tools
::run_fork
sub {
5714 PVE
::Systemd
::enter_systemd_scope
($vmid, "Proxmox VE VM $vmid", %systemd_properties);
5717 if (my $tpm = $conf->{tpmstate0
}) {
5718 # start the TPM emulator so QEMU can connect on start
5719 $tpmpid = start_swtpm
($storecfg, $vmid, $tpm, $migratedfrom);
5722 my $exitcode = run_command
($cmd, %run_params);
5725 warn "stopping swtpm instance (pid $tpmpid) due to QEMU startup error\n";
5726 kill 'TERM', $tpmpid;
5728 die "QEMU exited with code $exitcode\n";
5733 if ($conf->{hugepages
}) {
5736 my $hugepages_topology = PVE
::QemuServer
::Memory
::hugepages_topology
($conf);
5737 my $hugepages_host_topology = PVE
::QemuServer
::Memory
::hugepages_host_topology
();
5739 PVE
::QemuServer
::Memory
::hugepages_mount
();
5740 PVE
::QemuServer
::Memory
::hugepages_allocate
($hugepages_topology, $hugepages_host_topology);
5742 eval { $run_qemu->() };
5744 PVE
::QemuServer
::Memory
::hugepages_reset
($hugepages_host_topology)
5745 if !$conf->{keephugepages
};
5749 PVE
::QemuServer
::Memory
::hugepages_pre_deallocate
($hugepages_topology)
5750 if !$conf->{keephugepages
};
5752 eval { PVE
::QemuServer
::Memory
::hugepages_update_locked
($code); };
5755 eval { $run_qemu->() };
5759 # deactivate volumes if start fails
5760 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
5762 eval { cleanup_pci_devices
($vmid, $conf) };
5765 die "start failed: $err";
5768 # re-reserve all PCI IDs now that we can know the actual VM PID
5769 my $pid = PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
5770 eval { PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_id_list, $vmid, undef, $pid) };
5773 print "migration listens on $migrate_uri\n" if $migrate_uri;
5774 $res->{migrate_uri
} = $migrate_uri;
5776 if ($statefile && $statefile ne 'tcp' && $statefile ne 'unix') {
5777 eval { mon_cmd
($vmid, "cont"); };
5781 #start nbd server for storage migration
5782 if (my $nbd = $migrate_opts->{nbd
}) {
5783 my $nbd_protocol_version = $migrate_opts->{nbd_proto_version
} // 0;
5785 my $migrate_storage_uri;
5786 # nbd_protocol_version > 0 for unix socket support
5787 if ($nbd_protocol_version > 0 && $migration_type eq 'secure') {
5788 my $socket_path = "/run/qemu-server/$vmid\_nbd.migrate";
5789 mon_cmd
($vmid, "nbd-server-start", addr
=> { type
=> 'unix', data
=> { path
=> $socket_path } } );
5790 $migrate_storage_uri = "nbd:unix:$socket_path";
5792 my $nodename = nodename
();
5793 my $localip = $get_migration_ip->($nodename);
5794 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
5795 my $storage_migrate_port = PVE
::Tools
::next_migrate_port
($pfamily);
5797 mon_cmd
($vmid, "nbd-server-start", addr
=> {
5800 host
=> "${localip}",
5801 port
=> "${storage_migrate_port}",
5804 $localip = "[$localip]" if Net
::IP
::ip_is_ipv6
($localip);
5805 $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}";
5808 $res->{migrate_storage_uri
} = $migrate_storage_uri;
5810 foreach my $opt (sort keys %$nbd) {
5811 my $drivestr = $nbd->{$opt}->{drivestr
};
5812 my $volid = $nbd->{$opt}->{volid
};
5813 mon_cmd
($vmid, "nbd-server-add", device
=> "drive-$opt", writable
=> JSON
::true
);
5814 my $nbd_uri = "$migrate_storage_uri:exportname=drive-$opt";
5815 print "storage migration listens on $nbd_uri volume:$drivestr\n";
5816 print "re-using replicated volume: $opt - $volid\n"
5817 if $nbd->{$opt}->{replicated
};
5819 $res->{drives
}->{$opt} = $nbd->{$opt};
5820 $res->{drives
}->{$opt}->{nbd_uri
} = $nbd_uri;
5824 if ($migratedfrom) {
5826 set_migration_caps
($vmid);
5831 print "spice listens on port $spice_port\n";
5832 $res->{spice_port
} = $spice_port;
5833 if ($migrate_opts->{spice_ticket
}) {
5834 mon_cmd
($vmid, "set_password", protocol
=> 'spice', password
=>
5835 $migrate_opts->{spice_ticket
});
5836 mon_cmd
($vmid, "expire_password", protocol
=> 'spice', time => "+30");
5841 mon_cmd
($vmid, "balloon", value
=> $conf->{balloon
}*1024*1024)
5842 if !$statefile && $conf->{balloon
};
5844 foreach my $opt (keys %$conf) {
5845 next if $opt !~ m/^net\d+$/;
5846 my $nicconf = parse_net
($conf->{$opt});
5847 qemu_set_link_status
($vmid, $opt, 0) if $nicconf->{link_down
};
5851 mon_cmd
($vmid, 'qom-set',
5852 path
=> "machine/peripheral/balloon0",
5853 property
=> "guest-stats-polling-interval",
5854 value
=> 2) if (!defined($conf->{balloon
}) || $conf->{balloon
});
5857 print "Resumed VM, removing state\n";
5858 if (my $vmstate = $conf->{vmstate
}) {
5859 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
5860 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
5862 delete $conf->@{qw(lock vmstate runningmachine runningcpu)};
5863 PVE
::QemuConfig-
>write_config($vmid, $conf);
5866 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'post-start');
5871 sub vm_commandline
{
5872 my ($storecfg, $vmid, $snapname) = @_;
5874 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5876 my ($forcemachine, $forcecpu);
5878 my $snapshot = $conf->{snapshots
}->{$snapname};
5879 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
5881 # check for machine or CPU overrides in snapshot
5882 $forcemachine = $snapshot->{runningmachine
};
5883 $forcecpu = $snapshot->{runningcpu
};
5885 $snapshot->{digest
} = $conf->{digest
}; # keep file digest for API
5890 my $defaults = load_defaults
();
5892 my $cmd = config_to_command
($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu);
5894 return PVE
::Tools
::cmd2string
($cmd);
5898 my ($vmid, $skiplock) = @_;
5900 PVE
::QemuConfig-
>lock_config($vmid, sub {
5902 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5904 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5906 mon_cmd
($vmid, "system_reset");
5910 sub get_vm_volumes
{
5914 foreach_volid
($conf, sub {
5915 my ($volid, $attr) = @_;
5917 return if $volid =~ m
|^/|;
5919 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
5922 push @$vollist, $volid;
5928 sub cleanup_pci_devices
{
5929 my ($vmid, $conf) = @_;
5931 foreach my $key (keys %$conf) {
5932 next if $key !~ m/^hostpci(\d+)$/;
5933 my $hostpciindex = $1;
5934 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $hostpciindex);
5935 my $d = parse_hostpci
($conf->{$key});
5937 # NOTE: avoid PVE::SysFSTools::pci_cleanup_mdev_device as it requires PCI ID and we
5938 # don't want to break ABI just for this two liner
5939 my $dev_sysfs_dir = "/sys/bus/mdev/devices/$uuid";
5940 PVE
::SysFSTools
::file_write
("$dev_sysfs_dir/remove", "1") if -e
$dev_sysfs_dir;
5943 PVE
::QemuServer
::PCI
::remove_pci_reservation
($vmid);
5946 sub vm_stop_cleanup
{
5947 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
5952 my $vollist = get_vm_volumes
($conf);
5953 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
5955 if (my $tpmdrive = $conf->{tpmstate0
}) {
5956 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
5957 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
5959 PVE
::Storage
::unmap_volume
($storecfg, $tpm->{file
});
5964 foreach my $ext (qw(mon qmp pid vnc qga)) {
5965 unlink "/var/run/qemu-server/${vmid}.$ext";
5968 if ($conf->{ivshmem
}) {
5969 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
5970 # just delete it for now, VMs which have this already open do not
5971 # are affected, but new VMs will get a separated one. If this
5972 # becomes an issue we either add some sort of ref-counting or just
5973 # add a "don't delete on stop" flag to the ivshmem format.
5974 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name
} // $vmid);
5977 cleanup_pci_devices
($vmid, $conf);
5979 vmconfig_apply_pending
($vmid, $conf, $storecfg) if $apply_pending_changes;
5981 warn $@ if $@; # avoid errors - just warn
5984 # call only in locked context
5986 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
5988 my $pid = check_running
($vmid, $nocheck);
5993 $conf = PVE
::QemuConfig-
>load_config($vmid);
5994 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5995 if (!defined($timeout) && $shutdown && $conf->{startup
}) {
5996 my $opts = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
5997 $timeout = $opts->{down
} if $opts->{down
};
5999 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-stop');
6004 if (defined($conf) && get_qga_key
($conf, 'enabled')) {
6005 mon_cmd
($vmid, "guest-shutdown", timeout
=> $timeout);
6007 mon_cmd
($vmid, "system_powerdown");
6010 mon_cmd
($vmid, "quit");
6016 $timeout = 60 if !defined($timeout);
6019 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
6024 if ($count >= $timeout) {
6026 warn "VM still running - terminating now with SIGTERM\n";
6029 die "VM quit/powerdown failed - got timeout\n";
6032 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6036 if (!check_running
($vmid, $nocheck)) {
6037 warn "Unexpected: VM shutdown command failed, but VM not running anymore..\n";
6041 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
6044 die "VM quit/powerdown failed\n";
6052 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
6057 if ($count >= $timeout) {
6058 warn "VM still running - terminating now with SIGKILL\n";
6063 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6066 # Note: use $nocheck to skip tests if VM configuration file exists.
6067 # We need that when migration VMs to other nodes (files already moved)
6068 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
6070 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
6072 $force = 1 if !defined($force) && !$shutdown;
6075 my $pid = check_running
($vmid, $nocheck, $migratedfrom);
6076 kill 15, $pid if $pid;
6077 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migratedfrom);
6078 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 0);
6082 PVE
::QemuConfig-
>lock_config($vmid, sub {
6083 _do_vm_stop
($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
6088 my ($vmid, $timeout) = @_;
6090 PVE
::QemuConfig-
>lock_config($vmid, sub {
6093 # only reboot if running, as qmeventd starts it again on a stop event
6094 return if !check_running
($vmid);
6096 create_reboot_request
($vmid);
6098 my $storecfg = PVE
::Storage
::config
();
6099 _do_vm_stop
($storecfg, $vmid, undef, undef, $timeout, 1);
6103 # avoid that the next normal shutdown will be confused for a reboot
6104 clear_reboot_request
($vmid);
6110 # note: if using the statestorage parameter, the caller has to check privileges
6112 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
6119 PVE
::QemuConfig-
>lock_config($vmid, sub {
6121 $conf = PVE
::QemuConfig-
>load_config($vmid);
6123 my $is_backing_up = PVE
::QemuConfig-
>has_lock($conf, 'backup');
6124 PVE
::QemuConfig-
>check_lock($conf)
6125 if !($skiplock || $is_backing_up);
6127 die "cannot suspend to disk during backup\n"
6128 if $is_backing_up && $includestate;
6130 if ($includestate) {
6131 $conf->{lock} = 'suspending';
6132 my $date = strftime
("%Y-%m-%d", localtime(time()));
6133 $storecfg = PVE
::Storage
::config
();
6134 if (!$statestorage) {
6135 $statestorage = find_vmstate_storage
($conf, $storecfg);
6136 # check permissions for the storage
6137 my $rpcenv = PVE
::RPCEnvironment
::get
();
6138 if ($rpcenv->{type
} ne 'cli') {
6139 my $authuser = $rpcenv->get_user();
6140 $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
6145 $vmstate = PVE
::QemuConfig-
>__snapshot_save_vmstate(
6146 $vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
6147 $path = PVE
::Storage
::path
($storecfg, $vmstate);
6148 PVE
::QemuConfig-
>write_config($vmid, $conf);
6150 mon_cmd
($vmid, "stop");
6154 if ($includestate) {
6156 PVE
::Storage
::activate_volumes
($storecfg, [$vmstate]);
6159 set_migration_caps
($vmid, 1);
6160 mon_cmd
($vmid, "savevm-start", statefile
=> $path);
6162 my $state = mon_cmd
($vmid, "query-savevm");
6163 if (!$state->{status
}) {
6164 die "savevm not active\n";
6165 } elsif ($state->{status
} eq 'active') {
6168 } elsif ($state->{status
} eq 'completed') {
6169 print "State saved, quitting\n";
6171 } elsif ($state->{status
} eq 'failed' && $state->{error
}) {
6172 die "query-savevm failed with error '$state->{error}'\n"
6174 die "query-savevm returned status '$state->{status}'\n";
6180 PVE
::QemuConfig-
>lock_config($vmid, sub {
6181 $conf = PVE
::QemuConfig-
>load_config($vmid);
6183 # cleanup, but leave suspending lock, to indicate something went wrong
6185 mon_cmd
($vmid, "savevm-end");
6186 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
6187 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
6188 delete $conf->@{qw(vmstate runningmachine runningcpu)};
6189 PVE
::QemuConfig-
>write_config($vmid, $conf);
6195 die "lock changed unexpectedly\n"
6196 if !PVE
::QemuConfig-
>has_lock($conf, 'suspending');
6198 mon_cmd
($vmid, "quit");
6199 $conf->{lock} = 'suspended';
6200 PVE
::QemuConfig-
>write_config($vmid, $conf);
6206 my ($vmid, $skiplock, $nocheck) = @_;
6208 PVE
::QemuConfig-
>lock_config($vmid, sub {
6209 my $res = mon_cmd
($vmid, 'query-status');
6210 my $resume_cmd = 'cont';
6213 if ($res->{status
}) {
6214 return if $res->{status
} eq 'running'; # job done, go home
6215 $resume_cmd = 'system_wakeup' if $res->{status
} eq 'suspended';
6216 $reset = 1 if $res->{status
} eq 'shutdown';
6221 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6223 PVE
::QemuConfig-
>check_lock($conf)
6224 if !($skiplock || PVE
::QemuConfig-
>has_lock($conf, 'backup'));
6228 # required if a VM shuts down during a backup and we get a resume
6229 # request before the backup finishes for example
6230 mon_cmd
($vmid, "system_reset");
6232 mon_cmd
($vmid, $resume_cmd);
6237 my ($vmid, $skiplock, $key) = @_;
6239 PVE
::QemuConfig-
>lock_config($vmid, sub {
6241 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6243 # there is no qmp command, so we use the human monitor command
6244 my $res = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "sendkey $key");
6245 die $res if $res ne '';
6249 # vzdump restore implementaion
6251 sub tar_archive_read_firstfile
{
6252 my $archive = shift;
6254 die "ERROR: file '$archive' does not exist\n" if ! -f
$archive;
6256 # try to detect archive type first
6257 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
6258 die "unable to open file '$archive'\n";
6259 my $firstfile = <$fh>;
6263 die "ERROR: archive contaions no data\n" if !$firstfile;
6269 sub tar_restore_cleanup
{
6270 my ($storecfg, $statfile) = @_;
6272 print STDERR
"starting cleanup\n";
6274 if (my $fd = IO
::File-
>new($statfile, "r")) {
6275 while (defined(my $line = <$fd>)) {
6276 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6279 if ($volid =~ m
|^/|) {
6280 unlink $volid || die 'unlink failed\n';
6282 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6284 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6286 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6288 print STDERR
"unable to parse line in statfile - $line";
6295 sub restore_file_archive
{
6296 my ($archive, $vmid, $user, $opts) = @_;
6298 return restore_vma_archive
($archive, $vmid, $user, $opts)
6301 my $info = PVE
::Storage
::archive_info
($archive);
6302 my $format = $opts->{format
} // $info->{format
};
6303 my $comp = $info->{compression
};
6305 # try to detect archive format
6306 if ($format eq 'tar') {
6307 return restore_tar_archive
($archive, $vmid, $user, $opts);
6309 return restore_vma_archive
($archive, $vmid, $user, $opts, $comp);
6313 # hepler to remove disks that will not be used after restore
6314 my $restore_cleanup_oldconf = sub {
6315 my ($storecfg, $vmid, $oldconf, $virtdev_hash) = @_;
6317 my $kept_disks = {};
6319 PVE
::QemuConfig-
>foreach_volume($oldconf, sub {
6320 my ($ds, $drive) = @_;
6322 return if drive_is_cdrom
($drive, 1);
6324 my $volid = $drive->{file
};
6325 return if !$volid || $volid =~ m
|^/|;
6327 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
6328 return if !$path || !$owner || ($owner != $vmid);
6330 # Note: only delete disk we want to restore
6331 # other volumes will become unused
6332 if ($virtdev_hash->{$ds}) {
6333 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
6338 $kept_disks->{$volid} = 1;
6342 # after the restore we have no snapshots anymore
6343 for my $snapname (keys $oldconf->{snapshots
}->%*) {
6344 my $snap = $oldconf->{snapshots
}->{$snapname};
6345 if ($snap->{vmstate
}) {
6346 eval { PVE
::Storage
::vdisk_free
($storecfg, $snap->{vmstate
}); };
6352 for my $volid (keys $kept_disks->%*) {
6353 eval { PVE
::Storage
::volume_snapshot_delete
($storecfg, $volid, $snapname); };
6359 # Helper to parse vzdump backup device hints
6361 # $rpcenv: Environment, used to ckeck storage permissions
6362 # $user: User ID, to check storage permissions
6363 # $storecfg: Storage configuration
6364 # $fh: the file handle for reading the configuration
6365 # $devinfo: should contain device sizes for all backu-up'ed devices
6366 # $options: backup options (pool, default storage)
6368 # Return: $virtdev_hash, updates $devinfo (add devname, virtdev, format, storeid)
6369 my $parse_backup_hints = sub {
6370 my ($rpcenv, $user, $storecfg, $fh, $devinfo, $options) = @_;
6372 my $check_storage = sub { # assert if an image can be allocate
6373 my ($storeid, $scfg) = @_;
6374 die "Content type 'images' is not available on storage '$storeid'\n"
6375 if !$scfg->{content
}->{images
};
6376 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace'])
6377 if $user ne 'root@pam';
6380 my $virtdev_hash = {};
6381 while (defined(my $line = <$fh>)) {
6382 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
6383 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
6384 die "archive does not contain data for drive '$virtdev'\n"
6385 if !$devinfo->{$devname};
6387 if (defined($options->{storage
})) {
6388 $storeid = $options->{storage
} || 'local';
6389 } elsif (!$storeid) {
6392 $format = 'raw' if !$format;
6393 $devinfo->{$devname}->{devname
} = $devname;
6394 $devinfo->{$devname}->{virtdev
} = $virtdev;
6395 $devinfo->{$devname}->{format
} = $format;
6396 $devinfo->{$devname}->{storeid
} = $storeid;
6398 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6399 $check_storage->($storeid, $scfg); # permission and content type check
6401 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
6402 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
6404 my $drive = parse_drive
($virtdev, $2);
6406 if (drive_is_cloudinit
($drive)) {
6407 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
6408 $storeid = $options->{storage
} if defined ($options->{storage
});
6409 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6410 my $format = qemu_img_format
($scfg, $volname); # has 'raw' fallback
6412 $check_storage->($storeid, $scfg); # permission and content type check
6414 $virtdev_hash->{$virtdev} = {
6416 storeid
=> $storeid,
6417 size
=> PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
,
6424 return $virtdev_hash;
6427 # Helper to allocate and activate all volumes required for a restore
6429 # $storecfg: Storage configuration
6430 # $virtdev_hash: as returned by parse_backup_hints()
6432 # Returns: { $virtdev => $volid }
6433 my $restore_allocate_devices = sub {
6434 my ($storecfg, $virtdev_hash, $vmid) = @_;
6437 foreach my $virtdev (sort keys %$virtdev_hash) {
6438 my $d = $virtdev_hash->{$virtdev};
6439 my $alloc_size = int(($d->{size
} + 1024 - 1)/1024);
6440 my $storeid = $d->{storeid
};
6441 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6443 # test if requested format is supported
6444 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
6445 my $supported = grep { $_ eq $d->{format
} } @$validFormats;
6446 $d->{format
} = $defFormat if !$supported;
6449 if ($d->{is_cloudinit
}) {
6450 $name = "vm-$vmid-cloudinit";
6451 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6452 if ($scfg->{path
}) {
6453 $name .= ".$d->{format}";
6457 my $volid = PVE
::Storage
::vdisk_alloc
(
6458 $storecfg, $storeid, $vmid, $d->{format
}, $name, $alloc_size);
6460 print STDERR
"new volume ID is '$volid'\n";
6461 $d->{volid
} = $volid;
6463 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
6465 $map->{$virtdev} = $volid;
6471 sub restore_update_config_line
{
6472 my ($cookie, $map, $line, $unique) = @_;
6474 return '' if $line =~ m/^\#qmdump\#/;
6475 return '' if $line =~ m/^\#vzdump\#/;
6476 return '' if $line =~ m/^lock:/;
6477 return '' if $line =~ m/^unused\d+:/;
6478 return '' if $line =~ m/^parent:/;
6482 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
6483 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
6484 # try to convert old 1.X settings
6485 my ($id, $ind, $ethcfg) = ($1, $2, $3);
6486 foreach my $devconfig (PVE
::Tools
::split_list
($ethcfg)) {
6487 my ($model, $macaddr) = split(/\=/, $devconfig);
6488 $macaddr = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if !$macaddr || $unique;
6491 bridge
=> "vmbr$ind",
6492 macaddr
=> $macaddr,
6494 my $netstr = print_net
($net);
6496 $res .= "net$cookie->{netcount}: $netstr\n";
6497 $cookie->{netcount
}++;
6499 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
6500 my ($id, $netstr) = ($1, $2);
6501 my $net = parse_net
($netstr);
6502 $net->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if $net->{macaddr
};
6503 $netstr = print_net
($net);
6504 $res .= "$id: $netstr\n";
6505 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk|tpmstate)\d+):\s*(\S+)\s*$/) {
6508 my $di = parse_drive
($virtdev, $value);
6509 if (defined($di->{backup
}) && !$di->{backup
}) {
6511 } elsif ($map->{$virtdev}) {
6512 delete $di->{format
}; # format can change on restore
6513 $di->{file
} = $map->{$virtdev};
6514 $value = print_drive
($di);
6515 $res .= "$virtdev: $value\n";
6519 } elsif (($line =~ m/^vmgenid: (.*)/)) {
6521 if ($vmgenid ne '0') {
6522 # always generate a new vmgenid if there was a valid one setup
6523 $vmgenid = generate_uuid
();
6525 $res .= "vmgenid: $vmgenid\n";
6526 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
6527 my ($uuid, $uuid_str);
6528 UUID
::generate
($uuid);
6529 UUID
::unparse
($uuid, $uuid_str);
6530 my $smbios1 = parse_smbios1
($2);
6531 $smbios1->{uuid
} = $uuid_str;
6532 $res .= $1.print_smbios1
($smbios1)."\n";
6540 my $restore_deactivate_volumes = sub {
6541 my ($storecfg, $virtdev_hash) = @_;
6544 for my $dev (values $virtdev_hash->%*) {
6545 push $vollist->@*, $dev->{volid
} if $dev->{volid
};
6548 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
6549 print STDERR
$@ if $@;
6552 my $restore_destroy_volumes = sub {
6553 my ($storecfg, $virtdev_hash) = @_;
6555 for my $dev (values $virtdev_hash->%*) {
6556 my $volid = $dev->{volid
} or next;
6558 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6559 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6561 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6565 my $restore_merge_config = sub {
6566 my ($filename, $backup_conf_raw, $override_conf) = @_;
6568 my $backup_conf = parse_vm_config
($filename, $backup_conf_raw);
6569 for my $key (keys $override_conf->%*) {
6570 $backup_conf->{$key} = $override_conf->{$key};
6573 return $backup_conf;
6577 my ($cfg, $vmid) = @_;
6579 my $info = PVE
::Storage
::vdisk_list
($cfg, undef, $vmid, undef, 'images');
6581 my $volid_hash = {};
6582 foreach my $storeid (keys %$info) {
6583 foreach my $item (@{$info->{$storeid}}) {
6584 next if !($item->{volid
} && $item->{size
});
6585 $item->{path
} = PVE
::Storage
::path
($cfg, $item->{volid
});
6586 $volid_hash->{$item->{volid
}} = $item;
6593 sub update_disk_config
{
6594 my ($vmid, $conf, $volid_hash) = @_;
6597 my $prefix = "VM $vmid";
6599 # used and unused disks
6600 my $referenced = {};
6602 # Note: it is allowed to define multiple storages with same path (alias), so
6603 # we need to check both 'volid' and real 'path' (two different volid can point
6604 # to the same path).
6606 my $referencedpath = {};
6609 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6610 my ($opt, $drive) = @_;
6612 my $volid = $drive->{file
};
6614 my $volume = $volid_hash->{$volid};
6616 # mark volid as "in-use" for next step
6617 $referenced->{$volid} = 1;
6618 if ($volume && (my $path = $volume->{path
})) {
6619 $referencedpath->{$path} = 1;
6622 return if drive_is_cdrom
($drive);
6625 my ($updated, $msg) = PVE
::QemuServer
::Drive
::update_disksize
($drive, $volume->{size
});
6626 if (defined($updated)) {
6628 $conf->{$opt} = print_drive
($updated);
6629 print "$prefix ($opt): $msg\n";
6633 # remove 'unusedX' entry if volume is used
6634 PVE
::QemuConfig-
>foreach_unused_volume($conf, sub {
6635 my ($opt, $drive) = @_;
6637 my $volid = $drive->{file
};
6641 $path = $volid_hash->{$volid}->{path
} if $volid_hash->{$volid};
6642 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6643 print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
6645 delete $conf->{$opt};
6648 $referenced->{$volid} = 1;
6649 $referencedpath->{$path} = 1 if $path;
6652 foreach my $volid (sort keys %$volid_hash) {
6653 next if $volid =~ m/vm-$vmid-state-/;
6654 next if $referenced->{$volid};
6655 my $path = $volid_hash->{$volid}->{path
};
6656 next if !$path; # just to be sure
6657 next if $referencedpath->{$path};
6659 my $key = PVE
::QemuConfig-
>add_unused_volume($conf, $volid);
6660 print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
6661 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6668 my ($vmid, $nolock, $dryrun) = @_;
6670 my $cfg = PVE
::Storage
::config
();
6672 print "rescan volumes...\n";
6673 my $volid_hash = scan_volids
($cfg, $vmid);
6675 my $updatefn = sub {
6678 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6680 PVE
::QemuConfig-
>check_lock($conf);
6683 foreach my $volid (keys %$volid_hash) {
6684 my $info = $volid_hash->{$volid};
6685 $vm_volids->{$volid} = $info if $info->{vmid
} && $info->{vmid
} == $vmid;
6688 my $changes = update_disk_config
($vmid, $conf, $vm_volids);
6690 PVE
::QemuConfig-
>write_config($vmid, $conf) if $changes && !$dryrun;
6693 if (defined($vmid)) {
6697 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6700 my $vmlist = config_list
();
6701 foreach my $vmid (keys %$vmlist) {
6705 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6711 sub restore_proxmox_backup_archive
{
6712 my ($archive, $vmid, $user, $options) = @_;
6714 my $storecfg = PVE
::Storage
::config
();
6716 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($archive);
6717 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6719 my $fingerprint = $scfg->{fingerprint
};
6720 my $keyfile = PVE
::Storage
::PBSPlugin
::pbs_encryption_key_file_name
($storecfg, $storeid);
6722 my $repo = PVE
::PBSClient
::get_repository
($scfg);
6723 my $namespace = $scfg->{namespace
};
6725 # This is only used for `pbs-restore` and the QEMU PBS driver (live-restore)
6726 my $password = PVE
::Storage
::PBSPlugin
::pbs_get_password
($scfg, $storeid);
6727 local $ENV{PBS_PASSWORD
} = $password;
6728 local $ENV{PBS_FINGERPRINT
} = $fingerprint if defined($fingerprint);
6730 my ($vtype, $pbs_backup_name, undef, undef, undef, undef, $format) =
6731 PVE
::Storage
::parse_volname
($storecfg, $archive);
6733 die "got unexpected vtype '$vtype'\n" if $vtype ne 'backup';
6735 die "got unexpected backup format '$format'\n" if $format ne 'pbs-vm';
6737 my $tmpdir = "/var/tmp/vzdumptmp$$";
6741 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6742 # disable interrupts (always do cleanups)
6746 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
6748 # Note: $oldconf is undef if VM does not exists
6749 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
6750 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
6751 my $new_conf_raw = '';
6753 my $rpcenv = PVE
::RPCEnvironment
::get
();
6754 my $devinfo = {}; # info about drives included in backup
6755 my $virtdev_hash = {}; # info about allocated drives
6763 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6765 my $cfgfn = "$tmpdir/qemu-server.conf";
6766 my $firewall_config_fn = "$tmpdir/fw.conf";
6767 my $index_fn = "$tmpdir/index.json";
6769 my $cmd = "restore";
6771 my $param = [$pbs_backup_name, "index.json", $index_fn];
6772 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6773 my $index = PVE
::Tools
::file_get_contents
($index_fn);
6774 $index = decode_json
($index);
6776 foreach my $info (@{$index->{files
}}) {
6777 if ($info->{filename
} =~ m/^(drive-\S+).img.fidx$/) {
6779 if ($info->{size
} =~ m/^(\d+)$/) { # untaint size
6780 $devinfo->{$devname}->{size
} = $1;
6782 die "unable to parse file size in 'index.json' - got '$info->{size}'\n";
6787 my $is_qemu_server_backup = scalar(
6788 grep { $_->{filename
} eq 'qemu-server.conf.blob' } @{$index->{files
}}
6790 if (!$is_qemu_server_backup) {
6791 die "backup does not look like a qemu-server backup (missing 'qemu-server.conf' file)\n";
6793 my $has_firewall_config = scalar(grep { $_->{filename
} eq 'fw.conf.blob' } @{$index->{files
}});
6795 $param = [$pbs_backup_name, "qemu-server.conf", $cfgfn];
6796 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6798 if ($has_firewall_config) {
6799 $param = [$pbs_backup_name, "fw.conf", $firewall_config_fn];
6800 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6802 my $pve_firewall_dir = '/etc/pve/firewall';
6803 mkdir $pve_firewall_dir; # make sure the dir exists
6804 PVE
::Tools
::file_copy
($firewall_config_fn, "${pve_firewall_dir}/$vmid.fw");
6807 my $fh = IO
::File-
>new($cfgfn, "r") ||
6808 die "unable to read qemu-server.conf - $!\n";
6810 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $storecfg, $fh, $devinfo, $options);
6812 # fixme: rate limit?
6814 # create empty/temp config
6815 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\nlock: create");
6817 $restore_cleanup_oldconf->($storecfg, $vmid, $oldconf, $virtdev_hash) if $oldconf;
6820 my $map = $restore_allocate_devices->($storecfg, $virtdev_hash, $vmid);
6822 foreach my $virtdev (sort keys %$virtdev_hash) {
6823 my $d = $virtdev_hash->{$virtdev};
6824 next if $d->{is_cloudinit
}; # no need to restore cloudinit
6826 # this fails if storage is unavailable
6827 my $volid = $d->{volid
};
6828 my $path = PVE
::Storage
::path
($storecfg, $volid);
6830 # for live-restore we only want to preload the efidisk and TPM state
6831 next if $options->{live
} && $virtdev ne 'efidisk0' && $virtdev ne 'tpmstate0';
6834 if (defined(my $ns = $scfg->{namespace
})) {
6835 @ns_arg = ('--ns', $ns);
6838 my $pbs_restore_cmd = [
6839 '/usr/bin/pbs-restore',
6840 '--repository', $repo,
6843 "$d->{devname}.img.fidx",
6848 push @$pbs_restore_cmd, '--format', $d->{format
} if $d->{format
};
6849 push @$pbs_restore_cmd, '--keyfile', $keyfile if -e
$keyfile;
6851 if (PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $volid)) {
6852 push @$pbs_restore_cmd, '--skip-zero';
6855 my $dbg_cmdstring = PVE
::Tools
::cmd2string
($pbs_restore_cmd);
6856 print "restore proxmox backup image: $dbg_cmdstring\n";
6857 run_command
($pbs_restore_cmd);
6860 $fh->seek(0, 0) || die "seek failed - $!\n";
6862 my $cookie = { netcount
=> 0 };
6863 while (defined(my $line = <$fh>)) {
6864 $new_conf_raw .= restore_update_config_line
(
6876 if ($err || !$options->{live
}) {
6877 $restore_deactivate_volumes->($storecfg, $virtdev_hash);
6883 $restore_destroy_volumes->($storecfg, $virtdev_hash);
6887 if ($options->{live
}) {
6888 # keep lock during live-restore
6889 $new_conf_raw .= "\nlock: create";
6892 my $new_conf = $restore_merge_config->($conffile, $new_conf_raw, $options->{override_conf
});
6893 PVE
::QemuConfig-
>write_config($vmid, $new_conf);
6895 eval { rescan
($vmid, 1); };
6898 PVE
::AccessControl
::add_vm_to_pool
($vmid, $options->{pool
}) if $options->{pool
};
6900 if ($options->{live
}) {
6906 local $SIG{PIPE
} = sub { die "got signal ($!) - abort\n"; };
6908 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6909 die "cannot do live-restore for template\n" if PVE
::QemuConfig-
>is_template($conf);
6911 # these special drives are already restored before start
6912 delete $devinfo->{'drive-efidisk0'};
6913 delete $devinfo->{'drive-tpmstate0-backup'};
6917 keyfile
=> $keyfile,
6918 snapshot
=> $pbs_backup_name,
6919 namespace
=> $namespace,
6921 pbs_live_restore
($vmid, $conf, $storecfg, $devinfo, $pbs_opts);
6923 PVE
::QemuConfig-
>remove_lock($vmid, "create");
6927 sub pbs_live_restore
{
6928 my ($vmid, $conf, $storecfg, $restored_disks, $opts) = @_;
6930 print "starting VM for live-restore\n";
6931 print "repository: '$opts->{repo}', snapshot: '$opts->{snapshot}'\n";
6933 my $pbs_backing = {};
6934 for my $ds (keys %$restored_disks) {
6935 $ds =~ m/^drive-(.*)$/;
6937 $pbs_backing->{$confname} = {
6938 repository
=> $opts->{repo
},
6939 snapshot
=> $opts->{snapshot
},
6940 archive
=> "$ds.img.fidx",
6942 $pbs_backing->{$confname}->{keyfile
} = $opts->{keyfile
} if -e
$opts->{keyfile
};
6943 $pbs_backing->{$confname}->{namespace
} = $opts->{namespace
} if defined($opts->{namespace
});
6945 my $drive = parse_drive
($confname, $conf->{$confname});
6946 print "restoring '$ds' to '$drive->{file}'\n";
6949 my $drives_streamed = 0;
6951 # make sure HA doesn't interrupt our restore by stopping the VM
6952 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid)) {
6953 run_command
(['ha-manager', 'set', "vm:$vmid", '--state', 'started']);
6956 # start VM with backing chain pointing to PBS backup, environment vars for PBS driver
6957 # in QEMU (PBS_PASSWORD and PBS_FINGERPRINT) are already set by our caller
6958 vm_start_nolock
($storecfg, $vmid, $conf, {paused
=> 1, 'pbs-backing' => $pbs_backing}, {});
6960 my $qmeventd_fd = register_qmeventd_handle
($vmid);
6962 # begin streaming, i.e. data copy from PBS to target disk for every vol,
6963 # this will effectively collapse the backing image chain consisting of
6964 # [target <- alloc-track -> PBS snapshot] to just [target] (alloc-track
6965 # removes itself once all backing images vanish with 'auto-remove=on')
6967 for my $ds (sort keys %$restored_disks) {
6968 my $job_id = "restore-$ds";
6969 mon_cmd
($vmid, 'block-stream',
6970 'job-id' => $job_id,
6973 $jobs->{$job_id} = {};
6976 mon_cmd
($vmid, 'cont');
6977 qemu_drive_mirror_monitor
($vmid, undef, $jobs, 'auto', 0, 'stream');
6979 print "restore-drive jobs finished successfully, removing all tracking block devices"
6980 ." to disconnect from Proxmox Backup Server\n";
6982 for my $ds (sort keys %$restored_disks) {
6983 mon_cmd
($vmid, 'blockdev-del', 'node-name' => "$ds-pbs");
6986 close($qmeventd_fd);
6992 warn "An error occurred during live-restore: $err\n";
6993 _do_vm_stop
($storecfg, $vmid, 1, 1, 10, 0, 1);
6994 die "live-restore failed\n";
6998 sub restore_vma_archive
{
6999 my ($archive, $vmid, $user, $opts, $comp) = @_;
7001 my $readfrom = $archive;
7003 my $cfg = PVE
::Storage
::config
();
7005 my $bwlimit = $opts->{bwlimit
};
7007 my $dbg_cmdstring = '';
7008 my $add_pipe = sub {
7010 push @$commands, $cmd;
7011 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
7012 $dbg_cmdstring .= PVE
::Tools
::cmd2string
($cmd);
7017 if ($archive eq '-') {
7020 # If we use a backup from a PVE defined storage we also consider that
7021 # storage's rate limit:
7022 my (undef, $volid) = PVE
::Storage
::path_to_volume_id
($cfg, $archive);
7023 if (defined($volid)) {
7024 my ($sid, undef) = PVE
::Storage
::parse_volume_id
($volid);
7025 my $readlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [$sid], $bwlimit);
7027 print STDERR
"applying read rate limit: $readlimit\n";
7028 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
7029 $add_pipe->($cstream);
7035 my $info = PVE
::Storage
::decompressor_info
('vma', $comp);
7036 my $cmd = $info->{decompressor
};
7037 push @$cmd, $readfrom;
7041 my $tmpdir = "/var/tmp/vzdumptmp$$";
7044 # disable interrupts (always do cleanups)
7048 local $SIG{HUP
} = sub { warn "got interrupt - ignored\n"; };
7050 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
7051 POSIX
::mkfifo
($mapfifo, 0600);
7053 my $openfifo = sub { open($fifofh, '>', $mapfifo) or die $! };
7055 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
7060 my $devinfo = {}; # info about drives included in backup
7061 my $virtdev_hash = {}; # info about allocated drives
7063 my $rpcenv = PVE
::RPCEnvironment
::get
();
7065 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7067 # Note: $oldconf is undef if VM does not exist
7068 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
7069 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
7070 my $new_conf_raw = '';
7074 my $print_devmap = sub {
7075 my $cfgfn = "$tmpdir/qemu-server.conf";
7077 # we can read the config - that is already extracted
7078 my $fh = IO
::File-
>new($cfgfn, "r") ||
7079 die "unable to read qemu-server.conf - $!\n";
7081 my $fwcfgfn = "$tmpdir/qemu-server.fw";
7083 my $pve_firewall_dir = '/etc/pve/firewall';
7084 mkdir $pve_firewall_dir; # make sure the dir exists
7085 PVE
::Tools
::file_copy
($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
7088 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $cfg, $fh, $devinfo, $opts);
7090 foreach my $info (values %{$virtdev_hash}) {
7091 my $storeid = $info->{storeid
};
7092 next if defined($storage_limits{$storeid});
7094 my $limit = PVE
::Storage
::get_bandwidth_limit
('restore', [$storeid], $bwlimit) // 0;
7095 print STDERR
"rate limit for storage $storeid: $limit KiB/s\n" if $limit;
7096 $storage_limits{$storeid} = $limit * 1024;
7099 foreach my $devname (keys %$devinfo) {
7100 die "found no device mapping information for device '$devname'\n"
7101 if !$devinfo->{$devname}->{virtdev
};
7104 # create empty/temp config
7106 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\n");
7107 $restore_cleanup_oldconf->($cfg, $vmid, $oldconf, $virtdev_hash);
7111 my $map = $restore_allocate_devices->($cfg, $virtdev_hash, $vmid);
7113 # print restore information to $fifofh
7114 foreach my $virtdev (sort keys %$virtdev_hash) {
7115 my $d = $virtdev_hash->{$virtdev};
7116 next if $d->{is_cloudinit
}; # no need to restore cloudinit
7118 my $storeid = $d->{storeid
};
7119 my $volid = $d->{volid
};
7122 if (my $limit = $storage_limits{$storeid}) {
7123 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
7126 my $write_zeros = 1;
7127 if (PVE
::Storage
::volume_has_feature
($cfg, 'sparseinit', $volid)) {
7131 my $path = PVE
::Storage
::path
($cfg, $volid);
7133 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
7135 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
7138 $fh->seek(0, 0) || die "seek failed - $!\n";
7140 my $cookie = { netcount
=> 0 };
7141 while (defined(my $line = <$fh>)) {
7142 $new_conf_raw .= restore_update_config_line
(
7159 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7160 local $SIG{ALRM
} = sub { die "got timeout\n"; };
7162 $oldtimeout = alarm($timeout);
7169 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
7170 my ($dev_id, $size, $devname) = ($1, $2, $3);
7171 $devinfo->{$devname} = { size
=> $size, dev_id
=> $dev_id };
7172 } elsif ($line =~ m/^CTIME: /) {
7173 # we correctly received the vma config, so we can disable
7174 # the timeout now for disk allocation (set to 10 minutes, so
7175 # that we always timeout if something goes wrong)
7178 print $fifofh "done\n";
7179 my $tmp = $oldtimeout || 0;
7180 $oldtimeout = undef;
7187 print "restore vma archive: $dbg_cmdstring\n";
7188 run_command
($commands, input
=> $input, outfunc
=> $parser, afterfork
=> $openfifo);
7192 alarm($oldtimeout) if $oldtimeout;
7194 $restore_deactivate_volumes->($cfg, $virtdev_hash);
7196 close($fifofh) if $fifofh;
7201 $restore_destroy_volumes->($cfg, $virtdev_hash);
7205 my $new_conf = $restore_merge_config->($conffile, $new_conf_raw, $opts->{override_conf
});
7206 PVE
::QemuConfig-
>write_config($vmid, $new_conf);
7208 eval { rescan
($vmid, 1); };
7211 PVE
::AccessControl
::add_vm_to_pool
($vmid, $opts->{pool
}) if $opts->{pool
};
7214 sub restore_tar_archive
{
7215 my ($archive, $vmid, $user, $opts) = @_;
7217 if (scalar(keys $opts->{override_conf
}->%*) > 0) {
7218 my $keystring = join(' ', keys $opts->{override_conf
}->%*);
7219 die "cannot pass along options ($keystring) when restoring from tar archive\n";
7222 if ($archive ne '-') {
7223 my $firstfile = tar_archive_read_firstfile
($archive);
7224 die "ERROR: file '$archive' does not look like a QemuServer vzdump backup\n"
7225 if $firstfile ne 'qemu-server.conf';
7228 my $storecfg = PVE
::Storage
::config
();
7230 # avoid zombie disks when restoring over an existing VM -> cleanup first
7231 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
7232 # skiplock=1 because qmrestore has set the 'create' lock itself already
7233 my $vmcfgfn = PVE
::QemuConfig-
>config_file($vmid);
7234 destroy_vm
($storecfg, $vmid, 1, { lock => 'restore' }) if -f
$vmcfgfn;
7236 my $tocmd = "/usr/lib/qemu-server/qmextract";
7238 $tocmd .= " --storage " . PVE
::Tools
::shellquote
($opts->{storage
}) if $opts->{storage
};
7239 $tocmd .= " --pool " . PVE
::Tools
::shellquote
($opts->{pool
}) if $opts->{pool
};
7240 $tocmd .= ' --prealloc' if $opts->{prealloc
};
7241 $tocmd .= ' --info' if $opts->{info
};
7243 # tar option "xf" does not autodetect compression when read from STDIN,
7244 # so we pipe to zcat
7245 my $cmd = "zcat -f|tar xf " . PVE
::Tools
::shellquote
($archive) . " " .
7246 PVE
::Tools
::shellquote
("--to-command=$tocmd");
7248 my $tmpdir = "/var/tmp/vzdumptmp$$";
7251 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
7252 local $ENV{VZDUMP_VMID
} = $vmid;
7253 local $ENV{VZDUMP_USER
} = $user;
7255 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
7256 my $new_conf_raw = '';
7258 # disable interrupts (always do cleanups)
7262 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
7270 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
7272 if ($archive eq '-') {
7273 print "extracting archive from STDIN\n";
7274 run_command
($cmd, input
=> "<&STDIN");
7276 print "extracting archive '$archive'\n";
7280 return if $opts->{info
};
7284 my $statfile = "$tmpdir/qmrestore.stat";
7285 if (my $fd = IO
::File-
>new($statfile, "r")) {
7286 while (defined (my $line = <$fd>)) {
7287 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
7288 $map->{$1} = $2 if $1;
7290 print STDERR
"unable to parse line in statfile - $line\n";
7296 my $confsrc = "$tmpdir/qemu-server.conf";
7298 my $srcfd = IO
::File-
>new($confsrc, "r") || die "unable to open file '$confsrc'\n";
7300 my $cookie = { netcount
=> 0 };
7301 while (defined (my $line = <$srcfd>)) {
7302 $new_conf_raw .= restore_update_config_line
(
7313 tar_restore_cleanup
($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info
};
7319 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
7321 PVE
::Cluster
::cfs_update
(); # make sure we read new file
7323 eval { rescan
($vmid, 1); };
7327 sub foreach_storage_used_by_vm
{
7328 my ($conf, $func) = @_;
7332 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7333 my ($ds, $drive) = @_;
7334 return if drive_is_cdrom
($drive);
7336 my $volid = $drive->{file
};
7338 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
7339 $sidhash->{$sid} = $sid if $sid;
7342 foreach my $sid (sort keys %$sidhash) {
7347 my $qemu_snap_storage = {
7350 sub do_snapshots_with_qemu
{
7351 my ($storecfg, $volid, $deviceid) = @_;
7353 return if $deviceid =~ m/tpmstate0/;
7355 my $storage_name = PVE
::Storage
::parse_volume_id
($volid);
7356 my $scfg = $storecfg->{ids
}->{$storage_name};
7357 die "could not find storage '$storage_name'\n" if !defined($scfg);
7359 if ($qemu_snap_storage->{$scfg->{type
}} && !$scfg->{krbd
}){
7363 if ($volid =~ m/\.(qcow2|qed)$/){
7370 sub qga_check_running
{
7371 my ($vmid, $nowarn) = @_;
7373 eval { mon_cmd
($vmid, "guest-ping", timeout
=> 3); };
7375 warn "Qemu Guest Agent is not running - $@" if !$nowarn;
7381 sub template_create
{
7382 my ($vmid, $conf, $disk) = @_;
7384 my $storecfg = PVE
::Storage
::config
();
7386 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7387 my ($ds, $drive) = @_;
7389 return if drive_is_cdrom
($drive);
7390 return if $disk && $ds ne $disk;
7392 my $volid = $drive->{file
};
7393 return if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
7395 my $voliddst = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
7396 $drive->{file
} = $voliddst;
7397 $conf->{$ds} = print_drive
($drive);
7398 PVE
::QemuConfig-
>write_config($vmid, $conf);
7402 sub convert_iscsi_path
{
7405 if ($path =~ m
|^iscsi
://([^/]+)/([^/]+)/(.+)$|) {
7410 my $initiator_name = get_initiator_name
();
7412 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
7413 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
7416 die "cannot convert iscsi path '$path', unkown format\n";
7419 sub qemu_img_convert
{
7420 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
7422 my $storecfg = PVE
::Storage
::config
();
7423 my ($src_storeid, $src_volname) = PVE
::Storage
::parse_volume_id
($src_volid, 1);
7424 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid, 1);
7426 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
7430 my $src_is_iscsi = 0;
7434 PVE
::Storage
::activate_volumes
($storecfg, [$src_volid], $snapname);
7435 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
7436 $src_format = qemu_img_format
($src_scfg, $src_volname);
7437 $src_path = PVE
::Storage
::path
($storecfg, $src_volid, $snapname);
7438 $src_is_iscsi = ($src_path =~ m
|^iscsi
://|);
7439 $cachemode = 'none' if $src_scfg->{type
} eq 'zfspool';
7440 } elsif (-f
$src_volid || -b
$src_volid) {
7441 $src_path = $src_volid;
7442 if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7447 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
7449 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7450 my $dst_format = qemu_img_format
($dst_scfg, $dst_volname);
7451 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7452 my $dst_is_iscsi = ($dst_path =~ m
|^iscsi
://|);
7455 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
7456 push @$cmd, '-l', "snapshot.name=$snapname"
7457 if $snapname && $src_format && $src_format eq "qcow2";
7458 push @$cmd, '-t', 'none' if $dst_scfg->{type
} eq 'zfspool';
7459 push @$cmd, '-T', $cachemode if defined($cachemode);
7461 if ($src_is_iscsi) {
7462 push @$cmd, '--image-opts';
7463 $src_path = convert_iscsi_path
($src_path);
7464 } elsif ($src_format) {
7465 push @$cmd, '-f', $src_format;
7468 if ($dst_is_iscsi) {
7469 push @$cmd, '--target-image-opts';
7470 $dst_path = convert_iscsi_path
($dst_path);
7472 push @$cmd, '-O', $dst_format;
7475 push @$cmd, $src_path;
7477 if (!$dst_is_iscsi && $is_zero_initialized) {
7478 push @$cmd, "zeroinit:$dst_path";
7480 push @$cmd, $dst_path;
7485 if($line =~ m/\((\S+)\/100\
%\)/){
7487 my $transferred = int($size * $percent / 100);
7488 my $total_h = render_bytes
($size, 1);
7489 my $transferred_h = render_bytes
($transferred, 1);
7491 print "transferred $transferred_h of $total_h ($percent%)\n";
7496 eval { run_command
($cmd, timeout
=> undef, outfunc
=> $parser); };
7498 die "copy failed: $err" if $err;
7501 sub qemu_img_format
{
7502 my ($scfg, $volname) = @_;
7504 if ($scfg->{path
} && $volname =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7511 sub qemu_drive_mirror
{
7512 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
7514 $jobs = {} if !$jobs;
7518 $jobs->{"drive-$drive"} = {};
7520 if ($dst_volid =~ /^nbd:/) {
7521 $qemu_target = $dst_volid;
7524 my $storecfg = PVE
::Storage
::config
();
7525 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid);
7527 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7529 $format = qemu_img_format
($dst_scfg, $dst_volname);
7531 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7533 $qemu_target = $is_zero_initialized ?
"zeroinit:$dst_path" : $dst_path;
7536 my $opts = { timeout
=> 10, device
=> "drive-$drive", mode
=> "existing", sync
=> "full", target
=> $qemu_target };
7537 $opts->{format
} = $format if $format;
7539 if (defined($src_bitmap)) {
7540 $opts->{sync
} = 'incremental';
7541 $opts->{bitmap
} = $src_bitmap;
7542 print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
7545 if (defined($bwlimit)) {
7546 $opts->{speed
} = $bwlimit * 1024;
7547 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
7549 print "drive mirror is starting for drive-$drive\n";
7552 # if a job already runs for this device we get an error, catch it for cleanup
7553 eval { mon_cmd
($vmid, "drive-mirror", %$opts); };
7555 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7557 die "mirroring error: $err\n";
7560 qemu_drive_mirror_monitor
($vmid, $vmiddst, $jobs, $completion, $qga);
7563 # $completion can be either
7564 # 'complete': wait until all jobs are ready, block-job-complete them (default)
7565 # 'cancel': wait until all jobs are ready, block-job-cancel them
7566 # 'skip': wait until all jobs are ready, return with block jobs in ready state
7567 # 'auto': wait until all jobs disappear, only use for jobs which complete automatically
7568 sub qemu_drive_mirror_monitor
{
7569 my ($vmid, $vmiddst, $jobs, $completion, $qga, $op) = @_;
7571 $completion //= 'complete';
7575 my $err_complete = 0;
7577 my $starttime = time ();
7579 die "block job ('$op') timed out\n" if $err_complete > 300;
7581 my $stats = mon_cmd
($vmid, "query-block-jobs");
7584 my $running_jobs = {};
7585 for my $stat (@$stats) {
7586 next if $stat->{type
} ne $op;
7587 $running_jobs->{$stat->{device
}} = $stat;
7590 my $readycounter = 0;
7592 for my $job_id (sort keys %$jobs) {
7593 my $job = $running_jobs->{$job_id};
7595 my $vanished = !defined($job);
7596 my $complete = defined($jobs->{$job_id}->{complete
}) && $vanished;
7597 if($complete || ($vanished && $completion eq 'auto')) {
7598 print "$job_id: $op-job finished\n";
7599 delete $jobs->{$job_id};
7603 die "$job_id: '$op' has been cancelled\n" if !defined($job);
7605 my $busy = $job->{busy
};
7606 my $ready = $job->{ready
};
7607 if (my $total = $job->{len
}) {
7608 my $transferred = $job->{offset
} || 0;
7609 my $remaining = $total - $transferred;
7610 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
7612 my $duration = $ctime - $starttime;
7613 my $total_h = render_bytes
($total, 1);
7614 my $transferred_h = render_bytes
($transferred, 1);
7616 my $status = sprintf(
7617 "transferred $transferred_h of $total_h ($percent%%) in %s",
7618 render_duration
($duration),
7623 $status .= ", still busy"; # shouldn't even happen? but mirror is weird
7625 $status .= ", ready";
7628 print "$job_id: $status\n" if !$jobs->{$job_id}->{ready
};
7629 $jobs->{$job_id}->{ready
} = $ready;
7632 $readycounter++ if $job->{ready
};
7635 last if scalar(keys %$jobs) == 0;
7637 if ($readycounter == scalar(keys %$jobs)) {
7638 print "all '$op' jobs are ready\n";
7640 # do the complete later (or has already been done)
7641 last if $completion eq 'skip' || $completion eq 'auto';
7643 if ($vmiddst && $vmiddst != $vmid) {
7644 my $agent_running = $qga && qga_check_running
($vmid);
7645 if ($agent_running) {
7646 print "freeze filesystem\n";
7647 eval { mon_cmd
($vmid, "guest-fsfreeze-freeze"); };
7650 print "suspend vm\n";
7651 eval { PVE
::QemuServer
::vm_suspend
($vmid, 1); };
7655 # if we clone a disk for a new target vm, we don't switch the disk
7656 PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs);
7658 if ($agent_running) {
7659 print "unfreeze filesystem\n";
7660 eval { mon_cmd
($vmid, "guest-fsfreeze-thaw"); };
7663 print "resume vm\n";
7664 eval { PVE
::QemuServer
::vm_resume
($vmid, 1, 1); };
7671 for my $job_id (sort keys %$jobs) {
7672 # try to switch the disk if source and destination are on the same guest
7673 print "$job_id: Completing block job_id...\n";
7676 if ($completion eq 'complete') {
7677 $op = 'block-job-complete';
7678 } elsif ($completion eq 'cancel') {
7679 $op = 'block-job-cancel';
7681 die "invalid completion value: $completion\n";
7683 eval { mon_cmd
($vmid, $op, device
=> $job_id) };
7684 if ($@ =~ m/cannot be completed/) {
7685 print "$job_id: block job cannot be completed, trying again.\n";
7688 print "$job_id: Completed successfully.\n";
7689 $jobs->{$job_id}->{complete
} = 1;
7700 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7701 die "block job ($op) error: $err";
7705 sub qemu_blockjobs_cancel
{
7706 my ($vmid, $jobs) = @_;
7708 foreach my $job (keys %$jobs) {
7709 print "$job: Cancelling block job\n";
7710 eval { mon_cmd
($vmid, "block-job-cancel", device
=> $job); };
7711 $jobs->{$job}->{cancel
} = 1;
7715 my $stats = mon_cmd
($vmid, "query-block-jobs");
7717 my $running_jobs = {};
7718 foreach my $stat (@$stats) {
7719 $running_jobs->{$stat->{device
}} = $stat;
7722 foreach my $job (keys %$jobs) {
7724 if (defined($jobs->{$job}->{cancel
}) && !defined($running_jobs->{$job})) {
7725 print "$job: Done.\n";
7726 delete $jobs->{$job};
7730 last if scalar(keys %$jobs) == 0;
7737 my ($storecfg, $source, $dest, $full, $newvollist, $jobs, $completion, $qga, $bwlimit) = @_;
7739 my ($vmid, $running) = $source->@{qw(vmid running)};
7740 my ($src_drivename, $drive, $snapname) = $source->@{qw(drivename drive snapname)};
7742 my ($newvmid, $dst_drivename, $efisize) = $dest->@{qw(vmid drivename efisize)};
7743 my ($storage, $format) = $dest->@{qw(storage format)};
7745 my $use_drive_mirror = $full && $running && $src_drivename && !$snapname;
7747 if ($src_drivename && $dst_drivename && $src_drivename ne $dst_drivename) {
7748 die "cloning from/to EFI disk requires EFI disk\n"
7749 if $src_drivename eq 'efidisk0' || $dst_drivename eq 'efidisk0';
7750 die "cloning from/to TPM state requires TPM state\n"
7751 if $src_drivename eq 'tpmstate0' || $dst_drivename eq 'tpmstate0';
7753 # This would lead to two device nodes in QEMU pointing to the same backing image!
7754 die "cannot change drive name when cloning disk from/to the same VM\n"
7755 if $use_drive_mirror && $vmid == $newvmid;
7758 die "cannot move TPM state while VM is running\n"
7759 if $use_drive_mirror && $src_drivename eq 'tpmstate0';
7763 print "create " . ($full ?
'full' : 'linked') . " clone of drive ";
7764 print "$src_drivename " if $src_drivename;
7765 print "($drive->{file})\n";
7768 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $drive->{file
}, $newvmid, $snapname);
7769 push @$newvollist, $newvolid;
7772 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
7773 $storeid = $storage if $storage;
7775 my $dst_format = resolve_dst_disk_format
($storecfg, $storeid, $volname, $format);
7779 if (drive_is_cloudinit
($drive)) {
7780 $name = "vm-$newvmid-cloudinit";
7781 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7782 if ($scfg->{path
}) {
7783 $name .= ".$dst_format";
7786 $size = PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
;
7787 } elsif ($dst_drivename eq 'efidisk0') {
7788 $size = $efisize or die "internal error - need to specify EFI disk size\n";
7789 } elsif ($dst_drivename eq 'tpmstate0') {
7790 $dst_format = 'raw';
7791 $size = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
7793 ($size) = PVE
::Storage
::volume_size_info
($storecfg, $drive->{file
}, 10);
7795 $newvolid = PVE
::Storage
::vdisk_alloc
(
7796 $storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024)
7798 push @$newvollist, $newvolid;
7800 PVE
::Storage
::activate_volumes
($storecfg, [$newvolid]);
7802 if (drive_is_cloudinit
($drive)) {
7803 # when cloning multiple disks (e.g. during clone_vm) it might be the last disk
7804 # if this is the case, we have to complete any block-jobs still there from
7805 # previous drive-mirrors
7806 if (($completion eq 'complete') && (scalar(keys %$jobs) > 0)) {
7807 qemu_drive_mirror_monitor
($vmid, $newvmid, $jobs, $completion, $qga);
7812 my $sparseinit = PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $newvolid);
7813 if ($use_drive_mirror) {
7814 qemu_drive_mirror
($vmid, $src_drivename, $newvolid, $newvmid, $sparseinit, $jobs,
7815 $completion, $qga, $bwlimit);
7817 # TODO: handle bwlimits
7818 if ($dst_drivename eq 'efidisk0') {
7819 # the relevant data on the efidisk may be smaller than the source
7820 # e.g. on RBD/ZFS, so we use dd to copy only the amount
7821 # that is given by the OVMF_VARS.fd
7822 my $src_path = PVE
::Storage
::path
($storecfg, $drive->{file
}, $snapname);
7823 my $dst_path = PVE
::Storage
::path
($storecfg, $newvolid);
7825 my $src_format = (PVE
::Storage
::parse_volname
($storecfg, $drive->{file
}))[6];
7827 # better for Ceph if block size is not too small, see bug #3324
7830 my $cmd = ['qemu-img', 'dd', '-n', '-O', $dst_format];
7832 if ($src_format eq 'qcow2' && $snapname) {
7833 die "cannot clone qcow2 EFI disk snapshot - requires QEMU >= 6.2\n"
7834 if !min_version
(kvm_user_version
(), 6, 2);
7835 push $cmd->@*, '-l', $snapname;
7837 push $cmd->@*, "bs=$bs", "osize=$size", "if=$src_path", "of=$dst_path";
7840 qemu_img_convert
($drive->{file
}, $newvolid, $size, $snapname, $sparseinit);
7846 my ($size) = eval { PVE
::Storage
::volume_size_info
($storecfg, $newvolid, 10) };
7848 my $disk = dclone
($drive);
7849 delete $disk->{format
};
7850 $disk->{file
} = $newvolid;
7851 $disk->{size
} = $size if defined($size);
7856 sub get_running_qemu_version
{
7858 my $res = mon_cmd
($vmid, "query-version");
7859 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
7862 sub qemu_use_old_bios_files
{
7863 my ($machine_type) = @_;
7865 return if !$machine_type;
7867 my $use_old_bios_files = undef;
7869 if ($machine_type =~ m/^(\S+)\.pxe$/) {
7871 $use_old_bios_files = 1;
7873 my $version = extract_version
($machine_type, kvm_user_version
());
7874 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
7875 # load new efi bios files on migration. So this hack is required to allow
7876 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
7877 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
7878 $use_old_bios_files = !min_version
($version, 2, 4);
7881 return ($use_old_bios_files, $machine_type);
7884 sub get_efivars_size
{
7885 my ($conf, $efidisk) = @_;
7887 my $arch = get_vm_arch
($conf);
7888 $efidisk //= $conf->{efidisk0
} ? parse_drive
('efidisk0', $conf->{efidisk0
}) : undef;
7889 my $smm = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
7890 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
7891 die "uefi vars image '$ovmf_vars' not found\n" if ! -f
$ovmf_vars;
7892 return -s
$ovmf_vars;
7895 sub update_efidisk_size
{
7898 return if !defined($conf->{efidisk0
});
7900 my $disk = PVE
::QemuServer
::parse_drive
('efidisk0', $conf->{efidisk0
});
7901 $disk->{size
} = get_efivars_size
($conf);
7902 $conf->{efidisk0
} = print_drive
($disk);
7907 sub update_tpmstate_size
{
7910 my $disk = PVE
::QemuServer
::parse_drive
('tpmstate0', $conf->{tpmstate0
});
7911 $disk->{size
} = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
7912 $conf->{tpmstate0
} = print_drive
($disk);
7915 sub create_efidisk
($$$$$$$) {
7916 my ($storecfg, $storeid, $vmid, $fmt, $arch, $efidisk, $smm) = @_;
7918 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
7919 die "EFI vars default image not found\n" if ! -f
$ovmf_vars;
7921 my $vars_size_b = -s
$ovmf_vars;
7922 my $vars_size = PVE
::Tools
::convert_size
($vars_size_b, 'b' => 'kb');
7923 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
7924 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
7926 qemu_img_convert
($ovmf_vars, $volid, $vars_size_b, undef, 0);
7927 my ($size) = PVE
::Storage
::volume_size_info
($storecfg, $volid, 3);
7929 return ($volid, $size/1024);
7932 sub vm_iothreads_list
{
7935 my $res = mon_cmd
($vmid, 'query-iothreads');
7938 foreach my $iothread (@$res) {
7939 $iothreads->{ $iothread->{id
} } = $iothread->{"thread-id"};
7946 my ($conf, $drive) = @_;
7950 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)) {
7952 } elsif ($conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single')) {
7958 my $controller = int($drive->{index} / $maxdev);
7959 my $controller_prefix = ($conf->{scsihw
} && $conf->{scsihw
} eq 'virtio-scsi-single')
7963 return ($maxdev, $controller, $controller_prefix);
7966 sub resolve_dst_disk_format
{
7967 my ($storecfg, $storeid, $src_volname, $format) = @_;
7968 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
7971 # if no target format is specified, use the source disk format as hint
7973 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7974 $format = qemu_img_format
($scfg, $src_volname);
7980 # test if requested format is supported - else use default
7981 my $supported = grep { $_ eq $format } @$validFormats;
7982 $format = $defFormat if !$supported;
7986 # NOTE: if this logic changes, please update docs & possibly gui logic
7987 sub find_vmstate_storage
{
7988 my ($conf, $storecfg) = @_;
7990 # first, return storage from conf if set
7991 return $conf->{vmstatestorage
} if $conf->{vmstatestorage
};
7993 my ($target, $shared, $local);
7995 foreach_storage_used_by_vm
($conf, sub {
7997 my $scfg = PVE
::Storage
::storage_config
($storecfg, $sid);
7998 my $dst = $scfg->{shared
} ? \
$shared : \
$local;
7999 $$dst = $sid if !$$dst || $scfg->{path
}; # prefer file based storage
8002 # second, use shared storage where VM has at least one disk
8003 # third, use local storage where VM has at least one disk
8004 # fall back to local storage
8005 $target = $shared // $local // 'local';
8011 my ($uuid, $uuid_str);
8012 UUID
::generate
($uuid);
8013 UUID
::unparse
($uuid, $uuid_str);
8017 sub generate_smbios1_uuid
{
8018 return "uuid=".generate_uuid
();
8024 mon_cmd
($vmid, 'nbd-server-stop');
8027 sub create_reboot_request
{
8029 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
8030 or die "failed to create reboot trigger file: $!\n";
8034 sub clear_reboot_request
{
8036 my $path = "/run/qemu-server/$vmid.reboot";
8039 $res = unlink($path);
8040 die "could not remove reboot request for $vmid: $!"
8041 if !$res && $! != POSIX
::ENOENT
;
8046 sub bootorder_from_legacy
{
8047 my ($conf, $bootcfg) = @_;
8049 my $boot = $bootcfg->{legacy
} || $boot_fmt->{legacy
}->{default};
8050 my $bootindex_hash = {};
8052 foreach my $o (split(//, $boot)) {
8053 $bootindex_hash->{$o} = $i*100;
8059 PVE
::QemuConfig-
>foreach_volume($conf, sub {
8060 my ($ds, $drive) = @_;
8062 if (drive_is_cdrom
($drive, 1)) {
8063 if ($bootindex_hash->{d
}) {
8064 $bootorder->{$ds} = $bootindex_hash->{d
};
8065 $bootindex_hash->{d
} += 1;
8067 } elsif ($bootindex_hash->{c
}) {
8068 $bootorder->{$ds} = $bootindex_hash->{c
}
8069 if $conf->{bootdisk
} && $conf->{bootdisk
} eq $ds;
8070 $bootindex_hash->{c
} += 1;
8074 if ($bootindex_hash->{n
}) {
8075 for (my $i = 0; $i < $MAX_NETS; $i++) {
8076 my $netname = "net$i";
8077 next if !$conf->{$netname};
8078 $bootorder->{$netname} = $bootindex_hash->{n
};
8079 $bootindex_hash->{n
} += 1;
8086 # Generate default device list for 'boot: order=' property. Matches legacy
8087 # default boot order, but with explicit device names. This is important, since
8088 # the fallback for when neither 'order' nor the old format is specified relies
8089 # on 'bootorder_from_legacy' above, and it would be confusing if this diverges.
8090 sub get_default_bootdevices
{
8096 my $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 0);
8097 push @ret, $first if $first;
8100 $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 1);
8101 push @ret, $first if $first;
8104 for (my $i = 0; $i < $MAX_NETS; $i++) {
8105 my $netname = "net$i";
8106 next if !$conf->{$netname};
8107 push @ret, $netname;
8114 sub device_bootorder
{
8117 return bootorder_from_legacy
($conf) if !defined($conf->{boot
});
8119 my $boot = parse_property_string
($boot_fmt, $conf->{boot
});
8122 if (!defined($boot) || $boot->{legacy
}) {
8123 $bootorder = bootorder_from_legacy
($conf, $boot);
8124 } elsif ($boot->{order
}) {
8125 my $i = 100; # start at 100 to allow user to insert devices before us with -args
8126 for my $dev (PVE
::Tools
::split_list
($boot->{order
})) {
8127 $bootorder->{$dev} = $i++;
8134 sub register_qmeventd_handle
{
8138 my $peer = "/var/run/qmeventd.sock";
8143 $fh = IO
::Socket
::UNIX-
>new(Peer
=> $peer, Blocking
=> 0, Timeout
=> 1);
8145 if ($! != EINTR
&& $! != EAGAIN
) {
8146 die "unable to connect to qmeventd socket (vmid: $vmid) - $!\n";
8149 die "unable to connect to qmeventd socket (vmid: $vmid) - timeout "
8150 . "after $count retries\n";
8155 # send handshake to mark VM as backing up
8156 print $fh to_json
({vzdump
=> {vmid
=> "$vmid"}});
8158 # return handle to be closed later when inhibit is no longer required
8162 # bash completion helper
8164 sub complete_backup_archives
{
8165 my ($cmdname, $pname, $cvalue) = @_;
8167 my $cfg = PVE
::Storage
::config
();
8171 if ($cvalue =~ m/^([^:]+):/) {
8175 my $data = PVE
::Storage
::template_list
($cfg, $storeid, 'backup');
8178 foreach my $id (keys %$data) {
8179 foreach my $item (@{$data->{$id}}) {
8180 next if $item->{format
} !~ m/^vma\.(${\PVE::Storage::Plugin::COMPRESSOR_RE})$/;
8181 push @$res, $item->{volid
} if defined($item->{volid
});
8188 my $complete_vmid_full = sub {
8191 my $idlist = vmstatus
();
8195 foreach my $id (keys %$idlist) {
8196 my $d = $idlist->{$id};
8197 if (defined($running)) {
8198 next if $d->{template
};
8199 next if $running && $d->{status
} ne 'running';
8200 next if !$running && $d->{status
} eq 'running';
8209 return &$complete_vmid_full();
8212 sub complete_vmid_stopped
{
8213 return &$complete_vmid_full(0);
8216 sub complete_vmid_running
{
8217 return &$complete_vmid_full(1);
8220 sub complete_storage
{
8222 my $cfg = PVE
::Storage
::config
();
8223 my $ids = $cfg->{ids
};
8226 foreach my $sid (keys %$ids) {
8227 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, undef, 1);
8228 next if !$ids->{$sid}->{content
}->{images
};
8235 sub complete_migration_storage
{
8236 my ($cmd, $param, $current_value, $all_args) = @_;
8238 my $targetnode = @$all_args[1];
8240 my $cfg = PVE
::Storage
::config
();
8241 my $ids = $cfg->{ids
};
8244 foreach my $sid (keys %$ids) {
8245 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, $targetnode, 1);
8246 next if !$ids->{$sid}->{content
}->{images
};
8255 my $qmpstatus = eval {
8256 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid);
8257 mon_cmd
($vmid, "query-status");
8260 return $qmpstatus && $qmpstatus->{status
} eq "paused";
8263 sub check_volume_storage_type
{
8264 my ($storecfg, $vol) = @_;
8266 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($vol);
8267 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
8268 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $vol);
8270 die "storage '$storeid' does not support content-type '$vtype'\n"
8271 if !$scfg->{content
}->{$vtype};