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);
31 use PVE
::DataCenterConfig
;
32 use PVE
::Exception
qw(raise raise_param_exc);
33 use PVE
::Format
qw(render_duration render_bytes);
34 use PVE
::GuestHelpers
qw(safe_string_ne safe_num_ne safe_boolean_ne);
36 use PVE
::JSONSchema
qw(get_standard_option parse_property_string);
39 use PVE
::RPCEnvironment
;
43 use PVE
::Tools
qw(run_command file_read_firstline file_get_contents dir_glob_foreach get_host_arch $IPV6RE);
47 use PVE
::QemuServer
::Helpers
qw(min_version config_aware_timeout);
48 use PVE
::QemuServer
::Cloudinit
;
49 use PVE
::QemuServer
::CGroup
;
50 use PVE
::QemuServer
::CPUConfig
qw(print_cpu_device get_cpu_options);
51 use PVE
::QemuServer
::Drive
qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom drive_is_read_only parse_drive print_drive);
52 use PVE
::QemuServer
::Machine
;
53 use PVE
::QemuServer
::Memory
;
54 use PVE
::QemuServer
::Monitor
qw(mon_cmd);
55 use PVE
::QemuServer
::PCI
qw(print_pci_addr print_pcie_addr print_pcie_root_port parse_hostpci);
56 use PVE
::QemuServer
::USB
qw(parse_usb_device);
60 require PVE
::Network
::SDN
::Zones
;
64 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
68 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
69 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
72 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
73 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
76 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
77 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
80 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
81 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
84 "$EDK2_FW_BASE/OVMF_CODE.fd",
85 "$EDK2_FW_BASE/OVMF_VARS.fd",
90 "$EDK2_FW_BASE/AAVMF_CODE.fd",
91 "$EDK2_FW_BASE/AAVMF_VARS.fd",
96 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
98 # Note about locking: we use flock on the config file protect against concurent actions.
99 # Aditionaly, we have a 'lock' setting in the config file. This can be set to 'migrate',
100 # 'backup', 'snapshot' or 'rollback'. Most actions are not allowed when such lock is set.
101 # But you can ignore this kind of lock with the --skiplock flag.
103 cfs_register_file
('/qemu-server/',
107 PVE
::JSONSchema
::register_standard_option
('pve-qm-stateuri', {
108 description
=> "Some command save/restore state from this location.",
114 PVE
::JSONSchema
::register_standard_option
('pve-qemu-machine', {
115 description
=> "Specifies the Qemu machine type.",
117 pattern
=> '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
124 my ($map, $source) = @_;
126 return $source if !defined($map);
128 return $map->{entries
}->{$source}
129 if $map->{entries
} && defined($map->{entries
}->{$source});
131 return $map->{default} if $map->{default};
133 # identity (fallback)
137 PVE
::JSONSchema
::register_standard_option
('pve-targetstorage', {
138 description
=> "Mapping from source to target storages. Providing only a single storage ID maps all source storages to that storage. Providing the special value '1' will map each source storage to itself.",
140 format
=> 'storagepair-list',
144 #no warnings 'redefine';
148 $nodename_cache //= PVE
::INotify
::nodename
();
149 return $nodename_cache;
156 enum
=> [qw(i6300esb ib700)],
157 description
=> "Watchdog type to emulate.",
158 default => 'i6300esb',
163 enum
=> [qw(reset shutdown poweroff pause debug none)],
164 description
=> "The action to perform if after activation the guest fails to poll the watchdog in time.",
168 PVE
::JSONSchema
::register_format
('pve-qm-watchdog', $watchdog_fmt);
172 description
=> "Enable/disable communication with a Qemu Guest Agent (QGA) running in the VM.",
177 fstrim_cloned_disks
=> {
178 description
=> "Run fstrim after moving a disk or migrating the VM.",
184 description
=> "Select the agent type",
188 enum
=> [qw(virtio isa)],
194 description
=> "Select the VGA type.",
199 enum
=> [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio vmware)],
202 description
=> "Sets the VGA memory (in MiB). Has no effect with serial display.",
214 description
=> "The size of the file in MB.",
218 pattern
=> '[a-zA-Z0-9\-]+',
220 format_description
=> 'string',
221 description
=> "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
228 enum
=> [qw(ich9-intel-hda intel-hda AC97)],
229 description
=> "Configure an audio device."
233 enum
=> ['spice', 'none'],
236 description
=> "Driver backend for the audio device."
240 my $spice_enhancements_fmt = {
245 description
=> "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
249 enum
=> ['off', 'all', 'filter'],
252 description
=> "Enable video streaming. Uses compression for detected video streams."
259 enum
=> ['/dev/urandom', '/dev/random', '/dev/hwrng'],
261 description
=> "The file on the host to gather entropy from. In most cases '/dev/urandom'"
262 ." should be preferred over '/dev/random' to avoid entropy-starvation issues on the"
263 ." host. Using urandom does *not* decrease security in any meaningful way, as it's"
264 ." still seeded from real entropy, and the bytes provided will most likely be mixed"
265 ." with real entropy on the guest as well. '/dev/hwrng' can be used to pass through"
266 ." a hardware RNG from the host.",
270 description
=> "Maximum bytes of entropy allowed to get injected into the guest every"
271 ." 'period' milliseconds. Prefer a lower value when using '/dev/random' as source. Use"
272 ." `0` to disable limiting (potentially dangerous!).",
275 # default is 1 KiB/s, provides enough entropy to the guest to avoid boot-starvation issues
276 # (e.g. systemd etc...) while allowing no chance of overwhelming the host, provided we're
277 # reading from /dev/urandom
282 description
=> "Every 'period' milliseconds the entropy-injection quota is reset, allowing"
283 ." the guest to retrieve another 'max_bytes' of entropy.",
293 description
=> "Specifies whether a VM will be started during system bootup.",
299 description
=> "Automatic restart after crash (currently ignored).",
304 type
=> 'string', format
=> 'pve-hotplug-features',
305 description
=> "Selectively enable hotplug features. This is a comma separated list of"
306 ." hotplug features: 'network', 'disk', 'cpu', 'memory' and 'usb'. Use '0' to disable"
307 ." hotplug completely. Using '1' as value is an alias for the default `network,disk,usb`.",
308 default => 'network,disk,usb',
313 description
=> "Allow reboot. If set to '0' the VM exit on reboot.",
319 description
=> "Lock/unlock the VM.",
320 enum
=> [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
325 description
=> "Limit of CPU usage.",
326 verbose_description
=> "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has"
327 ." total of '2' CPU time. Value '0' indicates no CPU limit.",
335 description
=> "CPU weight for a VM, will be clamped to [1, 10000] in cgroup v2.",
336 verbose_description
=> "CPU weight for a VM. Argument is used in the kernel fair scheduler."
337 ." The larger the number is, the more CPU time this VM gets. Number is relative to"
338 ." weights of all the other running VMs.",
341 default => 'cgroup v1: 1024, cgroup v2: 100',
346 description
=> "Amount of RAM for the VM in MB. This is the maximum available memory when"
347 ." you use the balloon device.",
354 description
=> "Amount of target RAM for the VM in MB. Using zero disables the ballon driver.",
360 description
=> "Amount of memory shares for auto-ballooning. The larger the number is, the"
361 ." more memory this VM gets. Number is relative to weights of all other running VMs."
362 ." Using zero disables auto-ballooning. Auto-ballooning is done by pvestatd.",
370 description
=> "Keyboard layout for VNC server. The default is read from the"
371 ."'/etc/pve/datacenter.cfg' configuration file. It should not be necessary to set it.",
372 enum
=> PVE
::Tools
::kvmkeymaplist
(),
377 type
=> 'string', format
=> 'dns-name',
378 description
=> "Set a name for the VM. Only used on the configuration web interface.",
383 description
=> "SCSI controller model",
384 enum
=> [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
390 description
=> "Description for the VM. Shown in the web-interface VM's summary."
391 ." This is saved as comment inside the configuration file.",
392 maxLength
=> 1024 * 8,
397 enum
=> [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 win11 l24 l26 solaris)],
398 description
=> "Specify guest operating system.",
399 verbose_description
=> <<EODESC,
400 Specify guest operating system. This is used to enable special
401 optimization/features for specific operating systems:
404 other;; unspecified OS
405 wxp;; Microsoft Windows XP
406 w2k;; Microsoft Windows 2000
407 w2k3;; Microsoft Windows 2003
408 w2k8;; Microsoft Windows 2008
409 wvista;; Microsoft Windows Vista
410 win7;; Microsoft Windows 7
411 win8;; Microsoft Windows 8/2012/2012r2
412 win10;; Microsoft Windows 10/2016/2019
413 win11;; Microsoft Windows 11/2022
414 l24;; Linux 2.4 Kernel
415 l26;; Linux 2.6 - 5.X Kernel
416 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
421 type
=> 'string', format
=> 'pve-qm-boot',
422 description
=> "Specify guest boot order. Use the 'order=' sub-property as usage with no"
423 ." key or 'legacy=' is deprecated.",
427 type
=> 'string', format
=> 'pve-qm-bootdisk',
428 description
=> "Enable booting from specified disk. Deprecated: Use 'boot: order=foo;bar' instead.",
429 pattern
=> '(ide|sata|scsi|virtio)\d+',
434 description
=> "The number of CPUs. Please use option -sockets instead.",
441 description
=> "The number of CPU sockets.",
448 description
=> "The number of cores per socket.",
455 description
=> "Enable/disable NUMA.",
461 description
=> "Enable/disable hugepages memory.",
462 enum
=> [qw(any 2 1024)],
468 description
=> "Use together with hugepages. If enabled, hugepages will not not be deleted"
469 ." after VM shutdown and can be used for subsequent starts.",
474 description
=> "Number of hotplugged vcpus.",
481 description
=> "Enable/disable ACPI.",
486 description
=> "Enable/disable communication with the Qemu Guest Agent and its properties.",
488 format
=> $agent_fmt,
493 description
=> "Enable/disable KVM hardware virtualization.",
499 description
=> "Enable/disable time drift fix.",
505 description
=> "Set the real time clock (RTC) to local time. This is enabled by default if"
506 ." the `ostype` indicates a Microsoft Windows OS.",
511 description
=> "Freeze CPU at startup (use 'c' monitor command to start execution).",
515 type
=> 'string', format
=> $vga_fmt,
516 description
=> "Configure the VGA hardware.",
517 verbose_description
=> "Configure the VGA Hardware. If you want to use high resolution"
518 ." modes (>= 1280x1024x16) you may need to increase the vga memory option. Since QEMU"
519 ." 2.9 the default VGA display type is 'std' for all OS types besides some Windows"
520 ." versions (XP and older) which use 'cirrus'. The 'qxl' option enables the SPICE"
521 ." display server. For win* OS you can select how many independent displays you want,"
522 ." Linux guests can add displays them self.\nYou can also run without any graphic card,"
523 ." using a serial device as terminal.",
527 type
=> 'string', format
=> 'pve-qm-watchdog',
528 description
=> "Create a virtual hardware watchdog device.",
529 verbose_description
=> "Create a virtual hardware watchdog device. Once enabled (by a guest"
530 ." action), the watchdog must be periodically polled by an agent inside the guest or"
531 ." else the watchdog will reset the guest (or execute the respective action specified)",
536 typetext
=> "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
537 description
=> "Set the initial date of the real time clock. Valid format for date are:"
538 ."'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
539 pattern
=> '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
542 startup
=> get_standard_option
('pve-startup-order'),
546 description
=> "Enable/disable Template.",
552 description
=> "Arbitrary arguments passed to kvm.",
553 verbose_description
=> <<EODESCR,
554 Arbitrary arguments passed to kvm, for example:
556 args: -no-reboot -no-hpet
558 NOTE: this option is for experts only.
565 description
=> "Enable/disable the USB tablet device.",
566 verbose_description
=> "Enable/disable the USB tablet device. This device is usually needed"
567 ." to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with"
568 ." normal VNC clients. If you're running lots of console-only guests on one host, you"
569 ." may consider disabling this to save some context switches. This is turned off by"
570 ." default if you use spice (`qm set <vmid> --vga qxl`).",
575 description
=> "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
579 migrate_downtime
=> {
582 description
=> "Set maximum tolerated downtime (in seconds) for migrations.",
588 type
=> 'string', format
=> 'pve-qm-ide',
589 typetext
=> '<volume>',
590 description
=> "This is an alias for option -ide2",
594 description
=> "Emulated CPU type.",
596 format
=> 'pve-vm-cpu-conf',
598 parent
=> get_standard_option
('pve-snapshot-name', {
600 description
=> "Parent snapshot name. This is used internally, and should not be modified.",
604 description
=> "Timestamp for snapshots.",
610 type
=> 'string', format
=> 'pve-volume-id',
611 description
=> "Reference to a volume which stores the VM state. This is used internally"
614 vmstatestorage
=> get_standard_option
('pve-storage-id', {
615 description
=> "Default storage for VM state volumes/files.",
618 runningmachine
=> get_standard_option
('pve-qemu-machine', {
619 description
=> "Specifies the QEMU machine type of the running vm. This is used internally"
623 description
=> "Specifies the QEMU '-cpu' parameter of the running vm. This is used"
624 ." internally for snapshots.",
627 pattern
=> $PVE::QemuServer
::CPUConfig
::qemu_cmdline_cpu_re
,
628 format_description
=> 'QEMU -cpu parameter'
630 machine
=> get_standard_option
('pve-qemu-machine'),
632 description
=> "Virtual processor architecture. Defaults to the host.",
635 enum
=> [qw(x86_64 aarch64)],
638 description
=> "Specify SMBIOS type 1 fields.",
639 type
=> 'string', format
=> 'pve-qm-smbios1',
646 description
=> "Sets the protection flag of the VM. This will disable the remove VM and"
647 ." remove disk operations.",
653 enum
=> [ qw(seabios ovmf) ],
654 description
=> "Select BIOS implementation.",
655 default => 'seabios',
659 pattern
=> '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
660 format_description
=> 'UUID',
661 description
=> "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0'"
662 ." to disable explicitly.",
663 verbose_description
=> "The VM generation ID (vmgenid) device exposes a 128-bit integer"
664 ." value identifier to the guest OS. This allows to notify the guest operating system"
665 ." when the virtual machine is executed with a different configuration (e.g. snapshot"
666 ." execution or creation from a template). The guest operating system notices the"
667 ." change, and is then able to react as appropriate by marking its copies of"
668 ." distributed databases as dirty, re-initializing its random number generator, etc.\n"
669 ."Note that auto-creation only works when done through API/CLI create or update methods"
670 .", but not when manually editing the config file.",
671 default => "1 (autogenerated)",
676 format
=> 'pve-volume-id',
678 description
=> "Script that will be executed during various steps in the vms lifetime.",
682 format
=> $ivshmem_fmt,
683 description
=> "Inter-VM shared memory. Useful for direct communication between VMs, or to"
689 format
=> $audio_fmt,
690 description
=> "Configure a audio device, useful in combination with QXL/Spice.",
693 spice_enhancements
=> {
695 format
=> $spice_enhancements_fmt,
696 description
=> "Configure additional enhancements for SPICE.",
700 type
=> 'string', format
=> 'pve-tag-list',
701 description
=> 'Tags of the VM. This is only meta information.',
707 description
=> "Configure a VirtIO-based Random Number Generator.",
716 description
=> 'Specify a custom file containing all meta data passed to the VM via"
717 ." cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
718 format
=> 'pve-volume-id',
719 format_description
=> 'volume',
724 description
=> 'Specify a custom file containing all network data passed to the VM via'
726 format
=> 'pve-volume-id',
727 format_description
=> 'volume',
732 description
=> 'Specify a custom file containing all user data passed to the VM via'
734 format
=> 'pve-volume-id',
735 format_description
=> 'volume',
740 description
=> 'Specify a custom file containing all vendor data passed to the VM via'
742 format
=> 'pve-volume-id',
743 format_description
=> 'volume',
746 PVE
::JSONSchema
::register_format
('pve-qm-cicustom', $cicustom_fmt);
748 my $confdesc_cloudinit = {
752 description
=> 'Specifies the cloud-init configuration format. The default depends on the'
753 .' configured operating system type (`ostype`. We use the `nocloud` format for Linux,'
754 .' and `configdrive2` for windows.',
755 enum
=> ['configdrive2', 'nocloud', 'opennebula'],
760 description
=> "cloud-init: User name to change ssh keys and password for instead of the"
761 ." image's configured default user.",
766 description
=> 'cloud-init: Password to assign the user. Using this is generally not'
767 .' recommended. Use ssh keys instead. Also note that older cloud-init versions do not'
768 .' support hashed passwords.',
773 description
=> 'cloud-init: Specify custom files to replace the automatically generated'
775 format
=> 'pve-qm-cicustom',
780 description
=> "cloud-init: Sets DNS search domains for a container. Create will'
781 .' automatically use the setting from the host if neither searchdomain nor nameserver'
786 type
=> 'string', format
=> 'address-list',
787 description
=> "cloud-init: Sets DNS server IP address for a container. Create will'
788 .' automatically use the setting from the host if neither searchdomain nor nameserver'
794 format
=> 'urlencoded',
795 description
=> "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
799 # what about other qemu settings ?
801 #machine => 'string',
814 ##soundhw => 'string',
816 while (my ($k, $v) = each %$confdesc) {
817 PVE
::JSONSchema
::register_standard_option
("pve-qm-$k", $v);
820 my $MAX_USB_DEVICES = 5;
822 my $MAX_SERIAL_PORTS = 4;
823 my $MAX_PARALLEL_PORTS = 3;
829 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
830 description
=> "CPUs accessing this NUMA node.",
831 format_description
=> "id[-id];...",
835 description
=> "Amount of memory this NUMA node provides.",
840 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
841 description
=> "Host NUMA nodes to use.",
842 format_description
=> "id[-id];...",
847 enum
=> [qw(preferred bind interleave)],
848 description
=> "NUMA allocation policy.",
852 PVE
::JSONSchema
::register_format
('pve-qm-numanode', $numa_fmt);
855 type
=> 'string', format
=> $numa_fmt,
856 description
=> "NUMA topology.",
858 PVE
::JSONSchema
::register_standard_option
("pve-qm-numanode", $numadesc);
860 for (my $i = 0; $i < $MAX_NUMA; $i++) {
861 $confdesc->{"numa$i"} = $numadesc;
864 my $nic_model_list = [
880 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
882 my $net_fmt_bridge_descr = <<__EOD__;
883 Bridge to attach the network device to. The Proxmox VE standard bridge
886 If you do not specify a bridge, we create a kvm user (NATed) network
887 device, which provides DHCP and DNS services. The following addresses
894 The DHCP server assign addresses to the guest starting from 10.0.2.15.
898 macaddr
=> get_standard_option
('mac-addr', {
899 description
=> "MAC address. That address must be unique withing your network. This is"
900 ." automatically generated if not specified.",
904 description
=> "Network Card Model. The 'virtio' model provides the best performance with"
905 ." very low CPU overhead. If your guest does not support this driver, it is usually"
906 ." best to use 'e1000'.",
907 enum
=> $nic_model_list,
910 (map { $_ => { keyAlias
=> 'model', alias
=> 'macaddr' }} @$nic_model_list),
913 description
=> $net_fmt_bridge_descr,
914 format_description
=> 'bridge',
915 pattern
=> '[-_.\w\d]+',
920 minimum
=> 0, maximum
=> 16,
921 description
=> 'Number of packet queues to be used on the device.',
927 description
=> "Rate limit in mbps (megabytes per second) as floating point number.",
932 minimum
=> 1, maximum
=> 4094,
933 description
=> 'VLAN tag to apply to packets on this interface.',
938 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
939 description
=> 'VLAN trunks to pass through this interface.',
940 format_description
=> 'vlanid[;vlanid...]',
945 description
=> 'Whether this interface should be protected by the firewall.',
950 description
=> 'Whether this interface should be disconnected (like pulling the plug).',
955 minimum
=> 1, maximum
=> 65520,
956 description
=> "Force MTU, for VirtIO only. Set to '1' to use the bridge MTU",
963 type
=> 'string', format
=> $net_fmt,
964 description
=> "Specify network devices.",
967 PVE
::JSONSchema
::register_standard_option
("pve-qm-net", $netdesc);
972 format
=> 'pve-ipv4-config',
973 format_description
=> 'IPv4Format/CIDR',
974 description
=> 'IPv4 address in CIDR format.',
981 format_description
=> 'GatewayIPv4',
982 description
=> 'Default gateway for IPv4 traffic.',
988 format
=> 'pve-ipv6-config',
989 format_description
=> 'IPv6Format/CIDR',
990 description
=> 'IPv6 address in CIDR format.',
997 format_description
=> 'GatewayIPv6',
998 description
=> 'Default gateway for IPv6 traffic.',
1003 PVE
::JSONSchema
::register_format
('pve-qm-ipconfig', $ipconfig_fmt);
1004 my $ipconfigdesc = {
1006 type
=> 'string', format
=> 'pve-qm-ipconfig',
1007 description
=> <<'EODESCR',
1008 cloud-init: Specify IP addresses and gateways for the corresponding interface.
1010 IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
1012 The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit
1013 gateway should be provided.
1014 For IPv6 the special string 'auto' can be used to use stateless autoconfiguration. This requires
1015 cloud-init 19.4 or newer.
1017 If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using
1021 PVE
::JSONSchema
::register_standard_option
("pve-qm-ipconfig", $netdesc);
1023 for (my $i = 0; $i < $MAX_NETS; $i++) {
1024 $confdesc->{"net$i"} = $netdesc;
1025 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
1028 foreach my $key (keys %$confdesc_cloudinit) {
1029 $confdesc->{$key} = $confdesc_cloudinit->{$key};
1032 PVE
::JSONSchema
::register_format
('pve-volume-id-or-qm-path', \
&verify_volume_id_or_qm_path
);
1033 sub verify_volume_id_or_qm_path
{
1034 my ($volid, $noerr) = @_;
1036 if ($volid eq 'none' || $volid eq 'cdrom' || $volid =~ m
|^/|) {
1040 # if its neither 'none' nor 'cdrom' nor a path, check if its a volume-id
1041 $volid = eval { PVE
::JSONSchema
::check_format
('pve-volume-id', $volid, '') };
1052 type
=> 'string', format
=> 'pve-qm-usb-device',
1053 format_description
=> 'HOSTUSBDEVICE|spice',
1054 description
=> <<EODESCR,
1055 The Host USB device or port or the value 'spice'. HOSTUSBDEVICE syntax is:
1057 'bus-port(.port)*' (decimal numbers) or
1058 'vendor_id:product_id' (hexadeciaml numbers) or
1061 You can use the 'lsusb -t' command to list existing usb devices.
1063 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1064 machines - use with special care.
1066 The value 'spice' can be used to add a usb redirection devices for spice.
1072 description
=> "Specifies whether if given host option is a USB3 device or port.",
1079 type
=> 'string', format
=> $usb_fmt,
1080 description
=> "Configure an USB device (n is 0 to 4).",
1082 PVE
::JSONSchema
::register_standard_option
("pve-qm-usb", $usbdesc);
1087 pattern
=> '(/dev/.+|socket)',
1088 description
=> "Create a serial device inside the VM (n is 0 to 3)",
1089 verbose_description
=> <<EODESCR,
1090 Create a serial device inside the VM (n is 0 to 3), and pass through a
1091 host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1092 host side (use 'qm terminal' to open a terminal connection).
1094 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines -
1095 use with special care.
1097 CAUTION: Experimental! User reported problems with this option.
1104 pattern
=> '/dev/parport\d+|/dev/usb/lp\d+',
1105 description
=> "Map host parallel devices (n is 0 to 2).",
1106 verbose_description
=> <<EODESCR,
1107 Map host parallel devices (n is 0 to 2).
1109 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1110 machines - use with special care.
1112 CAUTION: Experimental! User reported problems with this option.
1116 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1117 $confdesc->{"parallel$i"} = $paralleldesc;
1120 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1121 $confdesc->{"serial$i"} = $serialdesc;
1124 for (my $i = 0; $i < $PVE::QemuServer
::PCI
::MAX_HOSTPCI_DEVICES
; $i++) {
1125 $confdesc->{"hostpci$i"} = $PVE::QemuServer
::PCI
::hostpcidesc
;
1128 for my $key (keys %{$PVE::QemuServer
::Drive
::drivedesc_hash
}) {
1129 $confdesc->{$key} = $PVE::QemuServer
::Drive
::drivedesc_hash-
>{$key};
1132 for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
1133 $confdesc->{"usb$i"} = $usbdesc;
1141 description
=> "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)."
1142 . " Deprecated, use 'order=' instead.",
1143 pattern
=> '[acdn]{1,4}',
1144 format_description
=> "[acdn]{1,4}",
1146 # note: this is also the fallback if boot: is not given at all
1152 format
=> 'pve-qm-bootdev-list',
1153 format_description
=> "device[;device...]",
1154 description
=> <<EODESC,
1155 The guest will attempt to boot from devices in the order they appear here.
1157 Disks, optical drives and passed-through storage USB devices will be directly
1158 booted from, NICs will load PXE, and PCIe devices will either behave like disks
1159 (e.g. NVMe) or load an option ROM (e.g. RAID controller, hardware NIC).
1161 Note that only devices in this list will be marked as bootable and thus loaded
1162 by the guest firmware (BIOS/UEFI). If you require multiple disks for booting
1163 (e.g. software-raid), you need to specify all of them here.
1165 Overrides the deprecated 'legacy=[acdn]*' value when given.
1169 PVE
::JSONSchema
::register_format
('pve-qm-boot', $boot_fmt);
1171 PVE
::JSONSchema
::register_format
('pve-qm-bootdev', \
&verify_bootdev
);
1172 sub verify_bootdev
{
1173 my ($dev, $noerr) = @_;
1175 my $special = $dev =~ m/^efidisk/ || $dev =~ m/^tpmstate/;
1176 return $dev if PVE
::QemuServer
::Drive
::is_valid_drivename
($dev) && !$special;
1180 return 0 if $dev !~ m/^$base\d+$/;
1181 return 0 if !$confdesc->{$dev};
1185 return $dev if $check->("net");
1186 return $dev if $check->("usb");
1187 return $dev if $check->("hostpci");
1190 die "invalid boot device '$dev'\n";
1193 sub print_bootorder
{
1195 return "" if !@$devs;
1196 my $data = { order
=> join(';', @$devs) };
1197 return PVE
::JSONSchema
::print_property_string
($data, $boot_fmt);
1200 my $kvm_api_version = 0;
1203 return $kvm_api_version if $kvm_api_version;
1205 open my $fh, '<', '/dev/kvm' or return;
1207 # 0xae00 => KVM_GET_API_VERSION
1208 $kvm_api_version = ioctl($fh, 0xae00, 0);
1211 return $kvm_api_version;
1214 my $kvm_user_version = {};
1217 sub kvm_user_version
{
1220 $binary //= get_command_for_arch
(get_host_arch
()); # get the native arch by default
1221 my $st = stat($binary);
1223 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1224 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1225 $cachedmtime == $st->mtime;
1227 $kvm_user_version->{$binary} = 'unknown';
1228 $kvm_mtime->{$binary} = $st->mtime;
1232 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1233 $kvm_user_version->{$binary} = $2;
1237 eval { run_command
([$binary, '--version'], outfunc
=> $code); };
1240 return $kvm_user_version->{$binary};
1243 my sub extract_version
{
1244 my ($machine_type, $version) = @_;
1245 $version = kvm_user_version
() if !defined($version);
1246 return PVE
::QemuServer
::Machine
::extract_version
($machine_type, $version)
1249 sub kernel_has_vhost_net
{
1250 return -c
'/dev/vhost-net';
1255 return defined($confdesc->{$key});
1259 sub get_cdrom_path
{
1261 return $cdrom_path if $cdrom_path;
1263 return $cdrom_path = "/dev/cdrom" if -l
"/dev/cdrom";
1264 return $cdrom_path = "/dev/cdrom1" if -l
"/dev/cdrom1";
1265 return $cdrom_path = "/dev/cdrom2" if -l
"/dev/cdrom2";
1269 my ($storecfg, $vmid, $cdrom) = @_;
1271 if ($cdrom eq 'cdrom') {
1272 return get_cdrom_path
();
1273 } elsif ($cdrom eq 'none') {
1275 } elsif ($cdrom =~ m
|^/|) {
1278 return PVE
::Storage
::path
($storecfg, $cdrom);
1282 # try to convert old style file names to volume IDs
1283 sub filename_to_volume_id
{
1284 my ($vmid, $file, $media) = @_;
1286 if (!($file eq 'none' || $file eq 'cdrom' ||
1287 $file =~ m
|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1289 return if $file =~ m
|/|;
1291 if ($media && $media eq 'cdrom') {
1292 $file = "local:iso/$file";
1294 $file = "local:$vmid/$file";
1301 sub verify_media_type
{
1302 my ($opt, $vtype, $media) = @_;
1307 if ($media eq 'disk') {
1309 } elsif ($media eq 'cdrom') {
1312 die "internal error";
1315 return if ($vtype eq $etype);
1317 raise_param_exc
({ $opt => "unexpected media type ($vtype != $etype)" });
1320 sub cleanup_drive_path
{
1321 my ($opt, $storecfg, $drive) = @_;
1323 # try to convert filesystem paths to volume IDs
1325 if (($drive->{file
} !~ m/^(cdrom|none)$/) &&
1326 ($drive->{file
} !~ m
|^/dev/.+|) &&
1327 ($drive->{file
} !~ m/^([^:]+):(.+)$/) &&
1328 ($drive->{file
} !~ m/^\d+$/)) {
1329 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($storecfg, $drive->{file
});
1330 raise_param_exc
({ $opt => "unable to associate path '$drive->{file}' to any storage"})
1332 $drive->{media
} = 'cdrom' if !$drive->{media
} && $vtype eq 'iso';
1333 verify_media_type
($opt, $vtype, $drive->{media
});
1334 $drive->{file
} = $volid;
1337 $drive->{media
} = 'cdrom' if !$drive->{media
} && $drive->{file
} =~ m/^(cdrom|none)$/;
1340 sub parse_hotplug_features
{
1345 return $res if $data eq '0';
1347 $data = $confdesc->{hotplug
}->{default} if $data eq '1';
1349 foreach my $feature (PVE
::Tools
::split_list
($data)) {
1350 if ($feature =~ m/^(network|disk|cpu|memory|usb)$/) {
1353 die "invalid hotplug feature '$feature'\n";
1359 PVE
::JSONSchema
::register_format
('pve-hotplug-features', \
&pve_verify_hotplug_features
);
1360 sub pve_verify_hotplug_features
{
1361 my ($value, $noerr) = @_;
1363 return $value if parse_hotplug_features
($value);
1367 die "unable to parse hotplug option\n";
1371 my($fh, $noerr) = @_;
1374 my $SG_GET_VERSION_NUM = 0x2282;
1376 my $versionbuf = "\x00" x
8;
1377 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1379 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
1382 my $version = unpack("I", $versionbuf);
1383 if ($version < 30000) {
1384 die "scsi generic interface too old\n" if !$noerr;
1388 my $buf = "\x00" x
36;
1389 my $sensebuf = "\x00" x
8;
1390 my $cmd = pack("C x3 C x1", 0x12, 36);
1392 # see /usr/include/scsi/sg.h
1393 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";
1396 $sg_io_hdr_t, ord('S'), -3, length($cmd), length($sensebuf), 0, length($buf), $buf, $cmd, $sensebuf, 6000
1399 $ret = ioctl($fh, $SG_IO, $packet);
1401 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
1405 my @res = unpack($sg_io_hdr_t, $packet);
1406 if ($res[17] || $res[18]) {
1407 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
1412 $res->@{qw(type removable vendor product revision)} = unpack("C C x6 A8 A16 A4", $buf);
1414 $res->{removable
} = $res->{removable
} & 128 ?
1 : 0;
1415 $res->{type
} &= 0x1F;
1423 my $fh = IO
::File-
>new("+<$path") || return;
1424 my $res = scsi_inquiry
($fh, 1);
1430 sub print_tabletdevice_full
{
1431 my ($conf, $arch) = @_;
1433 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1435 # we use uhci for old VMs because tablet driver was buggy in older qemu
1437 if (PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf) || $arch eq 'aarch64') {
1443 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1446 sub print_keyboarddevice_full
{
1447 my ($conf, $arch) = @_;
1449 return if $arch ne 'aarch64';
1451 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1454 my sub get_drive_id
{
1456 return "$drive->{interface}$drive->{index}";
1459 sub print_drivedevice_full
{
1460 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1465 my $drive_id = get_drive_id
($drive);
1466 if ($drive->{interface
} eq 'virtio') {
1467 my $pciaddr = print_pci_addr
("$drive_id", $bridges, $arch, $machine_type);
1468 $device = "virtio-blk-pci,drive=drive-$drive_id,id=${drive_id}${pciaddr}";
1469 $device .= ",iothread=iothread-$drive_id" if $drive->{iothread
};
1470 } elsif ($drive->{interface
} eq 'scsi') {
1472 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
1473 my $unit = $drive->{index} % $maxdev;
1474 my $devicetype = 'hd';
1476 if (drive_is_cdrom
($drive)) {
1479 if ($drive->{file
} =~ m
|^/|) {
1480 $path = $drive->{file
};
1481 if (my $info = path_is_scsi
($path)) {
1482 if ($info->{type
} == 0 && $drive->{scsiblock
}) {
1483 $devicetype = 'block';
1484 } elsif ($info->{type
} == 1) { # tape
1485 $devicetype = 'generic';
1489 $path = PVE
::Storage
::path
($storecfg, $drive->{file
});
1492 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1493 my $version = extract_version
($machine_type, kvm_user_version
());
1494 if ($path =~ m/^iscsi\:\/\
// &&
1495 !min_version
($version, 4, 1)) {
1496 $devicetype = 'generic';
1500 if (!$conf->{scsihw
} || $conf->{scsihw
} =~ m/^lsi/ || $conf->{scsihw
} eq 'pvscsi') {
1501 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
1503 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
1504 .",lun=$drive->{index}";
1506 $device .= ",drive=drive-$drive_id,id=$drive_id";
1508 if ($drive->{ssd
} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1509 $device .= ",rotation_rate=1";
1511 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1513 } elsif ($drive->{interface
} eq 'ide' || $drive->{interface
} eq 'sata') {
1514 my $maxdev = ($drive->{interface
} eq 'sata') ?
$PVE::QemuServer
::Drive
::MAX_SATA_DISKS
: 2;
1515 my $controller = int($drive->{index} / $maxdev);
1516 my $unit = $drive->{index} % $maxdev;
1517 my $devicetype = ($drive->{media
} && $drive->{media
} eq 'cdrom') ?
"cd" : "hd";
1519 $device = "ide-$devicetype";
1520 if ($drive->{interface
} eq 'ide') {
1521 $device .= ",bus=ide.$controller,unit=$unit";
1523 $device .= ",bus=ahci$controller.$unit";
1525 $device .= ",drive=drive-$drive_id,id=$drive_id";
1527 if ($devicetype eq 'hd') {
1528 if (my $model = $drive->{model
}) {
1529 $model = URI
::Escape
::uri_unescape
($model);
1530 $device .= ",model=$model";
1532 if ($drive->{ssd
}) {
1533 $device .= ",rotation_rate=1";
1536 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1537 } elsif ($drive->{interface
} eq 'usb') {
1539 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1541 die "unsupported interface type";
1544 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex
};
1546 if (my $serial = $drive->{serial
}) {
1547 $serial = URI
::Escape
::uri_unescape
($serial);
1548 $device .= ",serial=$serial";
1555 sub get_initiator_name
{
1558 my $fh = IO
::File-
>new('/etc/iscsi/initiatorname.iscsi') || return;
1559 while (defined(my $line = <$fh>)) {
1560 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1569 sub print_drive_commandline_full
{
1570 my ($storecfg, $vmid, $drive, $pbs_name, $io_uring) = @_;
1573 my $volid = $drive->{file
};
1574 my $format = $drive->{format
};
1575 my $drive_id = get_drive_id
($drive);
1577 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1578 my $scfg = $storeid ? PVE
::Storage
::storage_config
($storecfg, $storeid) : undef;
1580 if (drive_is_cdrom
($drive)) {
1581 $path = get_iso_path
($storecfg, $vmid, $volid);
1582 die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
1585 $path = PVE
::Storage
::path
($storecfg, $volid);
1586 $format //= qemu_img_format
($scfg, $volname);
1593 my $is_rbd = $path =~ m/^rbd:/;
1596 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1597 foreach my $o (@qemu_drive_options) {
1598 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1601 # snapshot only accepts on|off
1602 if (defined($drive->{snapshot
})) {
1603 my $v = $drive->{snapshot
} ?
'on' : 'off';
1604 $opts .= ",snapshot=$v";
1607 if (defined($drive->{ro
})) { # ro maps to QEMUs `readonly`, which accepts `on` or `off` only
1608 $opts .= ",readonly=" . ($drive->{ro
} ?
'on' : 'off');
1611 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1612 my ($dir, $qmpname) = @$type;
1613 if (my $v = $drive->{"mbps$dir"}) {
1614 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1616 if (my $v = $drive->{"mbps${dir}_max"}) {
1617 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1619 if (my $v = $drive->{"bps${dir}_max_length"}) {
1620 $opts .= ",throttling.bps$qmpname-max-length=$v";
1622 if (my $v = $drive->{"iops${dir}"}) {
1623 $opts .= ",throttling.iops$qmpname=$v";
1625 if (my $v = $drive->{"iops${dir}_max"}) {
1626 $opts .= ",throttling.iops$qmpname-max=$v";
1628 if (my $v = $drive->{"iops${dir}_max_length"}) {
1629 $opts .= ",throttling.iops$qmpname-max-length=$v";
1634 $format = "rbd" if $is_rbd;
1635 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1637 $opts .= ",format=alloc-track,file.driver=$format";
1639 $opts .= ",format=$format";
1642 my $cache_direct = 0;
1644 if (my $cache = $drive->{cache
}) {
1645 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1646 } elsif (!drive_is_cdrom
($drive) && !($scfg && $scfg->{type
} eq 'btrfs' && !$scfg->{nocow
})) {
1647 $opts .= ",cache=none";
1651 # io_uring with cache mode writeback or writethrough on krbd will hang...
1652 my $rbd_no_io_uring = $scfg && $scfg->{type
} eq 'rbd' && $scfg->{krbd
} && !$cache_direct;
1654 # io_uring with cache mode writeback or writethrough on LVM will hang, without cache only
1655 # sometimes, just plain disable...
1656 my $lvm_no_io_uring = $scfg && $scfg->{type
} eq 'lvm';
1658 if (!$drive->{aio
}) {
1659 if ($io_uring && !$rbd_no_io_uring && !$lvm_no_io_uring) {
1660 # io_uring supports all cache modes
1661 $opts .= ",aio=io_uring";
1663 # aio native works only with O_DIRECT
1665 $opts .= ",aio=native";
1667 $opts .= ",aio=threads";
1672 if (!drive_is_cdrom
($drive)) {
1674 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
1675 $detectzeroes = 'off';
1676 } elsif ($drive->{discard
}) {
1677 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
1679 # This used to be our default with discard not being specified:
1680 $detectzeroes = 'on';
1683 # note: 'detect-zeroes' works per blockdev and we want it to persist
1684 # after the alloc-track is removed, so put it on 'file' directly
1685 my $dz_param = $pbs_name ?
"file.detect-zeroes" : "detect-zeroes";
1686 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
1690 $opts .= ",backing=$pbs_name";
1691 $opts .= ",auto-remove=on";
1694 # my $file_param = $pbs_name ? "file.file.filename" : "file";
1695 my $file_param = "file";
1697 # non-rbd drivers require the underlying file to be a seperate block
1698 # node, so add a second .file indirection
1699 $file_param .= ".file" if !$is_rbd;
1700 $file_param .= ".filename";
1702 my $pathinfo = $path ?
"$file_param=$path," : '';
1704 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1707 sub print_pbs_blockdev
{
1708 my ($pbs_conf, $pbs_name) = @_;
1709 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1710 $blockdev .= ",repository=$pbs_conf->{repository}";
1711 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1712 $blockdev .= ",archive=$pbs_conf->{archive}";
1713 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile
};
1717 sub print_netdevice_full
{
1718 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type) = @_;
1720 my $device = $net->{model
};
1721 if ($net->{model
} eq 'virtio') {
1722 $device = 'virtio-net-pci';
1725 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
1726 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
1727 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
1728 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1729 # and out of each queue plus one config interrupt and control vector queue
1730 my $vectors = $net->{queues
} * 2 + 2;
1731 $tmpstr .= ",vectors=$vectors,mq=on";
1733 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
1735 if (my $mtu = $net->{mtu
}) {
1736 if ($net->{model
} eq 'virtio' && $net->{bridge
}) {
1737 my $bridge_mtu = PVE
::Network
::read_bridge_mtu
($net->{bridge
});
1740 } elsif ($mtu < 576) {
1741 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1742 } elsif ($mtu > $bridge_mtu) {
1743 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1745 $tmpstr .= ",host_mtu=$mtu";
1747 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
1751 if ($use_old_bios_files) {
1753 if ($device eq 'virtio-net-pci') {
1754 $romfile = 'pxe-virtio.rom';
1755 } elsif ($device eq 'e1000') {
1756 $romfile = 'pxe-e1000.rom';
1757 } elsif ($device eq 'e1000e') {
1758 $romfile = 'pxe-e1000e.rom';
1759 } elsif ($device eq 'ne2k') {
1760 $romfile = 'pxe-ne2k_pci.rom';
1761 } elsif ($device eq 'pcnet') {
1762 $romfile = 'pxe-pcnet.rom';
1763 } elsif ($device eq 'rtl8139') {
1764 $romfile = 'pxe-rtl8139.rom';
1766 $tmpstr .= ",romfile=$romfile" if $romfile;
1772 sub print_netdev_full
{
1773 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
1776 if ($netid =~ m/^net(\d+)$/) {
1780 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
1782 my $ifname = "tap${vmid}i$i";
1784 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1785 die "interface name '$ifname' is too long (max 15 character)\n"
1786 if length($ifname) >= 16;
1788 my $vhostparam = '';
1789 if (is_native
($arch)) {
1790 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
1793 my $vmname = $conf->{name
} || "vm$vmid";
1796 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
1798 if ($net->{bridge
}) {
1799 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1800 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
1802 $netdev = "type=user,id=$netid,hostname=$vmname";
1805 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
1811 'cirrus' => 'cirrus-vga',
1813 'vmware' => 'vmware-svga',
1814 'virtio' => 'virtio-vga',
1817 sub print_vga_device
{
1818 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
1820 my $type = $vga_map->{$vga->{type
}};
1821 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
1822 $type = 'virtio-gpu';
1824 my $vgamem_mb = $vga->{memory
};
1826 my $max_outputs = '';
1828 $type = $id ?
'qxl' : 'qxl-vga';
1830 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
1831 # set max outputs so linux can have up to 4 qxl displays with one device
1832 if (min_version
($machine_version, 4, 1)) {
1833 $max_outputs = ",max_outputs=4";
1838 die "no devicetype for $vga->{type}\n" if !$type;
1842 if ($vga->{type
} eq 'virtio') {
1843 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
1844 $memory = ",max_hostmem=$bytes";
1846 # from https://www.spice-space.org/multiple-monitors.html
1847 $memory = ",vgamem_mb=$vga->{memory}";
1848 my $ram = $vgamem_mb * 4;
1849 my $vram = $vgamem_mb * 2;
1850 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1852 $memory = ",vgamem_mb=$vga->{memory}";
1854 } elsif ($qxlnum && $id) {
1855 $memory = ",ram_size=67108864,vram_size=33554432";
1859 if ($type eq 'VGA' && windows_version
($conf->{ostype
})) {
1860 $edidoff=",edid=off" if (!defined($conf->{bios
}) || $conf->{bios
} ne 'ovmf');
1863 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1864 my $vgaid = "vga" . ($id // '');
1866 if ($q35 && $vgaid eq 'vga') {
1867 # the first display uses pcie.0 bus on q35 machines
1868 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
1870 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
1873 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
1876 sub parse_number_sets
{
1879 foreach my $part (split(/;/, $set)) {
1880 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
1881 die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1;
1882 push @$res, [ $1, $2 ];
1884 die "invalid range: $part\n";
1893 my $res = parse_property_string
($numa_fmt, $data);
1894 $res->{cpus
} = parse_number_sets
($res->{cpus
}) if defined($res->{cpus
});
1895 $res->{hostnodes
} = parse_number_sets
($res->{hostnodes
}) if defined($res->{hostnodes
});
1899 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1903 my $res = eval { parse_property_string
($net_fmt, $data) };
1908 if (!defined($res->{macaddr
})) {
1909 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1910 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1915 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1916 sub parse_ipconfig
{
1919 my $res = eval { parse_property_string
($ipconfig_fmt, $data) };
1925 if ($res->{gw
} && !$res->{ip
}) {
1926 warn 'gateway specified without specifying an IP address';
1929 if ($res->{gw6
} && !$res->{ip6
}) {
1930 warn 'IPv6 gateway specified without specifying an IPv6 address';
1933 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
1934 warn 'gateway specified together with DHCP';
1937 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
1939 warn "IPv6 gateway specified together with $res->{ip6} address";
1943 if (!$res->{ip
} && !$res->{ip6
}) {
1944 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
1953 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
1956 sub add_random_macs
{
1957 my ($settings) = @_;
1959 foreach my $opt (keys %$settings) {
1960 next if $opt !~ m/^net(\d+)$/;
1961 my $net = parse_net
($settings->{$opt});
1963 $settings->{$opt} = print_net
($net);
1967 sub vm_is_volid_owner
{
1968 my ($storecfg, $vmid, $volid) = @_;
1970 if ($volid !~ m
|^/|) {
1972 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
1973 if ($owner && ($owner == $vmid)) {
1981 sub vmconfig_register_unused_drive
{
1982 my ($storecfg, $vmid, $conf, $drive) = @_;
1984 if (drive_is_cloudinit
($drive)) {
1985 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
1987 } elsif (!drive_is_cdrom
($drive)) {
1988 my $volid = $drive->{file
};
1989 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
1990 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
1995 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
1999 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
2000 format_description
=> 'UUID',
2001 description
=> "Set SMBIOS1 UUID.",
2006 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2007 format_description
=> 'Base64 encoded string',
2008 description
=> "Set SMBIOS1 version.",
2013 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2014 format_description
=> 'Base64 encoded string',
2015 description
=> "Set SMBIOS1 serial number.",
2020 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2021 format_description
=> 'Base64 encoded string',
2022 description
=> "Set SMBIOS1 manufacturer.",
2027 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2028 format_description
=> 'Base64 encoded string',
2029 description
=> "Set SMBIOS1 product ID.",
2034 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2035 format_description
=> 'Base64 encoded string',
2036 description
=> "Set SMBIOS1 SKU string.",
2041 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2042 format_description
=> 'Base64 encoded string',
2043 description
=> "Set SMBIOS1 family string.",
2048 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
2056 my $res = eval { parse_property_string
($smbios1_fmt, $data) };
2063 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2066 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2068 sub parse_watchdog
{
2073 my $res = eval { parse_property_string
($watchdog_fmt, $value) };
2078 sub parse_guest_agent
{
2081 return {} if !defined($conf->{agent
});
2083 my $res = eval { parse_property_string
($agent_fmt, $conf->{agent
}) };
2086 # if the agent is disabled ignore the other potentially set properties
2087 return {} if !$res->{enabled
};
2092 my ($conf, $key) = @_;
2093 return undef if !defined($conf->{agent
});
2095 my $agent = parse_guest_agent
($conf);
2096 return $agent->{$key};
2102 return {} if !$value;
2103 my $res = eval { parse_property_string
($vga_fmt, $value) };
2113 my $res = eval { parse_property_string
($rng_fmt, $value) };
2118 PVE
::JSONSchema
::register_format
('pve-qm-usb-device', \
&verify_usb_device
);
2119 sub verify_usb_device
{
2120 my ($value, $noerr) = @_;
2122 return $value if parse_usb_device
($value);
2126 die "unable to parse usb device\n";
2129 # add JSON properties for create and set function
2130 sub json_config_properties
{
2133 my $skip_json_config_opts = {
2137 runningmachine
=> 1,
2141 foreach my $opt (keys %$confdesc) {
2142 next if $skip_json_config_opts->{$opt};
2143 $prop->{$opt} = $confdesc->{$opt};
2149 # return copy of $confdesc_cloudinit to generate documentation
2150 sub cloudinit_config_properties
{
2152 return dclone
($confdesc_cloudinit);
2156 my ($key, $value) = @_;
2158 die "unknown setting '$key'\n" if !$confdesc->{$key};
2160 my $type = $confdesc->{$key}->{type
};
2162 if (!defined($value)) {
2163 die "got undefined value\n";
2166 if ($value =~ m/[\n\r]/) {
2167 die "property contains a line feed\n";
2170 if ($type eq 'boolean') {
2171 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2172 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2173 die "type check ('boolean') failed - got '$value'\n";
2174 } elsif ($type eq 'integer') {
2175 return int($1) if $value =~ m/^(\d+)$/;
2176 die "type check ('integer') failed - got '$value'\n";
2177 } elsif ($type eq 'number') {
2178 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2179 die "type check ('number') failed - got '$value'\n";
2180 } elsif ($type eq 'string') {
2181 if (my $fmt = $confdesc->{$key}->{format
}) {
2182 PVE
::JSONSchema
::check_format
($fmt, $value);
2185 $value =~ s/^\"(.*)\"$/$1/;
2188 die "internal error"
2193 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
2195 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2197 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
2199 if ($conf->{template
}) {
2200 # check if any base image is still used by a linked clone
2201 PVE
::QemuConfig-
>foreach_volume_full($conf, { include_unused
=> 1 }, sub {
2202 my ($ds, $drive) = @_;
2203 return if drive_is_cdrom
($drive);
2205 my $volid = $drive->{file
};
2206 return if !$volid || $volid =~ m
|^/|;
2208 die "base volume '$volid' is still in use by linked cloned\n"
2209 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2215 my $remove_owned_drive = sub {
2216 my ($ds, $drive) = @_;
2217 return if drive_is_cdrom
($drive, 1);
2219 my $volid = $drive->{file
};
2220 return if !$volid || $volid =~ m
|^/|;
2221 return if $volids->{$volid};
2223 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2224 return if !$path || !$owner || ($owner != $vmid);
2226 $volids->{$volid} = 1;
2227 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2228 warn "Could not remove disk '$volid', check manually: $@" if $@;
2231 # only remove disks owned by this VM (referenced in the config)
2232 my $include_opts = {
2233 include_unused
=> 1,
2234 extra_keys
=> ['vmstate'],
2236 PVE
::QemuConfig-
>foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2238 for my $snap (values %{$conf->{snapshots
}}) {
2239 next if !defined($snap->{vmstate
});
2240 my $drive = PVE
::QemuConfig-
>parse_volume('vmstate', $snap->{vmstate
}, 1);
2241 next if !defined($drive);
2242 $remove_owned_drive->('vmstate', $drive);
2245 PVE
::QemuConfig-
>foreach_volume_full($conf->{pending
}, $include_opts, $remove_owned_drive);
2247 if ($purge_unreferenced) { # also remove unreferenced disk
2248 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid, undef, 'images');
2249 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2250 my ($volid, $sid, $volname, $d) = @_;
2251 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2256 if (defined $replacement_conf) {
2257 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2259 PVE
::QemuConfig-
>destroy_config($vmid);
2263 sub parse_vm_config
{
2264 my ($filename, $raw) = @_;
2266 return if !defined($raw);
2269 digest
=> Digest
::SHA
::sha1_hex
($raw),
2274 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2275 || die "got strange filename '$filename'";
2283 my @lines = split(/\n/, $raw);
2284 foreach my $line (@lines) {
2285 next if $line =~ m/^\s*$/;
2287 if ($line =~ m/^\[PENDING\]\s*$/i) {
2288 $section = 'pending';
2289 if (defined($descr)) {
2291 $conf->{description
} = $descr;
2294 $conf = $res->{$section} = {};
2297 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2299 if (defined($descr)) {
2301 $conf->{description
} = $descr;
2304 $conf = $res->{snapshots
}->{$section} = {};
2308 if ($line =~ m/^\#(.*)\s*$/) {
2309 $descr = '' if !defined($descr);
2310 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2314 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2315 $descr = '' if !defined($descr);
2316 $descr .= PVE
::Tools
::decode_text
($2);
2317 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2318 $conf->{snapstate
} = $1;
2319 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2322 $conf->{$key} = $value;
2323 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2325 if ($section eq 'pending') {
2326 $conf->{delete} = $value; # we parse this later
2328 warn "vm $vmid - propertry 'delete' is only allowed in [PENDING]\n";
2330 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2333 eval { $value = check_type
($key, $value); };
2335 warn "vm $vmid - unable to parse value of '$key' - $@";
2337 $key = 'ide2' if $key eq 'cdrom';
2338 my $fmt = $confdesc->{$key}->{format
};
2339 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2340 my $v = parse_drive
($key, $value);
2341 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2342 $v->{file
} = $volid;
2343 $value = print_drive
($v);
2345 warn "vm $vmid - unable to parse value of '$key'\n";
2350 $conf->{$key} = $value;
2353 warn "vm $vmid - unable to parse config: $line\n";
2357 if (defined($descr)) {
2359 $conf->{description
} = $descr;
2361 delete $res->{snapstate
}; # just to be sure
2366 sub write_vm_config
{
2367 my ($filename, $conf) = @_;
2369 delete $conf->{snapstate
}; # just to be sure
2371 if ($conf->{cdrom
}) {
2372 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2373 $conf->{ide2
} = $conf->{cdrom
};
2374 delete $conf->{cdrom
};
2377 # we do not use 'smp' any longer
2378 if ($conf->{sockets
}) {
2379 delete $conf->{smp
};
2380 } elsif ($conf->{smp
}) {
2381 $conf->{sockets
} = $conf->{smp
};
2382 delete $conf->{cores
};
2383 delete $conf->{smp
};
2386 my $used_volids = {};
2388 my $cleanup_config = sub {
2389 my ($cref, $pending, $snapname) = @_;
2391 foreach my $key (keys %$cref) {
2392 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2393 $key eq 'snapstate' || $key eq 'pending';
2394 my $value = $cref->{$key};
2395 if ($key eq 'delete') {
2396 die "propertry 'delete' is only allowed in [PENDING]\n"
2398 # fixme: check syntax?
2401 eval { $value = check_type
($key, $value); };
2402 die "unable to parse value of '$key' - $@" if $@;
2404 $cref->{$key} = $value;
2406 if (!$snapname && is_valid_drivename
($key)) {
2407 my $drive = parse_drive
($key, $value);
2408 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2413 &$cleanup_config($conf);
2415 &$cleanup_config($conf->{pending
}, 1);
2417 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2418 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2419 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2422 # remove 'unusedX' settings if we re-add a volume
2423 foreach my $key (keys %$conf) {
2424 my $value = $conf->{$key};
2425 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2426 delete $conf->{$key};
2430 my $generate_raw_config = sub {
2431 my ($conf, $pending) = @_;
2435 # add description as comment to top of file
2436 if (defined(my $descr = $conf->{description
})) {
2438 foreach my $cl (split(/\n/, $descr)) {
2439 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2442 $raw .= "#\n" if $pending;
2446 foreach my $key (sort keys %$conf) {
2447 next if $key =~ /^(digest|description|pending|snapshots)$/;
2448 $raw .= "$key: $conf->{$key}\n";
2453 my $raw = &$generate_raw_config($conf);
2455 if (scalar(keys %{$conf->{pending
}})){
2456 $raw .= "\n[PENDING]\n";
2457 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2460 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2461 $raw .= "\n[$snapname]\n";
2462 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2472 # we use static defaults from our JSON schema configuration
2473 foreach my $key (keys %$confdesc) {
2474 if (defined(my $default = $confdesc->{$key}->{default})) {
2475 $res->{$key} = $default;
2483 my $vmlist = PVE
::Cluster
::get_vmlist
();
2485 return $res if !$vmlist || !$vmlist->{ids
};
2486 my $ids = $vmlist->{ids
};
2487 my $nodename = nodename
();
2489 foreach my $vmid (keys %$ids) {
2490 my $d = $ids->{$vmid};
2491 next if !$d->{node
} || $d->{node
} ne $nodename;
2492 next if !$d->{type
} || $d->{type
} ne 'qemu';
2493 $res->{$vmid}->{exists} = 1;
2498 # test if VM uses local resources (to prevent migration)
2499 sub check_local_resources
{
2500 my ($conf, $noerr) = @_;
2504 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2505 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2507 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2509 foreach my $k (keys %$conf) {
2510 next if $k =~ m/^usb/ && ($conf->{$k} =~ m/^spice(?![^,])/);
2511 # sockets are safe: they will recreated be on the target side post-migrate
2512 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2513 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2516 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2521 # check if used storages are available on all nodes (use by migrate)
2522 sub check_storage_availability
{
2523 my ($storecfg, $conf, $node) = @_;
2525 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2526 my ($ds, $drive) = @_;
2528 my $volid = $drive->{file
};
2531 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2534 # check if storage is available on both nodes
2535 my $scfg = PVE
::Storage
::storage_check_enabled
($storecfg, $sid);
2536 PVE
::Storage
::storage_check_enabled
($storecfg, $sid, $node);
2538 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $volid);
2540 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2541 if !$scfg->{content
}->{$vtype};
2545 # list nodes where all VM images are available (used by has_feature API)
2547 my ($conf, $storecfg) = @_;
2549 my $nodelist = PVE
::Cluster
::get_nodelist
();
2550 my $nodehash = { map { $_ => 1 } @$nodelist };
2551 my $nodename = nodename
();
2553 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2554 my ($ds, $drive) = @_;
2556 my $volid = $drive->{file
};
2559 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2561 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2562 if ($scfg->{disable
}) {
2564 } elsif (my $avail = $scfg->{nodes
}) {
2565 foreach my $node (keys %$nodehash) {
2566 delete $nodehash->{$node} if !$avail->{$node};
2568 } elsif (!$scfg->{shared
}) {
2569 foreach my $node (keys %$nodehash) {
2570 delete $nodehash->{$node} if $node ne $nodename
2579 sub check_local_storage_availability
{
2580 my ($conf, $storecfg) = @_;
2582 my $nodelist = PVE
::Cluster
::get_nodelist
();
2583 my $nodehash = { map { $_ => {} } @$nodelist };
2585 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2586 my ($ds, $drive) = @_;
2588 my $volid = $drive->{file
};
2591 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2593 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2595 if ($scfg->{disable
}) {
2596 foreach my $node (keys %$nodehash) {
2597 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2599 } elsif (my $avail = $scfg->{nodes
}) {
2600 foreach my $node (keys %$nodehash) {
2601 if (!$avail->{$node}) {
2602 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2609 foreach my $node (values %$nodehash) {
2610 if (my $unavail = $node->{unavailable_storages
}) {
2611 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2618 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2620 my ($vmid, $nocheck, $node) = @_;
2622 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2623 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2628 my $vzlist = config_list
();
2630 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2632 while (defined(my $de = $fd->read)) {
2633 next if $de !~ m/^(\d+)\.pid$/;
2635 next if !defined($vzlist->{$vmid});
2636 if (my $pid = check_running
($vmid)) {
2637 $vzlist->{$vmid}->{pid
} = $pid;
2644 our $vmstatus_return_properties = {
2645 vmid
=> get_standard_option
('pve-vmid'),
2647 description
=> "Qemu process status.",
2649 enum
=> ['stopped', 'running'],
2652 description
=> "Maximum memory in bytes.",
2655 renderer
=> 'bytes',
2658 description
=> "Root disk size in bytes.",
2661 renderer
=> 'bytes',
2664 description
=> "VM name.",
2669 description
=> "Qemu QMP agent status.",
2674 description
=> "PID of running qemu process.",
2679 description
=> "Uptime.",
2682 renderer
=> 'duration',
2685 description
=> "Maximum usable CPUs.",
2690 description
=> "The current config lock, if any.",
2695 description
=> "The current configured tags, if any",
2699 'running-machine' => {
2700 description
=> "The currently running machine type (if running).",
2705 description
=> "The currently running QEMU version (if running).",
2711 my $last_proc_pid_stat;
2713 # get VM status information
2714 # This must be fast and should not block ($full == false)
2715 # We only query KVM using QMP if $full == true (this can be slow)
2717 my ($opt_vmid, $full) = @_;
2721 my $storecfg = PVE
::Storage
::config
();
2723 my $list = vzlist
();
2724 my $defaults = load_defaults
();
2726 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
(1);
2728 my $cpucount = $cpuinfo->{cpus
} || 1;
2730 foreach my $vmid (keys %$list) {
2731 next if $opt_vmid && ($vmid ne $opt_vmid);
2733 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2735 my $d = { vmid
=> int($vmid) };
2736 $d->{pid
} = int($list->{$vmid}->{pid
}) if $list->{$vmid}->{pid
};
2738 # fixme: better status?
2739 $d->{status
} = $list->{$vmid}->{pid
} ?
'running' : 'stopped';
2741 my $size = PVE
::QemuServer
::Drive
::bootdisk_size
($storecfg, $conf);
2742 if (defined($size)) {
2743 $d->{disk
} = 0; # no info available
2744 $d->{maxdisk
} = $size;
2750 $d->{cpus
} = ($conf->{sockets
} || $defaults->{sockets
})
2751 * ($conf->{cores
} || $defaults->{cores
});
2752 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
2753 $d->{cpus
} = $conf->{vcpus
} if $conf->{vcpus
};
2755 $d->{name
} = $conf->{name
} || "VM $vmid";
2756 $d->{maxmem
} = $conf->{memory
} ?
$conf->{memory
}*(1024*1024)
2757 : $defaults->{memory
}*(1024*1024);
2759 if ($conf->{balloon
}) {
2760 $d->{balloon_min
} = $conf->{balloon
}*(1024*1024);
2761 $d->{shares
} = defined($conf->{shares
}) ?
$conf->{shares
}
2762 : $defaults->{shares
};
2773 $d->{diskwrite
} = 0;
2775 $d->{template
} = 1 if PVE
::QemuConfig-
>is_template($conf);
2777 $d->{serial
} = 1 if conf_has_serial
($conf);
2778 $d->{lock} = $conf->{lock} if $conf->{lock};
2779 $d->{tags
} = $conf->{tags
} if defined($conf->{tags
});
2784 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
2785 foreach my $dev (keys %$netdev) {
2786 next if $dev !~ m/^tap([1-9]\d*)i/;
2788 my $d = $res->{$vmid};
2791 $d->{netout
} += $netdev->{$dev}->{receive
};
2792 $d->{netin
} += $netdev->{$dev}->{transmit
};
2795 $d->{nics
}->{$dev}->{netout
} = int($netdev->{$dev}->{receive
});
2796 $d->{nics
}->{$dev}->{netin
} = int($netdev->{$dev}->{transmit
});
2801 my $ctime = gettimeofday
;
2803 foreach my $vmid (keys %$list) {
2805 my $d = $res->{$vmid};
2806 my $pid = $d->{pid
};
2809 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
2810 next if !$pstat; # not running
2812 my $used = $pstat->{utime} + $pstat->{stime
};
2814 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
2816 if ($pstat->{vsize
}) {
2817 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
2820 my $old = $last_proc_pid_stat->{$pid};
2822 $last_proc_pid_stat->{$pid} = {
2830 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
2832 if ($dtime > 1000) {
2833 my $dutime = $used - $old->{used
};
2835 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
2836 $last_proc_pid_stat->{$pid} = {
2842 $d->{cpu
} = $old->{cpu
};
2846 return $res if !$full;
2848 my $qmpclient = PVE
::QMPClient-
>new();
2850 my $ballooncb = sub {
2851 my ($vmid, $resp) = @_;
2853 my $info = $resp->{'return'};
2854 return if !$info->{max_mem
};
2856 my $d = $res->{$vmid};
2858 # use memory assigned to VM
2859 $d->{maxmem
} = $info->{max_mem
};
2860 $d->{balloon
} = $info->{actual
};
2862 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
2863 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
2864 $d->{freemem
} = $info->{free_mem
};
2867 $d->{ballooninfo
} = $info;
2870 my $blockstatscb = sub {
2871 my ($vmid, $resp) = @_;
2872 my $data = $resp->{'return'} || [];
2873 my $totalrdbytes = 0;
2874 my $totalwrbytes = 0;
2876 for my $blockstat (@$data) {
2877 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
2878 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
2880 $blockstat->{device
} =~ s/drive-//;
2881 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
2883 $res->{$vmid}->{diskread
} = $totalrdbytes;
2884 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
2887 my $machinecb = sub {
2888 my ($vmid, $resp) = @_;
2889 my $data = $resp->{'return'} || [];
2891 $res->{$vmid}->{'running-machine'} =
2892 PVE
::QemuServer
::Machine
::current_from_query_machines
($data);
2895 my $versioncb = sub {
2896 my ($vmid, $resp) = @_;
2897 my $data = $resp->{'return'} // {};
2898 my $version = 'unknown';
2900 if (my $v = $data->{qemu
}) {
2901 $version = $v->{major
} . "." . $v->{minor
} . "." . $v->{micro
};
2904 $res->{$vmid}->{'running-qemu'} = $version;
2907 my $statuscb = sub {
2908 my ($vmid, $resp) = @_;
2910 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
2911 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
2912 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
2913 # this fails if ballon driver is not loaded, so this must be
2914 # the last commnand (following command are aborted if this fails).
2915 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
2917 my $status = 'unknown';
2918 if (!defined($status = $resp->{'return'}->{status
})) {
2919 warn "unable to get VM status\n";
2923 $res->{$vmid}->{qmpstatus
} = $resp->{'return'}->{status
};
2926 foreach my $vmid (keys %$list) {
2927 next if $opt_vmid && ($vmid ne $opt_vmid);
2928 next if !$res->{$vmid}->{pid
}; # not running
2929 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
2932 $qmpclient->queue_execute(undef, 2);
2934 foreach my $vmid (keys %$list) {
2935 next if $opt_vmid && ($vmid ne $opt_vmid);
2936 next if !$res->{$vmid}->{pid
}; #not running
2938 # we can't use the $qmpclient since it might have already aborted on
2939 # 'query-balloon', but this might also fail for older versions...
2940 my $qemu_support = eval { mon_cmd
($vmid, "query-proxmox-support") };
2941 $res->{$vmid}->{'proxmox-support'} = $qemu_support // {};
2944 foreach my $vmid (keys %$list) {
2945 next if $opt_vmid && ($vmid ne $opt_vmid);
2946 $res->{$vmid}->{qmpstatus
} = $res->{$vmid}->{status
} if !$res->{$vmid}->{qmpstatus
};
2952 sub conf_has_serial
{
2955 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
2956 if ($conf->{"serial$i"}) {
2964 sub conf_has_audio
{
2965 my ($conf, $id) = @_;
2968 my $audio = $conf->{"audio$id"};
2969 return if !defined($audio);
2971 my $audioproperties = parse_property_string
($audio_fmt, $audio);
2972 my $audiodriver = $audioproperties->{driver
} // 'spice';
2975 dev
=> $audioproperties->{device
},
2976 dev_id
=> "audiodev$id",
2977 backend
=> $audiodriver,
2978 backend_id
=> "$audiodriver-backend${id}",
2983 my ($audio, $audiopciaddr, $machine_version) = @_;
2987 my $id = $audio->{dev_id
};
2989 if (min_version
($machine_version, 4, 2)) {
2990 $audiodev = ",audiodev=$audio->{backend_id}";
2993 if ($audio->{dev
} eq 'AC97') {
2994 push @$devs, '-device', "AC97,id=${id}${audiopciaddr}$audiodev";
2995 } elsif ($audio->{dev
} =~ /intel\-hda$/) {
2996 push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
2997 push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0$audiodev";
2998 push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1$audiodev";
3000 die "unkown audio device '$audio->{dev}', implement me!";
3003 push @$devs, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
3011 socket => "/var/run/qemu-server/$vmid.swtpm",
3012 pid
=> "/var/run/qemu-server/$vmid.swtpm.pid",
3016 sub add_tpm_device
{
3017 my ($vmid, $devices, $conf) = @_;
3019 return if !$conf->{tpmstate0
};
3021 my $paths = get_tpm_paths
($vmid);
3023 push @$devices, "-chardev", "socket,id=tpmchar,path=$paths->{socket}";
3024 push @$devices, "-tpmdev", "emulator,id=tpmdev,chardev=tpmchar";
3025 push @$devices, "-device", "tpm-tis,tpmdev=tpmdev";
3029 my ($storecfg, $vmid, $tpmdrive, $migration) = @_;
3031 return if !$tpmdrive;
3034 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
3035 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
3037 $state = PVE
::Storage
::map_volume
($storecfg, $tpm->{file
});
3039 $state = $tpm->{file
};
3042 my $paths = get_tpm_paths
($vmid);
3044 # during migration, we will get state from remote
3047 # run swtpm_setup to create a new TPM state if it doesn't exist yet
3054 "--create-platform-cert",
3057 "/etc/swtpm_setup.conf", # do not use XDG configs
3059 "0", # force creation as root, error if not possible
3060 "--not-overwrite", # ignore existing state, do not modify
3063 push @$setup_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3064 # TPM 2.0 supports ECC crypto, use if possible
3065 push @$setup_cmd, "--ecc" if $tpm->{version
} eq 'v2.0';
3067 run_command
($setup_cmd, outfunc
=> sub {
3068 print "swtpm_setup: $1\n";
3072 my $emulator_cmd = [
3076 "backend-uri=file://$state,mode=0600",
3078 "type=unixio,path=$paths->{socket},mode=0600",
3080 "file=$paths->{pid}",
3081 "--terminate", # terminate on QEMU disconnect
3084 push @$emulator_cmd, "--tpm2" if $tpm->{version
} eq 'v2.0';
3085 run_command
($emulator_cmd, outfunc
=> sub { print $1; });
3087 my $tries = 100; # swtpm may take a bit to start before daemonizing, wait up to 5s for pid
3088 while (! -e
$paths->{pid
}) {
3089 die "failed to start swtpm: pid file '$paths->{pid}' wasn't created.\n" if --$tries == 0;
3093 # return untainted PID of swtpm daemon so it can be killed on error
3094 file_read_firstline
($paths->{pid
}) =~ m/(\d+)/;
3098 sub vga_conf_has_spice
{
3101 my $vgaconf = parse_vga
($vga);
3102 my $vgatype = $vgaconf->{type
};
3103 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
3110 return get_host_arch
() eq $arch;
3115 return $conf->{arch
} // get_host_arch
();
3118 my $default_machines = {
3123 sub get_installed_machine_version
{
3124 my ($kvmversion) = @_;
3125 $kvmversion = kvm_user_version
() if !defined($kvmversion);
3126 $kvmversion =~ m/^(\d+\.\d+)/;
3130 sub windows_get_pinned_machine_version
{
3131 my ($machine, $base_version, $kvmversion) = @_;
3133 my $pin_version = $base_version;
3134 if (!defined($base_version) ||
3135 !PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($base_version, $kvmversion)
3137 $pin_version = get_installed_machine_version
($kvmversion);
3139 if (!$machine || $machine eq 'pc') {
3140 $machine = "pc-i440fx-$pin_version";
3141 } elsif ($machine eq 'q35') {
3142 $machine = "pc-q35-$pin_version";
3143 } elsif ($machine eq 'virt') {
3144 $machine = "virt-$pin_version";
3146 warn "unknown machine type '$machine', not touching that!\n";
3152 sub get_vm_machine
{
3153 my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
3155 my $machine = $forcemachine || $conf->{machine
};
3157 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
3158 $kvmversion //= kvm_user_version
();
3159 # we must pin Windows VMs without a specific version to 5.1, as 5.2 fixed a bug in ACPI
3160 # layout which confuses windows quite a bit and may result in various regressions..
3161 # see: https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg08484.html
3162 if (windows_version
($conf->{ostype
})) {
3163 $machine = windows_get_pinned_machine_version
($machine, '5.1', $kvmversion);
3166 $machine ||= $default_machines->{$arch};
3167 if ($add_pve_version) {
3168 my $pvever = PVE
::QemuServer
::Machine
::get_pve_version
($kvmversion);
3169 $machine .= "+pve$pvever";
3173 if ($add_pve_version && $machine !~ m/\+pve\d+?(?:\.pxe)?$/) {
3174 my $is_pxe = $machine =~ m/^(.*?)\.pxe$/;
3175 $machine = $1 if $is_pxe;
3177 # for version-pinned machines that do not include a pve-version (e.g.
3178 # pc-q35-4.1), we assume 0 to keep them stable in case we bump
3179 $machine .= '+pve0';
3181 $machine .= '.pxe' if $is_pxe;
3187 sub get_ovmf_files
($$$) {
3188 my ($arch, $efidisk, $smm) = @_;
3190 my $types = $OVMF->{$arch}
3191 or die "no OVMF images known for architecture '$arch'\n";
3193 my $type = 'default';
3194 if (defined($efidisk->{efitype
}) && $efidisk->{efitype
} eq '4m') {
3195 $type = $smm ?
"4m" : "4m-no-smm";
3196 $type .= '-ms' if $efidisk->{'pre-enrolled-keys'};
3199 return $types->{$type}->@*;
3203 aarch64
=> '/usr/bin/qemu-system-aarch64',
3204 x86_64
=> '/usr/bin/qemu-system-x86_64',
3206 sub get_command_for_arch
($) {
3208 return '/usr/bin/kvm' if is_native
($arch);
3210 my $cmd = $Arch2Qemu->{$arch}
3211 or die "don't know how to emulate architecture '$arch'\n";
3215 # To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
3216 # to use in a QEMU command line (-cpu element), first array_intersect the result
3217 # of query_supported_ with query_understood_. This is necessary because:
3219 # a) query_understood_ returns flags the host cannot use and
3220 # b) query_supported_ (rather the QMP call) doesn't actually return CPU
3221 # flags, but CPU settings - with most of them being flags. Those settings
3222 # (and some flags, curiously) cannot be specified as a "-cpu" argument.
3224 # query_supported_ needs to start up to 2 temporary VMs and is therefore rather
3225 # expensive. If you need the value returned from this, you can get it much
3226 # cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags-$accel') with
3227 # $accel being 'kvm' or 'tcg'.
3229 # pvestatd calls this function on startup and whenever the QEMU/KVM version
3230 # changes, automatically populating pmxcfs.
3232 # Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
3233 # since kvm and tcg machines support different flags
3235 sub query_supported_cpu_flags
{
3238 $arch //= get_host_arch
();
3239 my $default_machine = $default_machines->{$arch};
3243 # FIXME: Once this is merged, the code below should work for ARM as well:
3244 # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
3245 die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
3248 my $kvm_supported = defined(kvm_version
());
3249 my $qemu_cmd = get_command_for_arch
($arch);
3251 my $pidfile = PVE
::QemuServer
::Helpers
::pidfile_name
($fakevmid);
3253 # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
3254 my $query_supported_run_qemu = sub {
3260 '-machine', $default_machine,
3262 '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
3263 '-mon', 'chardev=qmp,mode=control',
3264 '-pidfile', $pidfile,
3269 push @$cmd, '-accel', 'tcg';
3272 my $rc = run_command
($cmd, noerr
=> 1, quiet
=> 0);
3273 die "QEMU flag querying VM exited with code " . $rc if $rc;
3276 my $cmd_result = mon_cmd
(
3278 'query-cpu-model-expansion',
3280 model
=> { name
=> 'host' }
3283 my $props = $cmd_result->{model
}->{props
};
3284 foreach my $prop (keys %$props) {
3285 next if $props->{$prop} ne '1';
3286 # QEMU returns some flags multiple times, with '_', '.' or '-'
3287 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
3288 # We only keep those with underscores, to match /proc/cpuinfo
3289 $prop =~ s/\.|-/_/g;
3290 $flags->{$prop} = 1;
3295 # force stop with 10 sec timeout and 'nocheck', always stop, even if QMP failed
3296 vm_stop
(undef, $fakevmid, 1, 1, 10, 0, 1);
3300 return [ sort keys %$flags ];
3303 # We need to query QEMU twice, since KVM and TCG have different supported flags
3304 PVE
::QemuConfig-
>lock_config($fakevmid, sub {
3305 $flags->{tcg
} = eval { $query_supported_run_qemu->(0) };
3306 warn "warning: failed querying supported tcg flags: $@\n" if $@;
3308 if ($kvm_supported) {
3309 $flags->{kvm
} = eval { $query_supported_run_qemu->(1) };
3310 warn "warning: failed querying supported kvm flags: $@\n" if $@;
3317 # Understood CPU flags are written to a file at 'pve-qemu' compile time
3318 my $understood_cpu_flag_dir = "/usr/share/kvm";
3319 sub query_understood_cpu_flags
{
3320 my $arch = get_host_arch
();
3321 my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
3323 die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
3326 my $raw = file_get_contents
($filepath);
3327 $raw =~ s/^\s+|\s+$//g;
3328 my @flags = split(/\s+/, $raw);
3333 my sub get_cpuunits
{
3335 return $conf->{cpuunits
} // (PVE
::CGroup
::cgroup_mode
() == 2 ?
100 : 1024);
3337 sub config_to_command
{
3338 my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
3342 my ($globalFlags, $machineFlags, $rtcFlags) = ([], [], []);
3345 my $ostype = $conf->{ostype
};
3346 my $winversion = windows_version
($ostype);
3347 my $kvm = $conf->{kvm
};
3348 my $nodename = nodename
();
3350 my $arch = get_vm_arch
($conf);
3351 my $kvm_binary = get_command_for_arch
($arch);
3352 my $kvmver = kvm_user_version
($kvm_binary);
3354 if (!$kvmver || $kvmver !~ m/^(\d+)\.(\d+)/ || $1 < 3) {
3355 $kvmver //= "undefined";
3356 die "Detected old QEMU binary ('$kvmver', at least 3.0 is required)\n";
3359 my $add_pve_version = min_version
($kvmver, 4, 1);
3361 my $machine_type = get_vm_machine
($conf, $forcemachine, $arch, $add_pve_version);
3362 my $machine_version = extract_version
($machine_type, $kvmver);
3363 $kvm //= 1 if is_native
($arch);
3365 $machine_version =~ m/(\d+)\.(\d+)/;
3366 my ($machine_major, $machine_minor) = ($1, $2);
3368 if ($kvmver =~ m/^\d+\.\d+\.(\d+)/ && $1 >= 90) {
3369 warn "warning: Installed QEMU version ($kvmver) is a release candidate, ignoring version checks\n";
3370 } elsif (!min_version
($kvmver, $machine_major, $machine_minor)) {
3371 die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type',"
3372 ." please upgrade node '$nodename'\n"
3373 } elsif (!PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($machine_version, $kvmver)) {
3374 my $max_pve_version = PVE
::QemuServer
::Machine
::get_pve_version
($machine_version);
3375 die "Installed qemu-server (max feature level for $machine_major.$machine_minor is"
3376 ." pve$max_pve_version) is too old to run machine type '$machine_type', please upgrade"
3377 ." node '$nodename'\n";
3380 # if a specific +pve version is required for a feature, use $version_guard
3381 # instead of min_version to allow machines to be run with the minimum
3383 my $required_pve_version = 0;
3384 my $version_guard = sub {
3385 my ($major, $minor, $pve) = @_;
3386 return 0 if !min_version
($machine_version, $major, $minor, $pve);
3387 my $max_pve = PVE
::QemuServer
::Machine
::get_pve_version
("$major.$minor");
3388 return 1 if min_version
($machine_version, $major, $minor, $max_pve+1);
3389 $required_pve_version = $pve if $pve && $pve > $required_pve_version;
3393 if ($kvm && !defined kvm_version
()) {
3394 die "KVM virtualisation configured, but not available. Either disable in VM configuration"
3395 ." or enable in BIOS.\n";
3398 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3399 my $hotplug_features = parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
3400 my $use_old_bios_files = undef;
3401 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3403 my $cpuunits = get_cpuunits
($conf);
3405 push @$cmd, $kvm_binary;
3407 push @$cmd, '-id', $vmid;
3409 my $vmname = $conf->{name
} || "vm$vmid";
3411 push @$cmd, '-name', $vmname;
3413 push @$cmd, '-no-shutdown';
3417 my $qmpsocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid);
3418 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server=on,wait=off";
3419 push @$cmd, '-mon', "chardev=qmp,mode=control";
3421 if (min_version
($machine_version, 2, 12)) {
3422 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3423 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3426 push @$cmd, '-pidfile' , PVE
::QemuServer
::Helpers
::pidfile_name
($vmid);
3428 push @$cmd, '-daemonize';
3430 if ($conf->{smbios1
}) {
3431 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
3432 if ($smbios_conf->{base64
}) {
3433 # Do not pass base64 flag to qemu
3434 delete $smbios_conf->{base64
};
3435 my $smbios_string = "";
3436 foreach my $key (keys %$smbios_conf) {
3438 if ($key eq "uuid") {
3439 $value = $smbios_conf->{uuid
}
3441 $value = decode_base64
($smbios_conf->{$key});
3443 # qemu accepts any binary data, only commas need escaping by double comma
3445 $smbios_string .= "," . $key . "=" . $value if $value;
3447 push @$cmd, '-smbios', "type=1" . $smbios_string;
3449 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3453 if ($conf->{bios
} && $conf->{bios
} eq 'ovmf') {
3455 if (my $efidisk = $conf->{efidisk0
}) {
3456 $d = parse_drive
('efidisk0', $efidisk);
3459 my ($ovmf_code, $ovmf_vars) = get_ovmf_files
($arch, $d, $q35);
3460 die "uefi base image '$ovmf_code' not found\n" if ! -f
$ovmf_code;
3462 my ($path, $format);
3463 my $read_only_str = '';
3465 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($d->{file
}, 1);
3466 $format = $d->{format
};
3468 $path = PVE
::Storage
::path
($storecfg, $d->{file
});
3469 if (!defined($format)) {
3470 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
3471 $format = qemu_img_format
($scfg, $volname);
3475 die "efidisk format must be specified\n"
3476 if !defined($format);
3479 $read_only_str = ',readonly=on' if drive_is_read_only
($conf, $d);
3481 warn "no efidisk configured! Using temporary efivars disk.\n";
3482 $path = "/tmp/$vmid-ovmf.fd";
3483 PVE
::Tools
::file_copy
($ovmf_vars, $path, -s
$ovmf_vars);
3489 if ($format eq 'raw' && $version_guard->(4, 1, 2)) {
3490 $size_str = ",size=" . (-s
$ovmf_vars);
3493 # SPI flash does lots of read-modify-write OPs, without writeback this gets really slow #3329
3495 if ($path =~ m/^rbd:/) {
3496 $cache = ',cache=writeback';
3497 $path .= ':rbd_cache_policy=writeback'; # avoid write-around, we *need* to cache writes too
3500 push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code";
3501 push @$cmd, '-drive', "if=pflash,unit=1$cache,format=$format,id=drive-efidisk0$size_str,file=${path}${read_only_str}";
3504 if ($q35) { # tell QEMU to load q35 config early
3505 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3506 if (min_version
($machine_version, 4, 0)) {
3507 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3509 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3513 if ($conf->{vmgenid
}) {
3514 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid
};
3517 # add usb controllers
3518 my @usbcontrollers = PVE
::QemuServer
::USB
::get_usb_controllers
(
3519 $conf, $bridges, $arch, $machine_type, $usbdesc->{format
}, $MAX_USB_DEVICES);
3520 push @$devices, @usbcontrollers if @usbcontrollers;
3521 my $vga = parse_vga
($conf->{vga
});
3523 my $qxlnum = vga_conf_has_spice
($conf->{vga
});
3524 $vga->{type
} = 'qxl' if $qxlnum;
3526 if (!$vga->{type
}) {
3527 if ($arch eq 'aarch64') {
3528 $vga->{type
} = 'virtio';
3529 } elsif (min_version
($machine_version, 2, 9)) {
3530 $vga->{type
} = (!$winversion || $winversion >= 6) ?
'std' : 'cirrus';
3532 $vga->{type
} = ($winversion >= 6) ?
'std' : 'cirrus';
3536 # enable absolute mouse coordinates (needed by vnc)
3537 my $tablet = $conf->{tablet
};
3538 if (!defined($tablet)) {
3539 $tablet = $defaults->{tablet
};
3540 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3541 $tablet = 0 if $vga->{type
} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3545 push @$devices, '-device', print_tabletdevice_full
($conf, $arch) if $tablet;
3546 my $kbd = print_keyboarddevice_full
($conf, $arch);
3547 push @$devices, '-device', $kbd if defined($kbd);
3550 my $bootorder = device_bootorder
($conf);
3552 # host pci device passthrough
3553 my ($kvm_off, $gpu_passthrough, $legacy_igd) = PVE
::QemuServer
::PCI
::print_hostpci_devices
(
3554 $vmid, $conf, $devices, $vga, $winversion, $q35, $bridges, $arch, $machine_type, $bootorder);
3557 my $usb_dev_features = {};
3558 $usb_dev_features->{spice_usb3
} = 1 if min_version
($machine_version, 4, 0);
3560 my @usbdevices = PVE
::QemuServer
::USB
::get_usb_devices
(
3561 $conf, $usbdesc->{format
}, $MAX_USB_DEVICES, $usb_dev_features, $bootorder);
3562 push @$devices, @usbdevices if @usbdevices;
3565 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3566 my $path = $conf->{"serial$i"} or next;
3567 if ($path eq 'socket') {
3568 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3569 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
3570 # On aarch64, serial0 is the UART device. Qemu only allows
3571 # connecting UART devices via the '-serial' command line, as
3572 # the device has a fixed slot on the hardware...
3573 if ($arch eq 'aarch64' && $i == 0) {
3574 push @$devices, '-serial', "chardev:serial$i";
3576 push @$devices, '-device', "isa-serial,chardev=serial$i";
3579 die "no such serial device\n" if ! -c
$path;
3580 push @$devices, '-chardev', "tty,id=serial$i,path=$path";
3581 push @$devices, '-device', "isa-serial,chardev=serial$i";
3586 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3587 if (my $path = $conf->{"parallel$i"}) {
3588 die "no such parallel device\n" if ! -c
$path;
3589 my $devtype = $path =~ m!^/dev/usb/lp! ?
'tty' : 'parport';
3590 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3591 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3595 if (min_version
($machine_version, 4, 0) && (my $audio = conf_has_audio
($conf))) {
3596 my $audiopciaddr = print_pci_addr
("audio0", $bridges, $arch, $machine_type);
3597 my $audio_devs = audio_devs
($audio, $audiopciaddr, $machine_version);
3598 push @$devices, @$audio_devs;
3601 add_tpm_device
($vmid, $devices, $conf);
3604 $sockets = $conf->{smp
} if $conf->{smp
}; # old style - no longer iused
3605 $sockets = $conf->{sockets
} if $conf->{sockets
};
3607 my $cores = $conf->{cores
} || 1;
3609 my $maxcpus = $sockets * $cores;
3611 my $vcpus = $conf->{vcpus
} ?
$conf->{vcpus
} : $maxcpus;
3613 my $allowed_vcpus = $cpuinfo->{cpus
};
3615 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n" if ($allowed_vcpus < $maxcpus);
3617 if ($hotplug_features->{cpu
} && min_version
($machine_version, 2, 7)) {
3618 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3619 for (my $i = 2; $i <= $vcpus; $i++) {
3620 my $cpustr = print_cpu_device
($conf,$i);
3621 push @$cmd, '-device', $cpustr;
3626 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3628 push @$cmd, '-nodefaults';
3630 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3632 push @$cmd, '-no-acpi' if defined($conf->{acpi
}) && $conf->{acpi
} == 0;
3634 push @$cmd, '-no-reboot' if defined($conf->{reboot
}) && $conf->{reboot
} == 0;
3636 if ($vga->{type
} && $vga->{type
} !~ m/^serial\d+$/ && $vga->{type
} ne 'none'){
3637 push @$devices, '-device', print_vga_device
(
3638 $conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
3639 my $socket = PVE
::QemuServer
::Helpers
::vnc_socket
($vmid);
3640 push @$cmd, '-vnc', "unix:$socket,password=on";
3642 push @$cmd, '-vga', 'none' if $vga->{type
} eq 'none';
3643 push @$cmd, '-nographic';
3647 my $tdf = defined($conf->{tdf
}) ?
$conf->{tdf
} : $defaults->{tdf
};
3648 my $useLocaltime = $conf->{localtime};
3650 if ($winversion >= 5) { # windows
3651 $useLocaltime = 1 if !defined($conf->{localtime});
3653 # use time drift fix when acpi is enabled
3654 if (!(defined($conf->{acpi
}) && $conf->{acpi
} == 0)) {
3655 $tdf = 1 if !defined($conf->{tdf
});
3659 if ($winversion >= 6) {
3660 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3661 push @$cmd, '-no-hpet';
3664 push @$rtcFlags, 'driftfix=slew' if $tdf;
3666 if ($conf->{startdate
} && $conf->{startdate
} ne 'now') {
3667 push @$rtcFlags, "base=$conf->{startdate}";
3668 } elsif ($useLocaltime) {
3669 push @$rtcFlags, 'base=localtime';
3673 push @$cmd, '-cpu', $forcecpu;
3675 push @$cmd, get_cpu_options
($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
3678 PVE
::QemuServer
::Memory
::config
($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
3680 push @$cmd, '-S' if $conf->{freeze
};
3682 push @$cmd, '-k', $conf->{keyboard
} if defined($conf->{keyboard
});
3684 my $guest_agent = parse_guest_agent
($conf);
3686 if ($guest_agent->{enabled
}) {
3687 my $qgasocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid, 1);
3688 push @$devices, '-chardev', "socket,path=$qgasocket,server=on,wait=off,id=qga0";
3690 if (!$guest_agent->{type
} || $guest_agent->{type
} eq 'virtio') {
3691 my $pciaddr = print_pci_addr
("qga0", $bridges, $arch, $machine_type);
3692 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3693 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3694 } elsif ($guest_agent->{type
} eq 'isa') {
3695 push @$devices, '-device', "isa-serial,chardev=qga0";
3699 my $rng = $conf->{rng0
} ? parse_rng
($conf->{rng0
}) : undef;
3700 if ($rng && $version_guard->(4, 1, 2)) {
3701 check_rng_source
($rng->{source
});
3703 my $max_bytes = $rng->{max_bytes
} // $rng_fmt->{max_bytes
}->{default};
3704 my $period = $rng->{period
} // $rng_fmt->{period
}->{default};
3705 my $limiter_str = "";
3707 $limiter_str = ",max-bytes=$max_bytes,period=$period";
3710 my $rng_addr = print_pci_addr
("rng0", $bridges, $arch, $machine_type);
3711 push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
3712 push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
3720 for (my $i = 1; $i < $qxlnum; $i++){
3721 push @$devices, '-device', print_vga_device
(
3722 $conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
3725 # assume other OS works like Linux
3726 my ($ram, $vram) = ("134217728", "67108864");
3727 if ($vga->{memory
}) {
3728 $ram = PVE
::Tools
::convert_size
($qxlnum*4*$vga->{memory
}, 'mb' => 'b');
3729 $vram = PVE
::Tools
::convert_size
($qxlnum*2*$vga->{memory
}, 'mb' => 'b');
3731 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3732 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3736 my $pciaddr = print_pci_addr
("spice", $bridges, $arch, $machine_type);
3738 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
3739 my @nodeaddrs = PVE
::Tools
::getaddrinfo_all
('localhost', family
=> $pfamily);
3740 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3742 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3743 push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
3744 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3746 my $localhost = PVE
::Network
::addr_to_ip
($nodeaddrs[0]->{addr
});
3747 $spice_port = PVE
::Tools
::next_spice_port
($pfamily, $localhost);
3749 my $spice_enhancement_str = $conf->{spice_enhancements
} // '';
3750 my $spice_enhancement = parse_property_string
($spice_enhancements_fmt, $spice_enhancement_str);
3751 if ($spice_enhancement->{foldersharing
}) {
3752 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3753 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3756 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3757 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}"
3758 if $spice_enhancement->{videostreaming
};
3760 push @$devices, '-spice', "$spice_opts";
3763 # enable balloon by default, unless explicitly disabled
3764 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
3765 my $pciaddr = print_pci_addr
("balloon0", $bridges, $arch, $machine_type);
3766 push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr";
3769 if ($conf->{watchdog
}) {
3770 my $wdopts = parse_watchdog
($conf->{watchdog
});
3771 my $pciaddr = print_pci_addr
("watchdog", $bridges, $arch, $machine_type);
3772 my $watchdog = $wdopts->{model
} || 'i6300esb';
3773 push @$devices, '-device', "$watchdog$pciaddr";
3774 push @$devices, '-watchdog-action', $wdopts->{action
} if $wdopts->{action
};
3778 my $scsicontroller = {};
3779 my $ahcicontroller = {};
3780 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : $defaults->{scsihw
};
3782 # Add iscsi initiator name if available
3783 if (my $initiator = get_initiator_name
()) {
3784 push @$devices, '-iscsi', "initiator-name=$initiator";
3787 PVE
::QemuConfig-
>foreach_volume($conf, sub {
3788 my ($ds, $drive) = @_;
3790 if (PVE
::Storage
::parse_volume_id
($drive->{file
}, 1)) {
3791 check_volume_storage_type
($storecfg, $drive->{file
});
3792 push @$vollist, $drive->{file
};
3795 # ignore efidisk here, already added in bios/fw handling code above
3796 return if $drive->{interface
} eq 'efidisk';
3798 return if $drive->{interface
} eq 'tpmstate';
3800 $use_virtio = 1 if $ds =~ m/^virtio/;
3802 $drive->{bootindex
} = $bootorder->{$ds} if $bootorder->{$ds};
3804 if ($drive->{interface
} eq 'virtio'){
3805 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread
};
3808 if ($drive->{interface
} eq 'scsi') {
3810 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
3812 die "scsi$drive->{index}: machine version 4.1~pve2 or higher is required to use more than 14 SCSI disks\n"
3813 if $drive->{index} > 13 && !&$version_guard(4, 1, 2);
3815 my $pciaddr = print_pci_addr
("$controller_prefix$controller", $bridges, $arch, $machine_type);
3816 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ?
"virtio-scsi-pci" : $scsihw;
3819 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{iothread
}){
3820 $iothread .= ",iothread=iothread-$controller_prefix$controller";
3821 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
3822 } elsif ($drive->{iothread
}) {
3823 warn "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n";
3827 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{queues
}){
3828 $queues = ",num_queues=$drive->{queues}";
3831 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues"
3832 if !$scsicontroller->{$controller};
3833 $scsicontroller->{$controller}=1;
3836 if ($drive->{interface
} eq 'sata') {
3837 my $controller = int($drive->{index} / $PVE::QemuServer
::Drive
::MAX_SATA_DISKS
);
3838 my $pciaddr = print_pci_addr
("ahci$controller", $bridges, $arch, $machine_type);
3839 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr"
3840 if !$ahcicontroller->{$controller};
3841 $ahcicontroller->{$controller}=1;
3844 my $pbs_conf = $pbs_backing->{$ds};
3845 my $pbs_name = undef;
3847 $pbs_name = "drive-$ds-pbs";
3848 push @$devices, '-blockdev', print_pbs_blockdev
($pbs_conf, $pbs_name);
3851 my $drive_cmd = print_drive_commandline_full
(
3852 $storecfg, $vmid, $drive, $pbs_name, min_version
($kvmver, 6, 0));
3854 # extra protection for templates, but SATA and IDE don't support it..
3855 $drive_cmd .= ',readonly=on' if drive_is_read_only
($conf, $drive);
3857 push @$devices, '-drive',$drive_cmd;
3858 push @$devices, '-device', print_drivedevice_full
(
3859 $storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
3862 for (my $i = 0; $i < $MAX_NETS; $i++) {
3863 my $netname = "net$i";
3865 next if !$conf->{$netname};
3866 my $d = parse_net
($conf->{$netname});
3869 $use_virtio = 1 if $d->{model
} eq 'virtio';
3871 $d->{bootindex
} = $bootorder->{$netname} if $bootorder->{$netname};
3873 my $netdevfull = print_netdev_full
($vmid, $conf, $arch, $d, $netname);
3874 push @$devices, '-netdev', $netdevfull;
3876 my $netdevicefull = print_netdevice_full
(
3877 $vmid, $conf, $d, $netname, $bridges, $use_old_bios_files, $arch, $machine_type);
3879 push @$devices, '-device', $netdevicefull;
3882 if ($conf->{ivshmem
}) {
3883 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
3887 $bus = print_pcie_addr
("ivshmem");
3889 $bus = print_pci_addr
("ivshmem", $bridges, $arch, $machine_type);
3892 my $ivshmem_name = $ivshmem->{name
} // $vmid;
3893 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
3895 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
3896 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path"
3897 .",size=$ivshmem->{size}M";
3900 # pci.4 is nested in pci.1
3901 $bridges->{1} = 1 if $bridges->{4};
3903 if (!$q35) { # add pci bridges
3904 if (min_version
($machine_version, 2, 3)) {
3908 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
3911 for my $k (sort {$b cmp $a} keys %$bridges) {
3912 next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
3915 if ($k == 2 && $legacy_igd) {
3918 my $pciaddr = print_pci_addr
("pci.$k_name", undef, $arch, $machine_type);
3919 my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
3921 if ($q35) { # add after -readconfig pve-q35.cfg
3922 splice @$devices, 2, 0, '-device', $devstr;
3924 unshift @$devices, '-device', $devstr if $k > 0;
3929 push @$machineFlags, 'accel=tcg';
3932 my $machine_type_min = $machine_type;
3933 if ($add_pve_version) {
3934 $machine_type_min =~ s/\+pve\d+$//;
3935 $machine_type_min .= "+pve$required_pve_version";
3937 push @$machineFlags, "type=${machine_type_min}";
3939 push @$cmd, @$devices;
3940 push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
3941 push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
3942 push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags);
3944 if (my $vmstate = $conf->{vmstate
}) {
3945 my $statepath = PVE
::Storage
::path
($storecfg, $vmstate);
3946 push @$vollist, $vmstate;
3947 push @$cmd, '-loadstate', $statepath;
3948 print "activating and using '$vmstate' as vmstate\n";
3951 if (PVE
::QemuConfig-
>is_template($conf)) {
3952 # needed to workaround base volumes being read-only
3953 push @$cmd, '-snapshot';
3957 if ($conf->{args
}) {
3958 my $aa = PVE
::Tools
::split_args
($conf->{args
});
3962 return wantarray ?
($cmd, $vollist, $spice_port) : $cmd;
3965 sub check_rng_source
{
3968 # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
3969 die "cannot create VirtIO RNG device: source file '$source' doesn't exist\n"
3972 my $rng_current = '/sys/devices/virtual/misc/hw_random/rng_current';
3973 if ($source eq '/dev/hwrng' && file_read_firstline
($rng_current) eq 'none') {
3974 # Needs to abort, otherwise QEMU crashes on first rng access. Note that rng_current cannot
3975 # be changed to 'none' manually, so once the VM is past this point, it's no longer an issue.
3976 die "Cannot start VM with passed-through RNG device: '/dev/hwrng' exists, but"
3977 ." '$rng_current' is set to 'none'. Ensure that a compatible hardware-RNG is attached"
3985 my $res = mon_cmd
($vmid, 'query-spice');
3987 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
3990 sub vm_devices_list
{
3993 my $res = mon_cmd
($vmid, 'query-pci');
3994 my $devices_to_check = [];
3996 foreach my $pcibus (@$res) {
3997 push @$devices_to_check, @{$pcibus->{devices
}},
4000 while (@$devices_to_check) {
4002 for my $d (@$devices_to_check) {
4003 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
4004 next if !$d->{'pci_bridge'};
4006 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices
}});
4007 push @$to_check, @{$d->{'pci_bridge'}->{devices
}};
4009 $devices_to_check = $to_check;
4012 my $resblock = mon_cmd
($vmid, 'query-block');
4013 foreach my $block (@$resblock) {
4014 if($block->{device
} =~ m/^drive-(\S+)/){
4019 my $resmice = mon_cmd
($vmid, 'query-mice');
4020 foreach my $mice (@$resmice) {
4021 if ($mice->{name
} eq 'QEMU HID Tablet') {
4022 $devices->{tablet
} = 1;
4027 # for usb devices there is no query-usb
4028 # but we can iterate over the entries in
4029 # qom-list path=/machine/peripheral
4030 my $resperipheral = mon_cmd
($vmid, 'qom-list', path
=> '/machine/peripheral');
4031 foreach my $per (@$resperipheral) {
4032 if ($per->{name
} =~ m/^usb\d+$/) {
4033 $devices->{$per->{name
}} = 1;
4041 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4043 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
4045 my $devices_list = vm_devices_list
($vmid);
4046 return 1 if defined($devices_list->{$deviceid});
4048 # add PCI bridge if we need it for the device
4049 qemu_add_pci_bridge
($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type);
4051 if ($deviceid eq 'tablet') {
4052 qemu_deviceadd
($vmid, print_tabletdevice_full
($conf, $arch));
4053 } elsif ($deviceid eq 'keyboard') {
4054 qemu_deviceadd
($vmid, print_keyboarddevice_full
($conf, $arch));
4055 } elsif ($deviceid =~ m/^usb(\d+)$/) {
4056 die "usb hotplug currently not reliable\n";
4057 # since we can't reliably hot unplug all added usb devices and usb
4058 # passthrough breaks live migration we disable usb hotplugging for now
4059 #qemu_deviceadd($vmid, PVE::QemuServer::USB::print_usbdevice_full($conf, $deviceid, $device));
4060 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4061 qemu_iothread_add
($vmid, $deviceid, $device);
4063 qemu_driveadd
($storecfg, $vmid, $device);
4064 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4066 qemu_deviceadd
($vmid, $devicefull);
4067 eval { qemu_deviceaddverify
($vmid, $deviceid); };
4069 eval { qemu_drivedel
($vmid, $deviceid); };
4073 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4074 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : "lsi";
4075 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4076 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ?
"virtio-scsi-pci" : $scsihw;
4078 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
4080 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread
}) {
4081 qemu_iothread_add
($vmid, $deviceid, $device);
4082 $devicefull .= ",iothread=iothread-$deviceid";
4085 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues
}) {
4086 $devicefull .= ",num_queues=$device->{queues}";
4089 qemu_deviceadd
($vmid, $devicefull);
4090 qemu_deviceaddverify
($vmid, $deviceid);
4091 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4092 qemu_findorcreatescsihw
($storecfg,$conf, $vmid, $device, $arch, $machine_type);
4093 qemu_driveadd
($storecfg, $vmid, $device);
4095 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
4096 eval { qemu_deviceadd
($vmid, $devicefull); };
4098 eval { qemu_drivedel
($vmid, $deviceid); };
4102 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4103 return if !qemu_netdevadd
($vmid, $conf, $arch, $device, $deviceid);
4105 my $machine_type = PVE
::QemuServer
::Machine
::qemu_machine_pxe
($vmid, $conf);
4106 my $use_old_bios_files = undef;
4107 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
4109 my $netdevicefull = print_netdevice_full
(
4110 $vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type);
4111 qemu_deviceadd
($vmid, $netdevicefull);
4113 qemu_deviceaddverify
($vmid, $deviceid);
4114 qemu_set_link_status
($vmid, $deviceid, !$device->{link_down
});
4117 eval { qemu_netdevdel
($vmid, $deviceid); };
4121 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
4123 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4124 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
4126 qemu_deviceadd
($vmid, $devicefull);
4127 qemu_deviceaddverify
($vmid, $deviceid);
4129 die "can't hotplug device '$deviceid'\n";
4135 # fixme: this should raise exceptions on error!
4136 sub vm_deviceunplug
{
4137 my ($vmid, $conf, $deviceid) = @_;
4139 my $devices_list = vm_devices_list
($vmid);
4140 return 1 if !defined($devices_list->{$deviceid});
4142 my $bootdisks = PVE
::QemuServer
::Drive
::get_bootdisks
($conf);
4143 die "can't unplug bootdisk '$deviceid'\n" if grep {$_ eq $deviceid} @$bootdisks;
4145 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard') {
4146 qemu_devicedel
($vmid, $deviceid);
4147 } elsif ($deviceid =~ m/^usb\d+$/) {
4148 die "usb hotplug currently not reliable\n";
4149 # when unplugging usb devices this way, there may be remaining usb
4150 # controllers/hubs so we disable it for now
4151 #qemu_devicedel($vmid, $deviceid);
4152 #qemu_devicedelverify($vmid, $deviceid);
4153 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4154 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4156 qemu_devicedel
($vmid, $deviceid);
4157 qemu_devicedelverify
($vmid, $deviceid);
4158 qemu_drivedel
($vmid, $deviceid);
4159 qemu_iothread_del
($vmid, $deviceid, $device);
4160 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4161 qemu_devicedel
($vmid, $deviceid);
4162 qemu_devicedelverify
($vmid, $deviceid);
4163 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4164 my $device = parse_drive
($deviceid, $conf->{$deviceid});
4166 qemu_devicedel
($vmid, $deviceid);
4167 qemu_drivedel
($vmid, $deviceid);
4168 qemu_deletescsihw
($conf, $vmid, $deviceid);
4170 qemu_iothread_del
($vmid, "virtioscsi$device->{index}", $device)
4171 if $conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single');
4172 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4173 qemu_devicedel
($vmid, $deviceid);
4174 qemu_devicedelverify
($vmid, $deviceid);
4175 qemu_netdevdel
($vmid, $deviceid);
4177 die "can't unplug device '$deviceid'\n";
4183 sub qemu_deviceadd
{
4184 my ($vmid, $devicefull) = @_;
4186 $devicefull = "driver=".$devicefull;
4187 my %options = split(/[=,]/, $devicefull);
4189 mon_cmd
($vmid, "device_add" , %options);
4192 sub qemu_devicedel
{
4193 my ($vmid, $deviceid) = @_;
4195 my $ret = mon_cmd
($vmid, "device_del", id
=> $deviceid);
4198 sub qemu_iothread_add
{
4199 my ($vmid, $deviceid, $device) = @_;
4201 if ($device->{iothread
}) {
4202 my $iothreads = vm_iothreads_list
($vmid);
4203 qemu_objectadd
($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4207 sub qemu_iothread_del
{
4208 my ($vmid, $deviceid, $device) = @_;
4210 if ($device->{iothread
}) {
4211 my $iothreads = vm_iothreads_list
($vmid);
4212 qemu_objectdel
($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4216 sub qemu_objectadd
{
4217 my ($vmid, $objectid, $qomtype) = @_;
4219 mon_cmd
($vmid, "object-add", id
=> $objectid, "qom-type" => $qomtype);
4224 sub qemu_objectdel
{
4225 my ($vmid, $objectid) = @_;
4227 mon_cmd
($vmid, "object-del", id
=> $objectid);
4233 my ($storecfg, $vmid, $device) = @_;
4235 my $kvmver = get_running_qemu_version
($vmid);
4236 my $io_uring = min_version
($kvmver, 6, 0);
4237 my $drive = print_drive_commandline_full
($storecfg, $vmid, $device, undef, $io_uring);
4238 $drive =~ s/\\/\\\\/g;
4239 my $ret = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "drive_add auto \"$drive\"");
4241 # If the command succeeds qemu prints: "OK
"
4242 return 1 if $ret =~ m/OK/s;
4244 die "adding drive failed
: $ret\n";
4248 my ($vmid, $deviceid) = @_;
4250 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-
$deviceid");
4253 return 1 if $ret eq "";
4255 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4256 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4258 die "deleting drive
$deviceid failed
: $ret\n";
4261 sub qemu_deviceaddverify {
4262 my ($vmid, $deviceid) = @_;
4264 for (my $i = 0; $i <= 5; $i++) {
4265 my $devices_list = vm_devices_list($vmid);
4266 return 1 if defined($devices_list->{$deviceid});
4270 die "error on hotplug device
'$deviceid'\n";
4274 sub qemu_devicedelverify {
4275 my ($vmid, $deviceid) = @_;
4277 # need to verify that the device is correctly removed as device_del
4278 # is async and empty return is not reliable
4280 for (my $i = 0; $i <= 5; $i++) {
4281 my $devices_list = vm_devices_list($vmid);
4282 return 1 if !defined($devices_list->{$deviceid});
4286 die "error on hot-unplugging device
'$deviceid'\n";
4289 sub qemu_findorcreatescsihw {
4290 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4292 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4294 my $scsihwid="$controller_prefix$controller";
4295 my $devices_list = vm_devices_list($vmid);
4297 if (!defined($devices_list->{$scsihwid})) {
4298 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4304 sub qemu_deletescsihw {
4305 my ($conf, $vmid, $opt) = @_;
4307 my $device = parse_drive($opt, $conf->{$opt});
4309 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4310 vm_deviceunplug($vmid, $conf, "virtioscsi
$device->{index}");
4314 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4316 my $devices_list = vm_devices_list($vmid);
4317 foreach my $opt (keys %{$devices_list}) {
4318 if (is_valid_drivename($opt)) {
4319 my $drive = parse_drive($opt, $conf->{$opt});
4320 if ($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4326 my $scsihwid="scsihw
$controller";
4328 vm_deviceunplug($vmid, $conf, $scsihwid);
4333 sub qemu_add_pci_bridge {
4334 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4340 print_pci_addr($device, $bridges, $arch, $machine_type);
4342 while (my ($k, $v) = each %$bridges) {
4345 return 1 if !defined($bridgeid) || $bridgeid < 1;
4347 my $bridge = "pci
.$bridgeid";
4348 my $devices_list = vm_devices_list($vmid);
4350 if (!defined($devices_list->{$bridge})) {
4351 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4357 sub qemu_set_link_status {
4358 my ($vmid, $device, $up) = @_;
4360 mon_cmd($vmid, "set_link
", name => $device,
4361 up => $up ? JSON::true : JSON::false);
4364 sub qemu_netdevadd {
4365 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4367 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4368 my %options = split(/[=,]/, $netdev);
4370 if (defined(my $vhost = $options{vhost})) {
4371 $options{vhost} = JSON::boolean(PVE::JSONSchema::parse_boolean($vhost));
4374 if (defined(my $queues = $options{queues})) {
4375 $options{queues} = $queues + 0;
4378 mon_cmd($vmid, "netdev_add
", %options);
4382 sub qemu_netdevdel {
4383 my ($vmid, $deviceid) = @_;
4385 mon_cmd($vmid, "netdev_del
", id => $deviceid);
4388 sub qemu_usb_hotplug {
4389 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4393 # remove the old one first
4394 vm_deviceunplug($vmid, $conf, $deviceid);
4396 # check if xhci controller is necessary and available
4397 if ($device->{usb3}) {
4399 my $devicelist = vm_devices_list($vmid);
4401 if (!$devicelist->{xhci}) {
4402 my $pciaddr = print_pci_addr("xhci
", undef, $arch, $machine_type);
4403 qemu_deviceadd($vmid, "nec-usb-xhci
,id
=xhci
$pciaddr");
4406 my $d = parse_usb_device($device->{host});
4407 $d->{usb3} = $device->{usb3};
4410 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d, $arch, $machine_type);
4413 sub qemu_cpu_hotplug {
4414 my ($vmid, $conf, $vcpus) = @_;
4416 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
4419 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4420 $sockets = $conf->{sockets} if $conf->{sockets};
4421 my $cores = $conf->{cores} || 1;
4422 my $maxcpus = $sockets * $cores;
4424 $vcpus = $maxcpus if !$vcpus;
4426 die "you can
't add more vcpus than maxcpus\n"
4427 if $vcpus > $maxcpus;
4429 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4431 if ($vcpus < $currentvcpus) {
4433 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4435 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4436 qemu_devicedel($vmid, "cpu$i");
4438 my $currentrunningvcpus = undef;
4440 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4441 last if scalar(@{$currentrunningvcpus}) == $i-1;
4442 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4446 #update conf after each succesfull cpu unplug
4447 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4448 PVE::QemuConfig->write_config($vmid, $conf);
4451 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4457 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4458 die "vcpus in running vm does not match its configuration\n"
4459 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4461 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4463 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4464 my $cpustr = print_cpu_device($conf, $i);
4465 qemu_deviceadd($vmid, $cpustr);
4468 my $currentrunningvcpus = undef;
4470 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4471 last if scalar(@{$currentrunningvcpus}) == $i;
4472 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4476 #update conf after each succesfull cpu hotplug
4477 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4478 PVE::QemuConfig->write_config($vmid, $conf);
4482 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4483 mon_cmd($vmid, "cpu-add", id => int($i));
4488 sub qemu_block_set_io_throttle {
4489 my ($vmid, $deviceid,
4490 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4491 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4492 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4493 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4495 return if !check_running($vmid) ;
4497 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4499 bps_rd => int($bps_rd),
4500 bps_wr => int($bps_wr),
4502 iops_rd => int($iops_rd),
4503 iops_wr => int($iops_wr),
4504 bps_max => int($bps_max),
4505 bps_rd_max => int($bps_rd_max),
4506 bps_wr_max => int($bps_wr_max),
4507 iops_max => int($iops_max),
4508 iops_rd_max => int($iops_rd_max),
4509 iops_wr_max => int($iops_wr_max),
4510 bps_max_length => int($bps_max_length),
4511 bps_rd_max_length => int($bps_rd_max_length),
4512 bps_wr_max_length => int($bps_wr_max_length),
4513 iops_max_length => int($iops_max_length),
4514 iops_rd_max_length => int($iops_rd_max_length),
4515 iops_wr_max_length => int($iops_wr_max_length),
4520 sub qemu_block_resize {
4521 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4523 my $running = check_running($vmid);
4525 $size = 0 if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4527 return if !$running;
4529 my $padding = (1024 - $size % 1024) % 1024;
4530 $size = $size + $padding;
4535 device => $deviceid,
4541 sub qemu_volume_snapshot {
4542 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4544 my $running = check_running($vmid);
4546 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4547 mon_cmd($vmid, 'blockdev-snapshot-internal-sync
', device => $deviceid, name => $snap);
4549 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4553 sub qemu_volume_snapshot_delete {
4554 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4556 my $running = check_running($vmid);
4561 my $conf = PVE::QemuConfig->load_config($vmid);
4562 PVE::QemuConfig->foreach_volume($conf, sub {
4563 my ($ds, $drive) = @_;
4564 $running = 1 if $drive->{file} eq $volid;
4568 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
4569 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync
', device => $deviceid, name => $snap);
4571 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4575 sub set_migration_caps {
4576 my ($vmid, $savevm) = @_;
4578 my $qemu_support = eval { mon_cmd($vmid, "query-proxmox-support") };
4580 my $bitmap_prop = $savevm ? 'pbs-dirty-bitmap-savevm
' : 'pbs-dirty-bitmap-migration
';
4581 my $dirty_bitmaps = $qemu_support->{$bitmap_prop} ? 1 : 0;
4586 "auto-converge" => 1,
4588 "x-rdma-pin-all" => 0,
4591 "dirty-bitmaps" => $dirty_bitmaps,
4594 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4596 for my $supported_capability (@$supported_capabilities) {
4598 capability => $supported_capability->{capability},
4599 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4603 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4607 my ($conf, $func, @param) = @_;
4611 my $test_volid = sub {
4612 my ($key, $drive, $snapname) = @_;
4614 my $volid = $drive->{file};
4617 $volhash->{$volid}->{cdrom} //= 1;
4618 $volhash->{$volid}->{cdrom} = 0 if !drive_is_cdrom($drive);
4620 my $replicate = $drive->{replicate} // 1;
4621 $volhash->{$volid}->{replicate} //= 0;
4622 $volhash->{$volid}->{replicate} = 1 if $replicate;
4624 $volhash->{$volid}->{shared} //= 0;
4625 $volhash->{$volid}->{shared} = 1 if $drive->{shared};
4627 $volhash->{$volid}->{referenced_in_config} //= 0;
4628 $volhash->{$volid}->{referenced_in_config} = 1 if !defined($snapname);
4630 $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
4631 if defined($snapname);
4633 my $size = $drive->{size};
4634 $volhash->{$volid}->{size} //= $size if $size;
4636 $volhash->{$volid}->{is_vmstate} //= 0;
4637 $volhash->{$volid}->{is_vmstate} = 1 if $key eq 'vmstate
';
4639 $volhash->{$volid}->{is_tpmstate} //= 0;
4640 $volhash->{$volid}->{is_tpmstate} = 1 if $key eq 'tpmstate0
';
4642 $volhash->{$volid}->{is_unused} //= 0;
4643 $volhash->{$volid}->{is_unused} = 1 if $key =~ /^unused\d+$/;
4645 $volhash->{$volid}->{drivename} = $key if is_valid_drivename($key);
4648 my $include_opts = {
4649 extra_keys => ['vmstate
'],
4650 include_unused => 1,
4653 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $test_volid);
4654 foreach my $snapname (keys %{$conf->{snapshots}}) {
4655 my $snap = $conf->{snapshots}->{$snapname};
4656 PVE::QemuConfig->foreach_volume_full($snap, $include_opts, $test_volid, $snapname);
4659 foreach my $volid (keys %$volhash) {
4660 &$func($volid, $volhash->{$volid}, @param);
4664 my $fast_plug_option = {
4672 'vmstatestorage
' => 1,
4677 # hotplug changes in [PENDING]
4678 # $selection hash can be used to only apply specified options, for
4679 # example: { cores => 1 } (only apply changed 'cores
')
4680 # $errors ref is used to return error messages
4681 sub vmconfig_hotplug_pending {
4682 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4684 my $defaults = load_defaults();
4685 my $arch = get_vm_arch($conf);
4686 my $machine_type = get_vm_machine($conf, undef, $arch);
4688 # commit values which do not have any impact on running VM first
4689 # Note: those option cannot raise errors, we we do not care about
4690 # $selection and always apply them.
4692 my $add_error = sub {
4693 my ($opt, $msg) = @_;
4694 $errors->{$opt} = "hotplug problem - $msg";
4698 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4699 if ($fast_plug_option->{$opt}) {
4700 $conf->{$opt} = $conf->{pending}->{$opt};
4701 delete $conf->{pending}->{$opt};
4707 PVE::QemuConfig->write_config($vmid, $conf);
4710 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
4712 my $cgroup = PVE::QemuServer::CGroup->new($vmid);
4713 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4714 foreach my $opt (sort keys %$pending_delete_hash) {
4715 next if $selection && !$selection->{$opt};
4716 my $force = $pending_delete_hash->{$opt}->{force};
4718 if ($opt eq 'hotplug
') {
4719 die "skip\n" if ($conf->{hotplug} =~ /memory/);
4720 } elsif ($opt eq 'tablet
') {
4721 die "skip\n" if !$hotplug_features->{usb};
4722 if ($defaults->{tablet}) {
4723 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4724 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4725 if $arch eq 'aarch64
';
4727 vm_deviceunplug($vmid, $conf, 'tablet
');
4728 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4730 } elsif ($opt =~ m/^usb\d+/) {
4732 # since we cannot reliably hot unplug usb devices we are disabling it
4733 #die "skip\n" if !$hotplug_features->{usb} || $conf->{$opt} =~ m/spice/i;
4734 #vm_deviceunplug($vmid, $conf, $opt);
4735 } elsif ($opt eq 'vcpus
') {
4736 die "skip\n" if !$hotplug_features->{cpu};
4737 qemu_cpu_hotplug($vmid, $conf, undef);
4738 } elsif ($opt eq 'balloon
') {
4739 # enable balloon device is not hotpluggable
4740 die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
4741 # here we reset the ballooning value to memory
4742 my $balloon = $conf->{memory} || $defaults->{memory};
4743 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4744 } elsif ($fast_plug_option->{$opt}) {
4746 } elsif ($opt =~ m/^net(\d+)$/) {
4747 die "skip\n" if !$hotplug_features->{network};
4748 vm_deviceunplug($vmid, $conf, $opt);
4749 } elsif (is_valid_drivename($opt)) {
4750 die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
4751 vm_deviceunplug($vmid, $conf, $opt);
4752 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4753 } elsif ($opt =~ m/^memory$/) {
4754 die "skip\n" if !$hotplug_features->{memory};
4755 PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
4756 } elsif ($opt eq 'cpuunits
') {
4757 $cgroup->change_cpu_shares(undef, 1024);
4758 } elsif ($opt eq 'cpulimit
') {
4759 $cgroup->change_cpu_quota(undef, undef); # reset, cgroup module can better decide values
4765 &$add_error($opt, $err) if $err ne "skip\n";
4767 delete $conf->{$opt};
4768 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4772 my ($apply_pending_cloudinit, $apply_pending_cloudinit_done);
4773 $apply_pending_cloudinit = sub {
4774 return if $apply_pending_cloudinit_done; # once is enough
4775 $apply_pending_cloudinit_done = 1; # once is enough
4777 my ($key, $value) = @_;
4779 my @cloudinit_opts = keys %$confdesc_cloudinit;
4780 foreach my $opt (keys %{$conf->{pending}}) {
4781 next if !grep { $_ eq $opt } @cloudinit_opts;
4782 $conf->{$opt} = delete $conf->{pending}->{$opt};
4785 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4786 foreach my $opt (sort keys %$pending_delete_hash) {
4787 next if !grep { $_ eq $opt } @cloudinit_opts;
4788 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4789 delete $conf->{$opt};
4792 my $new_conf = { %$conf };
4793 $new_conf->{$key} = $value;
4794 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($new_conf, $vmid);
4797 foreach my $opt (keys %{$conf->{pending}}) {
4798 next if $selection && !$selection->{$opt};
4799 my $value = $conf->{pending}->{$opt};
4801 if ($opt eq 'hotplug
') {
4802 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/);
4803 } elsif ($opt eq 'tablet
') {
4804 die "skip\n" if !$hotplug_features->{usb};
4806 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4807 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4808 if $arch eq 'aarch64
';
4809 } elsif ($value == 0) {
4810 vm_deviceunplug($vmid, $conf, 'tablet
');
4811 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4813 } elsif ($opt =~ m/^usb\d+$/) {
4815 # since we cannot reliably hot unplug usb devices we disable it for now
4816 #die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
4817 #my $d = eval { parse_property_string($usbdesc->{format}, $value) };
4818 #die "skip\n" if !$d;
4819 #qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d, $arch, $machine_type);
4820 } elsif ($opt eq 'vcpus
') {
4821 die "skip\n" if !$hotplug_features->{cpu};
4822 qemu_cpu_hotplug($vmid, $conf, $value);
4823 } elsif ($opt eq 'balloon
') {
4824 # enable/disable balloning device is not hotpluggable
4825 my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon});
4826 my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon});
4827 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
4829 # allow manual ballooning if shares is set to zero
4830 if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
4831 my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
4832 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4834 } elsif ($opt =~ m/^net(\d+)$/) {
4835 # some changes can be done without hotplug
4836 vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
4837 $vmid, $opt, $value, $arch, $machine_type);
4838 } elsif (is_valid_drivename($opt)) {
4839 die "skip\n" if $opt eq 'efidisk0
' || $opt eq 'tpmstate0
';
4840 # some changes can be done without hotplug
4841 my $drive = parse_drive($opt, $value);
4842 if (drive_is_cloudinit($drive)) {
4843 &$apply_pending_cloudinit($opt, $value);
4845 vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
4846 $vmid, $opt, $value, $arch, $machine_type);
4847 } elsif ($opt =~ m/^memory$/) { #dimms
4848 die "skip\n" if !$hotplug_features->{memory};
4849 $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
4850 } elsif ($opt eq 'cpuunits
') {
4851 $cgroup->change_cpu_shares($conf->{pending}->{$opt}, 1024);
4852 } elsif ($opt eq 'cpulimit
') {
4853 my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
4854 $cgroup->change_cpu_quota($cpulimit, 100000);
4855 } elsif ($opt eq 'agent
') {
4856 vmconfig_update_agent($conf, $opt, $value);
4858 die "skip\n"; # skip non-hot-pluggable options
4862 &$add_error($opt, $err) if $err ne "skip\n";
4864 $conf->{$opt} = $value;
4865 delete $conf->{pending}->{$opt};
4869 PVE::QemuConfig->write_config($vmid, $conf);
4872 sub try_deallocate_drive {
4873 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
4875 if (($force || $key =~ /^unused/) && !drive_is_cdrom($drive, 1)) {
4876 my $volid = $drive->{file};
4877 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
4878 my $sid = PVE::Storage::parse_volume_id($volid);
4879 $rpcenv->check($authuser, "/storage/$sid", ['Datastore
.AllocateSpace
']);
4881 # check if the disk is really unused
4882 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
4883 if PVE::QemuServer::Drive::is_volume_in_use($storecfg, $conf, $key, $volid);
4884 PVE::Storage::vdisk_free($storecfg, $volid);
4887 # If vm is not owner of this disk remove from config
4895 sub vmconfig_delete_or_detach_drive {
4896 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
4898 my $drive = parse_drive($opt, $conf->{$opt});
4900 my $rpcenv = PVE::RPCEnvironment::get();
4901 my $authuser = $rpcenv->get_user();
4904 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM
.Config
.Disk
']);
4905 try_deallocate_drive($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
4907 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $drive);
4913 sub vmconfig_apply_pending {
4914 my ($vmid, $conf, $storecfg, $errors) = @_;
4916 my $add_apply_error = sub {
4917 my ($opt, $msg) = @_;
4918 my $err_msg = "unable to apply pending change $opt : $msg";
4919 $errors->{$opt} = $err_msg;
4925 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4926 foreach my $opt (sort keys %$pending_delete_hash) {
4927 my $force = $pending_delete_hash->{$opt}->{force};
4929 if ($opt =~ m/^unused/) {
4930 die "internal error";
4931 } elsif (defined($conf->{$opt}) && is_valid_drivename($opt)) {
4932 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4936 $add_apply_error->($opt, $err);
4938 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4939 delete $conf->{$opt};
4943 PVE::QemuConfig->cleanup_pending($conf);
4945 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4946 next if $opt eq 'delete'; # just to be sure
4948 if (defined($conf->{$opt}) && is_valid_drivename($opt)) {
4949 vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
4953 $add_apply_error->($opt, $err);
4955 $conf->{$opt} = delete $conf->{pending}->{$opt};
4959 # write all changes at once to avoid unnecessary i/o
4960 PVE::QemuConfig->write_config($vmid, $conf);
4963 sub vmconfig_update_net {
4964 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
4966 my $newnet = parse_net($value);
4968 if ($conf->{$opt}) {
4969 my $oldnet = parse_net($conf->{$opt});
4971 if (safe_string_ne($oldnet->{model}, $newnet->{model}) ||
4972 safe_string_ne($oldnet->{macaddr}, $newnet->{macaddr}) ||
4973 safe_num_ne($oldnet->{queues}, $newnet->{queues}) ||
4974 !($newnet->{bridge} && $oldnet->{bridge})) { # bridge/nat mode change
4976 # for non online change, we try to hot-unplug
4977 die "skip\n" if !$hotplug;
4978 vm_deviceunplug($vmid, $conf, $opt);
4981 die "internal error" if $opt !~ m/net(\d+)/;
4982 my $iface = "tap${vmid}i$1";
4984 if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
4985 safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
4986 safe_string_ne($oldnet->{trunks}, $newnet->{trunks}) ||
4987 safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
4988 PVE::Network::tap_unplug($iface);
4991 PVE::Network::SDN::Zones::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
4993 PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
4995 } elsif (safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
4996 # Rate can be applied on its own but any change above needs to
4997 # include the rate in tap_plug since OVS resets everything.
4998 PVE::Network::tap_rate_limit($iface, $newnet->{rate});
5001 if (safe_string_ne($oldnet->{link_down}, $newnet->{link_down})) {
5002 qemu_set_link_status($vmid, $opt, !$newnet->{link_down});
5010 vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
5016 sub vmconfig_update_agent {
5017 my ($conf, $opt, $value) = @_;
5019 die "skip\n" if !$conf->{$opt};
5021 my $hotplug_options = { fstrim_cloned_disks => 1 };
5023 my $old_agent = parse_guest_agent($conf);
5024 my $agent = parse_guest_agent({$opt => $value});
5026 for my $option (keys %$agent) { # added/changed options
5027 next if defined($hotplug_options->{$option});
5028 die "skip\n" if safe_string_ne($agent->{$option}, $old_agent->{$option});
5031 for my $option (keys %$old_agent) { # removed options
5032 next if defined($hotplug_options->{$option});
5033 die "skip\n" if safe_string_ne($old_agent->{$option}, $agent->{$option});
5036 return; # either no actual change (e.g., format string reordered) or just hotpluggable changes
5039 sub vmconfig_update_disk {
5040 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5042 my $drive = parse_drive($opt, $value);
5044 if ($conf->{$opt} && (my $old_drive = parse_drive($opt, $conf->{$opt}))) {
5045 my $media = $drive->{media} || 'disk
';
5046 my $oldmedia = $old_drive->{media} || 'disk
';
5047 die "unable to change media type\n" if $media ne $oldmedia;
5049 if (!drive_is_cdrom($old_drive)) {
5051 if ($drive->{file} ne $old_drive->{file}) {
5053 die "skip\n" if !$hotplug;
5055 # unplug and register as unused
5056 vm_deviceunplug($vmid, $conf, $opt);
5057 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive)
5060 # update existing disk
5062 # skip non hotpluggable value
5063 if (safe_string_ne($drive->{discard}, $old_drive->{discard}) ||
5064 safe_string_ne($drive->{iothread}, $old_drive->{iothread}) ||
5065 safe_string_ne($drive->{queues}, $old_drive->{queues}) ||
5066 safe_string_ne($drive->{cache}, $old_drive->{cache}) ||
5067 safe_string_ne($drive->{ssd}, $old_drive->{ssd})) {
5072 if (safe_num_ne($drive->{mbps}, $old_drive->{mbps}) ||
5073 safe_num_ne($drive->{mbps_rd}, $old_drive->{mbps_rd}) ||
5074 safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) ||
5075 safe_num_ne($drive->{iops}, $old_drive->{iops}) ||
5076 safe_num_ne($drive->{iops_rd}, $old_drive->{iops_rd}) ||
5077 safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr}) ||
5078 safe_num_ne($drive->{mbps_max}, $old_drive->{mbps_max}) ||
5079 safe_num_ne($drive->{mbps_rd_max}, $old_drive->{mbps_rd_max}) ||
5080 safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) ||
5081 safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) ||
5082 safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) ||
5083 safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max}) ||
5084 safe_num_ne($drive->{bps_max_length}, $old_drive->{bps_max_length}) ||
5085 safe_num_ne($drive->{bps_rd_max_length}, $old_drive->{bps_rd_max_length}) ||
5086 safe_num_ne($drive->{bps_wr_max_length}, $old_drive->{bps_wr_max_length}) ||
5087 safe_num_ne($drive->{iops_max_length}, $old_drive->{iops_max_length}) ||
5088 safe_num_ne($drive->{iops_rd_max_length}, $old_drive->{iops_rd_max_length}) ||
5089 safe_num_ne($drive->{iops_wr_max_length}, $old_drive->{iops_wr_max_length})) {
5091 qemu_block_set_io_throttle(
5093 ($drive->{mbps} || 0)*1024*1024,
5094 ($drive->{mbps_rd} || 0)*1024*1024,
5095 ($drive->{mbps_wr} || 0)*1024*1024,
5096 $drive->{iops} || 0,
5097 $drive->{iops_rd} || 0,
5098 $drive->{iops_wr} || 0,
5099 ($drive->{mbps_max} || 0)*1024*1024,
5100 ($drive->{mbps_rd_max} || 0)*1024*1024,
5101 ($drive->{mbps_wr_max} || 0)*1024*1024,
5102 $drive->{iops_max} || 0,
5103 $drive->{iops_rd_max} || 0,
5104 $drive->{iops_wr_max} || 0,
5105 $drive->{bps_max_length} || 1,
5106 $drive->{bps_rd_max_length} || 1,
5107 $drive->{bps_wr_max_length} || 1,
5108 $drive->{iops_max_length} || 1,
5109 $drive->{iops_rd_max_length} || 1,
5110 $drive->{iops_wr_max_length} || 1,
5120 if ($drive->{file} eq 'none
') {
5121 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
5122 if (drive_is_cloudinit($old_drive)) {
5123 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive);
5126 my $path = get_iso_path($storecfg, $vmid, $drive->{file});
5128 # force eject if locked
5129 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
5132 mon_cmd($vmid, "blockdev-change-medium",
5133 id => "$opt", filename => "$path");
5141 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
5143 PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
5144 vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
5147 # called in locked context by incoming migration
5148 sub vm_migrate_get_nbd_disks {
5149 my ($storecfg, $conf, $replicated_volumes) = @_;
5151 my $local_volumes = {};
5152 PVE::QemuConfig->foreach_volume($conf, sub {
5153 my ($ds, $drive) = @_;
5155 return if drive_is_cdrom($drive);
5157 my $volid = $drive->{file};
5161 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
5163 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5164 return if $scfg->{shared};
5166 # replicated disks re-use existing state via bitmap
5167 my $use_existing = $replicated_volumes->{$volid} ? 1 : 0;
5168 $local_volumes->{$ds} = [$volid, $storeid, $volname, $drive, $use_existing];
5170 return $local_volumes;
5173 # called in locked context by incoming migration
5174 sub vm_migrate_alloc_nbd_disks {
5175 my ($storecfg, $vmid, $source_volumes, $storagemap) = @_;
5180 foreach my $opt (sort keys %$source_volumes) {
5181 my ($volid, $storeid, $volname, $drive, $use_existing) = @{$source_volumes->{$opt}};
5183 if ($use_existing) {
5184 $nbd->{$opt}->{drivestr} = print_drive($drive);
5185 $nbd->{$opt}->{volid} = $volid;
5186 $nbd->{$opt}->{replicated} = 1;
5190 # If a remote storage is specified and the format of the original
5191 # volume is not available there, fall back to the default format.
5192 # Otherwise use the same format as the original.
5193 if (!$storagemap->{identity}) {
5194 $storeid = map_storage($storagemap, $storeid);
5195 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
5196 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5197 my $fileFormat = qemu_img_format($scfg, $volname);
5198 $format = (grep {$fileFormat eq $_} @{$validFormats}) ? $fileFormat : $defFormat;
5200 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5201 $format = qemu_img_format($scfg, $volname);
5204 my $size = $drive->{size} / 1024;
5205 my $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $format, undef, $size);
5206 my $newdrive = $drive;
5207 $newdrive->{format} = $format;
5208 $newdrive->{file} = $newvolid;
5209 my $drivestr = print_drive($newdrive);
5210 $nbd->{$opt}->{drivestr} = $drivestr;
5211 $nbd->{$opt}->{volid} = $newvolid;
5217 # see vm_start_nolock for parameters, additionally:
5219 # storagemap = parsed storage map for allocating NBD disks
5221 my ($storecfg, $vmid, $params, $migrate_opts) = @_;
5223 return PVE::QemuConfig->lock_config($vmid, sub {
5224 my $conf = PVE::QemuConfig->load_config($vmid, $migrate_opts->{migratedfrom});
5226 die "you can't start a vm
if it
's a template\n"
5227 if !$params->{skiptemplate} && PVE::QemuConfig->is_template($conf);
5229 my $has_suspended_lock = PVE::QemuConfig->has_lock($conf, 'suspended
');
5230 my $has_backup_lock = PVE::QemuConfig->has_lock($conf, 'backup
');
5232 my $running = check_running($vmid, undef, $migrate_opts->{migratedfrom});
5234 if ($has_backup_lock && $running) {
5235 # a backup is currently running, attempt to start the guest in the
5236 # existing QEMU instance
5237 return vm_resume($vmid);
5240 PVE::QemuConfig->check_lock($conf)
5241 if !($params->{skiplock} || $has_suspended_lock);
5243 $params->{resume} = $has_suspended_lock || defined($conf->{vmstate});
5245 die "VM $vmid already running\n" if $running;
5247 if (my $storagemap = $migrate_opts->{storagemap}) {
5248 my $replicated = $migrate_opts->{replicated_volumes};
5249 my $disks = vm_migrate_get_nbd_disks($storecfg, $conf, $replicated);
5250 $migrate_opts->{nbd} = vm_migrate_alloc_nbd_disks($storecfg, $vmid, $disks, $storagemap);
5252 foreach my $opt (keys %{$migrate_opts->{nbd}}) {
5253 $conf->{$opt} = $migrate_opts->{nbd}->{$opt}->{drivestr};
5257 return vm_start_nolock($storecfg, $vmid, $conf, $params, $migrate_opts);
5263 # statefile => 'tcp
', 'unix
' for migration or path/volid for RAM state
5264 # skiplock => 0/1, skip checking for config lock
5265 # skiptemplate => 0/1, skip checking whether VM is template
5266 # forcemachine => to force Qemu machine (rollback/migration)
5267 # forcecpu => a QEMU '-cpu
' argument string to override get_cpu_options
5268 # timeout => in seconds
5269 # paused => start VM in paused state (backup)
5270 # resume => resume from hibernation
5281 # nbd => volumes for NBD exports (vm_migrate_alloc_nbd_disks)
5282 # migratedfrom => source node
5283 # spice_ticket => used for spice migration, passed via tunnel/stdin
5284 # network => CIDR of migration network
5285 # type => secure/insecure - tunnel over encrypted connection or plain-text
5286 # nbd_proto_version => int, 0 for TCP, 1 for UNIX
5287 # replicated_volumes = which volids should be re-used with bitmaps for nbd migration
5288 sub vm_start_nolock {
5289 my ($storecfg, $vmid, $conf, $params, $migrate_opts) = @_;
5291 my $statefile = $params->{statefile};
5292 my $resume = $params->{resume};
5294 my $migratedfrom = $migrate_opts->{migratedfrom};
5295 my $migration_type = $migrate_opts->{type};
5299 # clean up leftover reboot request files
5300 eval { clear_reboot_request($vmid); };
5303 if (!$statefile && scalar(keys %{$conf->{pending}})) {
5304 vmconfig_apply_pending($vmid, $conf, $storecfg);
5305 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
5308 # don't regenerate the ISO
if the VM
is started as part of a live migration
5309 # this way we can reuse the old ISO with the correct config
5310 PVE
::QemuServer
::Cloudinit
::generate_cloudinitconfig
($conf, $vmid) if !$migratedfrom;
5312 my $defaults = load_defaults
();
5314 # set environment variable useful inside network script
5315 $ENV{PVE_MIGRATED_FROM
} = $migratedfrom if $migratedfrom;
5317 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-start', 1);
5319 my $forcemachine = $params->{forcemachine
};
5320 my $forcecpu = $params->{forcecpu
};
5322 # enforce machine and CPU type on suspended vm to ensure HW compatibility
5323 $forcemachine = $conf->{runningmachine
};
5324 $forcecpu = $conf->{runningcpu
};
5325 print "Resuming suspended VM\n";
5328 my ($cmd, $vollist, $spice_port) = config_to_command
($storecfg, $vmid,
5329 $conf, $defaults, $forcemachine, $forcecpu, $params->{'pbs-backing'});
5332 my $get_migration_ip = sub {
5333 my ($nodename) = @_;
5335 return $migration_ip if defined($migration_ip);
5337 my $cidr = $migrate_opts->{network
};
5339 if (!defined($cidr)) {
5340 my $dc_conf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5341 $cidr = $dc_conf->{migration
}->{network
};
5344 if (defined($cidr)) {
5345 my $ips = PVE
::Network
::get_local_ip_from_cidr
($cidr);
5347 die "could not get IP: no address configured on local " .
5348 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5350 die "could not get IP: multiple addresses configured on local " .
5351 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5353 $migration_ip = @$ips[0];
5356 $migration_ip = PVE
::Cluster
::remote_node_ip
($nodename, 1)
5357 if !defined($migration_ip);
5359 return $migration_ip;
5364 if ($statefile eq 'tcp') {
5365 my $localip = "localhost";
5366 my $datacenterconf = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5367 my $nodename = nodename
();
5369 if (!defined($migration_type)) {
5370 if (defined($datacenterconf->{migration
}->{type
})) {
5371 $migration_type = $datacenterconf->{migration
}->{type
};
5373 $migration_type = 'secure';
5377 if ($migration_type eq 'insecure') {
5378 $localip = $get_migration_ip->($nodename);
5379 $localip = "[$localip]" if Net
::IP
::ip_is_ipv6
($localip);
5382 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
5383 my $migrate_port = PVE
::Tools
::next_migrate_port
($pfamily);
5384 $migrate_uri = "tcp:${localip}:${migrate_port}";
5385 push @$cmd, '-incoming', $migrate_uri;
5388 } elsif ($statefile eq 'unix') {
5389 # should be default for secure migrations as a ssh TCP forward
5390 # tunnel is not deterministic reliable ready and fails regurarly
5391 # to set up in time, so use UNIX socket forwards
5392 my $socket_addr = "/run/qemu-server/$vmid.migrate";
5393 unlink $socket_addr;
5395 $migrate_uri = "unix:$socket_addr";
5397 push @$cmd, '-incoming', $migrate_uri;
5400 } elsif (-e
$statefile) {
5401 push @$cmd, '-loadstate', $statefile;
5403 my $statepath = PVE
::Storage
::path
($storecfg, $statefile);
5404 push @$vollist, $statefile;
5405 push @$cmd, '-loadstate', $statepath;
5407 } elsif ($params->{paused
}) {
5411 my $start_timeout = $params->{timeout
} // config_aware_timeout
($conf, $resume);
5413 my $pci_devices = {}; # host pci devices
5414 for (my $i = 0; $i < $PVE::QemuServer
::PCI
::MAX_HOSTPCI_DEVICES
; $i++) {
5415 my $dev = $conf->{"hostpci$i"} or next;
5416 $pci_devices->{$i} = parse_hostpci
($dev);
5419 my $pci_id_list = [ map { $_->{id
} } map { $_->{pciid
}->@* } values $pci_devices->%* ];
5420 # reserve all PCI IDs before actually doing anything with them
5421 PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_id_list, $vmid, $start_timeout);
5424 for my $id (sort keys %$pci_devices) {
5425 my $d = $pci_devices->{$id};
5426 for my $dev ($d->{pciid
}->@*) {
5427 PVE
::QemuServer
::PCI
::prepare_pci_device
($vmid, $dev->{id
}, $id, $d->{mdev
});
5432 eval { PVE
::QemuServer
::PCI
::remove_pci_reservation
($pci_id_list) };
5437 PVE
::Storage
::activate_volumes
($storecfg, $vollist);
5440 run_command
(['/bin/systemctl', 'stop', "$vmid.scope"], outfunc
=> sub{}, errfunc
=> sub{});
5442 # Issues with the above 'stop' not being fully completed are extremely rare, a very low
5443 # timeout should be more than enough here...
5444 PVE
::Systemd
::wait_for_unit_removed
("$vmid.scope", 5);
5446 my $cpuunits = get_cpuunits
($conf);
5449 timeout
=> $statefile ?
undef : $start_timeout,
5454 # when migrating, prefix QEMU output so other side can pick up any
5455 # errors that might occur and show the user
5456 if ($migratedfrom) {
5457 $run_params{quiet
} = 1;
5458 $run_params{logfunc
} = sub { print "QEMU: $_[0]\n" };
5461 my %systemd_properties = (
5462 Slice
=> 'qemu.slice',
5463 KillMode
=> 'process',
5465 TimeoutStopUSec
=> ULONG_MAX
, # infinity
5468 if (PVE
::CGroup
::cgroup_mode
() == 2) {
5469 $cpuunits = 10000 if $cpuunits >= 10000; # else we get an error
5470 $systemd_properties{CPUWeight
} = $cpuunits;
5472 $systemd_properties{CPUShares
} = $cpuunits;
5475 if (my $cpulimit = $conf->{cpulimit
}) {
5476 $systemd_properties{CPUQuota
} = int($cpulimit * 100);
5478 $systemd_properties{timeout
} = 10 if $statefile; # setting up the scope shoul be quick
5480 my $run_qemu = sub {
5481 PVE
::Tools
::run_fork
sub {
5482 PVE
::Systemd
::enter_systemd_scope
($vmid, "Proxmox VE VM $vmid", %systemd_properties);
5485 if (my $tpm = $conf->{tpmstate0
}) {
5486 # start the TPM emulator so QEMU can connect on start
5487 $tpmpid = start_swtpm
($storecfg, $vmid, $tpm, $migratedfrom);
5490 my $exitcode = run_command
($cmd, %run_params);
5493 warn "stopping swtpm instance (pid $tpmpid) due to QEMU startup error\n";
5494 kill 'TERM', $tpmpid;
5496 die "QEMU exited with code $exitcode\n";
5501 if ($conf->{hugepages
}) {
5504 my $hugepages_topology = PVE
::QemuServer
::Memory
::hugepages_topology
($conf);
5505 my $hugepages_host_topology = PVE
::QemuServer
::Memory
::hugepages_host_topology
();
5507 PVE
::QemuServer
::Memory
::hugepages_mount
();
5508 PVE
::QemuServer
::Memory
::hugepages_allocate
($hugepages_topology, $hugepages_host_topology);
5510 eval { $run_qemu->() };
5512 PVE
::QemuServer
::Memory
::hugepages_reset
($hugepages_host_topology)
5513 if !$conf->{keephugepages
};
5517 PVE
::QemuServer
::Memory
::hugepages_pre_deallocate
($hugepages_topology)
5518 if !$conf->{keephugepages
};
5520 eval { PVE
::QemuServer
::Memory
::hugepages_update_locked
($code); };
5523 eval { $run_qemu->() };
5527 # deactivate volumes if start fails
5528 eval { PVE
::Storage
::deactivate_volumes
($storecfg, $vollist); };
5529 eval { PVE
::QemuServer
::PCI
::remove_pci_reservation
($pci_id_list) };
5531 die "start failed: $err";
5534 # re-reserve all PCI IDs now that we can know the actual VM PID
5535 my $pid = PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
5536 eval { PVE
::QemuServer
::PCI
::reserve_pci_usage
($pci_id_list, $vmid, undef, $pid) };
5539 print "migration listens on $migrate_uri\n" if $migrate_uri;
5540 $res->{migrate_uri
} = $migrate_uri;
5542 if ($statefile && $statefile ne 'tcp' && $statefile ne 'unix') {
5543 eval { mon_cmd
($vmid, "cont"); };
5547 #start nbd server for storage migration
5548 if (my $nbd = $migrate_opts->{nbd
}) {
5549 my $nbd_protocol_version = $migrate_opts->{nbd_proto_version
} // 0;
5551 my $migrate_storage_uri;
5552 # nbd_protocol_version > 0 for unix socket support
5553 if ($nbd_protocol_version > 0 && $migration_type eq 'secure') {
5554 my $socket_path = "/run/qemu-server/$vmid\_nbd.migrate";
5555 mon_cmd
($vmid, "nbd-server-start", addr
=> { type
=> 'unix', data
=> { path
=> $socket_path } } );
5556 $migrate_storage_uri = "nbd:unix:$socket_path";
5558 my $nodename = nodename
();
5559 my $localip = $get_migration_ip->($nodename);
5560 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
5561 my $storage_migrate_port = PVE
::Tools
::next_migrate_port
($pfamily);
5563 mon_cmd
($vmid, "nbd-server-start", addr
=> {
5566 host
=> "${localip}",
5567 port
=> "${storage_migrate_port}",
5570 $localip = "[$localip]" if Net
::IP
::ip_is_ipv6
($localip);
5571 $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}";
5574 $res->{migrate_storage_uri
} = $migrate_storage_uri;
5576 foreach my $opt (sort keys %$nbd) {
5577 my $drivestr = $nbd->{$opt}->{drivestr
};
5578 my $volid = $nbd->{$opt}->{volid
};
5579 mon_cmd
($vmid, "nbd-server-add", device
=> "drive-$opt", writable
=> JSON
::true
);
5580 my $nbd_uri = "$migrate_storage_uri:exportname=drive-$opt";
5581 print "storage migration listens on $nbd_uri volume:$drivestr\n";
5582 print "re-using replicated volume: $opt - $volid\n"
5583 if $nbd->{$opt}->{replicated
};
5585 $res->{drives
}->{$opt} = $nbd->{$opt};
5586 $res->{drives
}->{$opt}->{nbd_uri
} = $nbd_uri;
5590 if ($migratedfrom) {
5592 set_migration_caps
($vmid);
5597 print "spice listens on port $spice_port\n";
5598 $res->{spice_port
} = $spice_port;
5599 if ($migrate_opts->{spice_ticket
}) {
5600 mon_cmd
($vmid, "set_password", protocol
=> 'spice', password
=>
5601 $migrate_opts->{spice_ticket
});
5602 mon_cmd
($vmid, "expire_password", protocol
=> 'spice', time => "+30");
5607 mon_cmd
($vmid, "balloon", value
=> $conf->{balloon
}*1024*1024)
5608 if !$statefile && $conf->{balloon
};
5610 foreach my $opt (keys %$conf) {
5611 next if $opt !~ m/^net\d+$/;
5612 my $nicconf = parse_net
($conf->{$opt});
5613 qemu_set_link_status
($vmid, $opt, 0) if $nicconf->{link_down
};
5617 mon_cmd
($vmid, 'qom-set',
5618 path
=> "machine/peripheral/balloon0",
5619 property
=> "guest-stats-polling-interval",
5620 value
=> 2) if (!defined($conf->{balloon
}) || $conf->{balloon
});
5623 print "Resumed VM, removing state\n";
5624 if (my $vmstate = $conf->{vmstate
}) {
5625 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
5626 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
5628 delete $conf->@{qw(lock vmstate runningmachine runningcpu)};
5629 PVE
::QemuConfig-
>write_config($vmid, $conf);
5632 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'post-start');
5637 sub vm_commandline
{
5638 my ($storecfg, $vmid, $snapname) = @_;
5640 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5642 my ($forcemachine, $forcecpu);
5644 my $snapshot = $conf->{snapshots
}->{$snapname};
5645 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
5647 # check for machine or CPU overrides in snapshot
5648 $forcemachine = $snapshot->{runningmachine
};
5649 $forcecpu = $snapshot->{runningcpu
};
5651 $snapshot->{digest
} = $conf->{digest
}; # keep file digest for API
5656 my $defaults = load_defaults
();
5658 my $cmd = config_to_command
($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu);
5660 return PVE
::Tools
::cmd2string
($cmd);
5664 my ($vmid, $skiplock) = @_;
5666 PVE
::QemuConfig-
>lock_config($vmid, sub {
5668 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5670 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5672 mon_cmd
($vmid, "system_reset");
5676 sub get_vm_volumes
{
5680 foreach_volid
($conf, sub {
5681 my ($volid, $attr) = @_;
5683 return if $volid =~ m
|^/|;
5685 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
5688 push @$vollist, $volid;
5694 sub vm_stop_cleanup
{
5695 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
5700 my $vollist = get_vm_volumes
($conf);
5701 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
5703 if (my $tpmdrive = $conf->{tpmstate0
}) {
5704 my $tpm = parse_drive
("tpmstate0", $tpmdrive);
5705 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($tpm->{file
}, 1);
5707 PVE
::Storage
::unmap_volume
($storecfg, $tpm->{file
});
5712 foreach my $ext (qw(mon qmp pid vnc qga)) {
5713 unlink "/var/run/qemu-server/${vmid}.$ext";
5716 if ($conf->{ivshmem
}) {
5717 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
5718 # just delete it for now, VMs which have this already open do not
5719 # are affected, but new VMs will get a separated one. If this
5720 # becomes an issue we either add some sort of ref-counting or just
5721 # add a "don't delete on stop" flag to the ivshmem format.
5722 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name
} // $vmid);
5726 foreach my $key (keys %$conf) {
5727 next if $key !~ m/^hostpci(\d+)$/;
5728 my $hostpciindex = $1;
5729 my $d = parse_hostpci
($conf->{$key});
5730 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $hostpciindex);
5732 foreach my $pci (@{$d->{pciid
}}) {
5733 my $pciid = $pci->{id
};
5734 push @$ids, $pci->{id
};
5735 PVE
::SysFSTools
::pci_cleanup_mdev_device
($pciid, $uuid);
5738 PVE
::QemuServer
::PCI
::remove_pci_reservation
($ids);
5740 vmconfig_apply_pending
($vmid, $conf, $storecfg) if $apply_pending_changes;
5742 warn $@ if $@; # avoid errors - just warn
5745 # call only in locked context
5747 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
5749 my $pid = check_running
($vmid, $nocheck);
5754 $conf = PVE
::QemuConfig-
>load_config($vmid);
5755 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5756 if (!defined($timeout) && $shutdown && $conf->{startup
}) {
5757 my $opts = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
5758 $timeout = $opts->{down
} if $opts->{down
};
5760 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-stop');
5765 if (defined($conf) && get_qga_key
($conf, 'enabled')) {
5766 mon_cmd
($vmid, "guest-shutdown", timeout
=> $timeout);
5768 mon_cmd
($vmid, "system_powerdown");
5771 mon_cmd
($vmid, "quit");
5777 $timeout = 60 if !defined($timeout);
5780 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
5785 if ($count >= $timeout) {
5787 warn "VM still running - terminating now with SIGTERM\n";
5790 die "VM quit/powerdown failed - got timeout\n";
5793 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5797 if (!check_running
($vmid, $nocheck)) {
5798 warn "Unexpected: VM shutdown command failed, but VM not running anymore..\n";
5802 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
5805 die "VM quit/powerdown failed\n";
5813 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
5818 if ($count >= $timeout) {
5819 warn "VM still running - terminating now with SIGKILL\n";
5824 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5827 # Note: use $nocheck to skip tests if VM configuration file exists.
5828 # We need that when migration VMs to other nodes (files already moved)
5829 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
5831 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
5833 $force = 1 if !defined($force) && !$shutdown;
5836 my $pid = check_running
($vmid, $nocheck, $migratedfrom);
5837 kill 15, $pid if $pid;
5838 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migratedfrom);
5839 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 0);
5843 PVE
::QemuConfig-
>lock_config($vmid, sub {
5844 _do_vm_stop
($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
5849 my ($vmid, $timeout) = @_;
5851 PVE
::QemuConfig-
>lock_config($vmid, sub {
5854 # only reboot if running, as qmeventd starts it again on a stop event
5855 return if !check_running
($vmid);
5857 create_reboot_request
($vmid);
5859 my $storecfg = PVE
::Storage
::config
();
5860 _do_vm_stop
($storecfg, $vmid, undef, undef, $timeout, 1);
5864 # avoid that the next normal shutdown will be confused for a reboot
5865 clear_reboot_request
($vmid);
5871 # note: if using the statestorage parameter, the caller has to check privileges
5873 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
5880 PVE
::QemuConfig-
>lock_config($vmid, sub {
5882 $conf = PVE
::QemuConfig-
>load_config($vmid);
5884 my $is_backing_up = PVE
::QemuConfig-
>has_lock($conf, 'backup');
5885 PVE
::QemuConfig-
>check_lock($conf)
5886 if !($skiplock || $is_backing_up);
5888 die "cannot suspend to disk during backup\n"
5889 if $is_backing_up && $includestate;
5891 if ($includestate) {
5892 $conf->{lock} = 'suspending';
5893 my $date = strftime
("%Y-%m-%d", localtime(time()));
5894 $storecfg = PVE
::Storage
::config
();
5895 if (!$statestorage) {
5896 $statestorage = find_vmstate_storage
($conf, $storecfg);
5897 # check permissions for the storage
5898 my $rpcenv = PVE
::RPCEnvironment
::get
();
5899 if ($rpcenv->{type
} ne 'cli') {
5900 my $authuser = $rpcenv->get_user();
5901 $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
5906 $vmstate = PVE
::QemuConfig-
>__snapshot_save_vmstate(
5907 $vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
5908 $path = PVE
::Storage
::path
($storecfg, $vmstate);
5909 PVE
::QemuConfig-
>write_config($vmid, $conf);
5911 mon_cmd
($vmid, "stop");
5915 if ($includestate) {
5917 PVE
::Storage
::activate_volumes
($storecfg, [$vmstate]);
5920 set_migration_caps
($vmid, 1);
5921 mon_cmd
($vmid, "savevm-start", statefile
=> $path);
5923 my $state = mon_cmd
($vmid, "query-savevm");
5924 if (!$state->{status
}) {
5925 die "savevm not active\n";
5926 } elsif ($state->{status
} eq 'active') {
5929 } elsif ($state->{status
} eq 'completed') {
5930 print "State saved, quitting\n";
5932 } elsif ($state->{status
} eq 'failed' && $state->{error
}) {
5933 die "query-savevm failed with error '$state->{error}'\n"
5935 die "query-savevm returned status '$state->{status}'\n";
5941 PVE
::QemuConfig-
>lock_config($vmid, sub {
5942 $conf = PVE
::QemuConfig-
>load_config($vmid);
5944 # cleanup, but leave suspending lock, to indicate something went wrong
5946 mon_cmd
($vmid, "savevm-end");
5947 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
5948 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
5949 delete $conf->@{qw(vmstate runningmachine runningcpu)};
5950 PVE
::QemuConfig-
>write_config($vmid, $conf);
5956 die "lock changed unexpectedly\n"
5957 if !PVE
::QemuConfig-
>has_lock($conf, 'suspending');
5959 mon_cmd
($vmid, "quit");
5960 $conf->{lock} = 'suspended';
5961 PVE
::QemuConfig-
>write_config($vmid, $conf);
5967 my ($vmid, $skiplock, $nocheck) = @_;
5969 PVE
::QemuConfig-
>lock_config($vmid, sub {
5970 my $res = mon_cmd
($vmid, 'query-status');
5971 my $resume_cmd = 'cont';
5974 if ($res->{status
}) {
5975 return if $res->{status
} eq 'running'; # job done, go home
5976 $resume_cmd = 'system_wakeup' if $res->{status
} eq 'suspended';
5977 $reset = 1 if $res->{status
} eq 'shutdown';
5982 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5984 PVE
::QemuConfig-
>check_lock($conf)
5985 if !($skiplock || PVE
::QemuConfig-
>has_lock($conf, 'backup'));
5989 # required if a VM shuts down during a backup and we get a resume
5990 # request before the backup finishes for example
5991 mon_cmd
($vmid, "system_reset");
5993 mon_cmd
($vmid, $resume_cmd);
5998 my ($vmid, $skiplock, $key) = @_;
6000 PVE
::QemuConfig-
>lock_config($vmid, sub {
6002 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6004 # there is no qmp command, so we use the human monitor command
6005 my $res = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "sendkey $key");
6006 die $res if $res ne '';
6010 # vzdump restore implementaion
6012 sub tar_archive_read_firstfile
{
6013 my $archive = shift;
6015 die "ERROR: file '$archive' does not exist\n" if ! -f
$archive;
6017 # try to detect archive type first
6018 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
6019 die "unable to open file '$archive'\n";
6020 my $firstfile = <$fh>;
6024 die "ERROR: archive contaions no data\n" if !$firstfile;
6030 sub tar_restore_cleanup
{
6031 my ($storecfg, $statfile) = @_;
6033 print STDERR
"starting cleanup\n";
6035 if (my $fd = IO
::File-
>new($statfile, "r")) {
6036 while (defined(my $line = <$fd>)) {
6037 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6040 if ($volid =~ m
|^/|) {
6041 unlink $volid || die 'unlink failed\n';
6043 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6045 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6047 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6049 print STDERR
"unable to parse line in statfile - $line";
6056 sub restore_file_archive
{
6057 my ($archive, $vmid, $user, $opts) = @_;
6059 return restore_vma_archive
($archive, $vmid, $user, $opts)
6062 my $info = PVE
::Storage
::archive_info
($archive);
6063 my $format = $opts->{format
} // $info->{format
};
6064 my $comp = $info->{compression
};
6066 # try to detect archive format
6067 if ($format eq 'tar') {
6068 return restore_tar_archive
($archive, $vmid, $user, $opts);
6070 return restore_vma_archive
($archive, $vmid, $user, $opts, $comp);
6074 # hepler to remove disks that will not be used after restore
6075 my $restore_cleanup_oldconf = sub {
6076 my ($storecfg, $vmid, $oldconf, $virtdev_hash) = @_;
6078 PVE
::QemuConfig-
>foreach_volume($oldconf, sub {
6079 my ($ds, $drive) = @_;
6081 return if drive_is_cdrom
($drive, 1);
6083 my $volid = $drive->{file
};
6084 return if !$volid || $volid =~ m
|^/|;
6086 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
6087 return if !$path || !$owner || ($owner != $vmid);
6089 # Note: only delete disk we want to restore
6090 # other volumes will become unused
6091 if ($virtdev_hash->{$ds}) {
6092 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
6099 # delete vmstate files, after the restore we have no snapshots anymore
6100 foreach my $snapname (keys %{$oldconf->{snapshots
}}) {
6101 my $snap = $oldconf->{snapshots
}->{$snapname};
6102 if ($snap->{vmstate
}) {
6103 eval { PVE
::Storage
::vdisk_free
($storecfg, $snap->{vmstate
}); };
6111 # Helper to parse vzdump backup device hints
6113 # $rpcenv: Environment, used to ckeck storage permissions
6114 # $user: User ID, to check storage permissions
6115 # $storecfg: Storage configuration
6116 # $fh: the file handle for reading the configuration
6117 # $devinfo: should contain device sizes for all backu-up'ed devices
6118 # $options: backup options (pool, default storage)
6120 # Return: $virtdev_hash, updates $devinfo (add devname, virtdev, format, storeid)
6121 my $parse_backup_hints = sub {
6122 my ($rpcenv, $user, $storecfg, $fh, $devinfo, $options) = @_;
6124 my $virtdev_hash = {};
6126 while (defined(my $line = <$fh>)) {
6127 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
6128 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
6129 die "archive does not contain data for drive '$virtdev'\n"
6130 if !$devinfo->{$devname};
6132 if (defined($options->{storage
})) {
6133 $storeid = $options->{storage
} || 'local';
6134 } elsif (!$storeid) {
6137 $format = 'raw' if !$format;
6138 $devinfo->{$devname}->{devname
} = $devname;
6139 $devinfo->{$devname}->{virtdev
} = $virtdev;
6140 $devinfo->{$devname}->{format
} = $format;
6141 $devinfo->{$devname}->{storeid
} = $storeid;
6143 # check permission on storage
6144 my $pool = $options->{pool
}; # todo: do we need that?
6145 if ($user ne 'root@pam') {
6146 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
6149 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
6150 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
6152 my $drive = parse_drive
($virtdev, $2);
6153 if (drive_is_cloudinit
($drive)) {
6154 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
6155 $storeid = $options->{storage
} if defined ($options->{storage
});
6156 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6157 my $format = qemu_img_format
($scfg, $volname); # has 'raw' fallback
6159 $virtdev_hash->{$virtdev} = {
6161 storeid
=> $storeid,
6162 size
=> PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
,
6169 return $virtdev_hash;
6172 # Helper to allocate and activate all volumes required for a restore
6174 # $storecfg: Storage configuration
6175 # $virtdev_hash: as returned by parse_backup_hints()
6177 # Returns: { $virtdev => $volid }
6178 my $restore_allocate_devices = sub {
6179 my ($storecfg, $virtdev_hash, $vmid) = @_;
6182 foreach my $virtdev (sort keys %$virtdev_hash) {
6183 my $d = $virtdev_hash->{$virtdev};
6184 my $alloc_size = int(($d->{size
} + 1024 - 1)/1024);
6185 my $storeid = $d->{storeid
};
6186 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6188 # test if requested format is supported
6189 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
6190 my $supported = grep { $_ eq $d->{format
} } @$validFormats;
6191 $d->{format
} = $defFormat if !$supported;
6194 if ($d->{is_cloudinit
}) {
6195 $name = "vm-$vmid-cloudinit";
6196 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6197 if ($scfg->{path
}) {
6198 $name .= ".$d->{format}";
6202 my $volid = PVE
::Storage
::vdisk_alloc
(
6203 $storecfg, $storeid, $vmid, $d->{format
}, $name, $alloc_size);
6205 print STDERR
"new volume ID is '$volid'\n";
6206 $d->{volid
} = $volid;
6208 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
6210 $map->{$virtdev} = $volid;
6216 sub restore_update_config_line
{
6217 my ($cookie, $map, $line, $unique) = @_;
6219 return '' if $line =~ m/^\#qmdump\#/;
6220 return '' if $line =~ m/^\#vzdump\#/;
6221 return '' if $line =~ m/^lock:/;
6222 return '' if $line =~ m/^unused\d+:/;
6223 return '' if $line =~ m/^parent:/;
6227 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
6228 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
6229 # try to convert old 1.X settings
6230 my ($id, $ind, $ethcfg) = ($1, $2, $3);
6231 foreach my $devconfig (PVE
::Tools
::split_list
($ethcfg)) {
6232 my ($model, $macaddr) = split(/\=/, $devconfig);
6233 $macaddr = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if !$macaddr || $unique;
6236 bridge
=> "vmbr$ind",
6237 macaddr
=> $macaddr,
6239 my $netstr = print_net
($net);
6241 $res .= "net$cookie->{netcount}: $netstr\n";
6242 $cookie->{netcount
}++;
6244 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
6245 my ($id, $netstr) = ($1, $2);
6246 my $net = parse_net
($netstr);
6247 $net->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if $net->{macaddr
};
6248 $netstr = print_net
($net);
6249 $res .= "$id: $netstr\n";
6250 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk|tpmstate)\d+):\s*(\S+)\s*$/) {
6253 my $di = parse_drive
($virtdev, $value);
6254 if (defined($di->{backup
}) && !$di->{backup
}) {
6256 } elsif ($map->{$virtdev}) {
6257 delete $di->{format
}; # format can change on restore
6258 $di->{file
} = $map->{$virtdev};
6259 $value = print_drive
($di);
6260 $res .= "$virtdev: $value\n";
6264 } elsif (($line =~ m/^vmgenid: (.*)/)) {
6266 if ($vmgenid ne '0') {
6267 # always generate a new vmgenid if there was a valid one setup
6268 $vmgenid = generate_uuid
();
6270 $res .= "vmgenid: $vmgenid\n";
6271 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
6272 my ($uuid, $uuid_str);
6273 UUID
::generate
($uuid);
6274 UUID
::unparse
($uuid, $uuid_str);
6275 my $smbios1 = parse_smbios1
($2);
6276 $smbios1->{uuid
} = $uuid_str;
6277 $res .= $1.print_smbios1
($smbios1)."\n";
6285 my $restore_deactivate_volumes = sub {
6286 my ($storecfg, $devinfo) = @_;
6289 foreach my $devname (keys %$devinfo) {
6290 my $volid = $devinfo->{$devname}->{volid
};
6291 push @$vollist, $volid if $volid;
6294 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
6297 my $restore_destroy_volumes = sub {
6298 my ($storecfg, $devinfo) = @_;
6300 foreach my $devname (keys %$devinfo) {
6301 my $volid = $devinfo->{$devname}->{volid
};
6304 if ($volid =~ m
|^/|) {
6305 unlink $volid || die 'unlink failed\n';
6307 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6309 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6311 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6316 my ($cfg, $vmid) = @_;
6318 my $info = PVE
::Storage
::vdisk_list
($cfg, undef, $vmid, undef, 'images');
6320 my $volid_hash = {};
6321 foreach my $storeid (keys %$info) {
6322 foreach my $item (@{$info->{$storeid}}) {
6323 next if !($item->{volid
} && $item->{size
});
6324 $item->{path
} = PVE
::Storage
::path
($cfg, $item->{volid
});
6325 $volid_hash->{$item->{volid
}} = $item;
6332 sub update_disk_config
{
6333 my ($vmid, $conf, $volid_hash) = @_;
6336 my $prefix = "VM $vmid";
6338 # used and unused disks
6339 my $referenced = {};
6341 # Note: it is allowed to define multiple storages with same path (alias), so
6342 # we need to check both 'volid' and real 'path' (two different volid can point
6343 # to the same path).
6345 my $referencedpath = {};
6348 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6349 my ($opt, $drive) = @_;
6351 my $volid = $drive->{file
};
6353 my $volume = $volid_hash->{$volid};
6355 # mark volid as "in-use" for next step
6356 $referenced->{$volid} = 1;
6357 if ($volume && (my $path = $volume->{path
})) {
6358 $referencedpath->{$path} = 1;
6361 return if drive_is_cdrom
($drive);
6364 my ($updated, $msg) = PVE
::QemuServer
::Drive
::update_disksize
($drive, $volume->{size
});
6365 if (defined($updated)) {
6367 $conf->{$opt} = print_drive
($updated);
6368 print "$prefix ($opt): $msg\n";
6372 # remove 'unusedX' entry if volume is used
6373 PVE
::QemuConfig-
>foreach_unused_volume($conf, sub {
6374 my ($opt, $drive) = @_;
6376 my $volid = $drive->{file
};
6380 $path = $volid_hash->{$volid}->{path
} if $volid_hash->{$volid};
6381 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6382 print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
6384 delete $conf->{$opt};
6387 $referenced->{$volid} = 1;
6388 $referencedpath->{$path} = 1 if $path;
6391 foreach my $volid (sort keys %$volid_hash) {
6392 next if $volid =~ m/vm-$vmid-state-/;
6393 next if $referenced->{$volid};
6394 my $path = $volid_hash->{$volid}->{path
};
6395 next if !$path; # just to be sure
6396 next if $referencedpath->{$path};
6398 my $key = PVE
::QemuConfig-
>add_unused_volume($conf, $volid);
6399 print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
6400 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6407 my ($vmid, $nolock, $dryrun) = @_;
6409 my $cfg = PVE
::Storage
::config
();
6411 print "rescan volumes...\n";
6412 my $volid_hash = scan_volids
($cfg, $vmid);
6414 my $updatefn = sub {
6417 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6419 PVE
::QemuConfig-
>check_lock($conf);
6422 foreach my $volid (keys %$volid_hash) {
6423 my $info = $volid_hash->{$volid};
6424 $vm_volids->{$volid} = $info if $info->{vmid
} && $info->{vmid
} == $vmid;
6427 my $changes = update_disk_config
($vmid, $conf, $vm_volids);
6429 PVE
::QemuConfig-
>write_config($vmid, $conf) if $changes && !$dryrun;
6432 if (defined($vmid)) {
6436 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6439 my $vmlist = config_list
();
6440 foreach my $vmid (keys %$vmlist) {
6444 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6450 sub restore_proxmox_backup_archive
{
6451 my ($archive, $vmid, $user, $options) = @_;
6453 my $storecfg = PVE
::Storage
::config
();
6455 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($archive);
6456 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6458 my $fingerprint = $scfg->{fingerprint
};
6459 my $keyfile = PVE
::Storage
::PBSPlugin
::pbs_encryption_key_file_name
($storecfg, $storeid);
6461 my $repo = PVE
::PBSClient
::get_repository
($scfg);
6463 # This is only used for `pbs-restore` and the QEMU PBS driver (live-restore)
6464 my $password = PVE
::Storage
::PBSPlugin
::pbs_get_password
($scfg, $storeid);
6465 local $ENV{PBS_PASSWORD
} = $password;
6466 local $ENV{PBS_FINGERPRINT
} = $fingerprint if defined($fingerprint);
6468 my ($vtype, $pbs_backup_name, undef, undef, undef, undef, $format) =
6469 PVE
::Storage
::parse_volname
($storecfg, $archive);
6471 die "got unexpected vtype '$vtype'\n" if $vtype ne 'backup';
6473 die "got unexpected backup format '$format'\n" if $format ne 'pbs-vm';
6475 my $tmpdir = "/var/tmp/vzdumptmp$$";
6479 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6480 # disable interrupts (always do cleanups)
6484 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
6486 # Note: $oldconf is undef if VM does not exists
6487 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
6488 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
6489 my $new_conf_raw = '';
6491 my $rpcenv = PVE
::RPCEnvironment
::get
();
6500 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6502 my $cfgfn = "$tmpdir/qemu-server.conf";
6503 my $firewall_config_fn = "$tmpdir/fw.conf";
6504 my $index_fn = "$tmpdir/index.json";
6506 my $cmd = "restore";
6508 my $param = [$pbs_backup_name, "index.json", $index_fn];
6509 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6510 my $index = PVE
::Tools
::file_get_contents
($index_fn);
6511 $index = decode_json
($index);
6513 # print Dumper($index);
6514 foreach my $info (@{$index->{files
}}) {
6515 if ($info->{filename
} =~ m/^(drive-\S+).img.fidx$/) {
6517 if ($info->{size
} =~ m/^(\d+)$/) { # untaint size
6518 $devinfo->{$devname}->{size
} = $1;
6520 die "unable to parse file size in 'index.json' - got '$info->{size}'\n";
6525 my $is_qemu_server_backup = scalar(
6526 grep { $_->{filename
} eq 'qemu-server.conf.blob' } @{$index->{files
}}
6528 if (!$is_qemu_server_backup) {
6529 die "backup does not look like a qemu-server backup (missing 'qemu-server.conf' file)\n";
6531 my $has_firewall_config = scalar(grep { $_->{filename
} eq 'fw.conf.blob' } @{$index->{files
}});
6533 $param = [$pbs_backup_name, "qemu-server.conf", $cfgfn];
6534 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6536 if ($has_firewall_config) {
6537 $param = [$pbs_backup_name, "fw.conf", $firewall_config_fn];
6538 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6540 my $pve_firewall_dir = '/etc/pve/firewall';
6541 mkdir $pve_firewall_dir; # make sure the dir exists
6542 PVE
::Tools
::file_copy
($firewall_config_fn, "${pve_firewall_dir}/$vmid.fw");
6545 my $fh = IO
::File-
>new($cfgfn, "r") ||
6546 die "unable to read qemu-server.conf - $!\n";
6548 my $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $storecfg, $fh, $devinfo, $options);
6550 # fixme: rate limit?
6552 # create empty/temp config
6553 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\nlock: create");
6555 $restore_cleanup_oldconf->($storecfg, $vmid, $oldconf, $virtdev_hash) if $oldconf;
6558 my $map = $restore_allocate_devices->($storecfg, $virtdev_hash, $vmid);
6560 foreach my $virtdev (sort keys %$virtdev_hash) {
6561 my $d = $virtdev_hash->{$virtdev};
6562 next if $d->{is_cloudinit
}; # no need to restore cloudinit
6564 # this fails if storage is unavailable
6565 my $volid = $d->{volid
};
6566 my $path = PVE
::Storage
::path
($storecfg, $volid);
6568 # for live-restore we only want to preload the efidisk and TPM state
6569 next if $options->{live
} && $virtdev ne 'efidisk0' && $virtdev ne 'tpmstate0';
6571 my $pbs_restore_cmd = [
6572 '/usr/bin/pbs-restore',
6573 '--repository', $repo,
6575 "$d->{devname}.img.fidx",
6580 push @$pbs_restore_cmd, '--format', $d->{format
} if $d->{format
};
6581 push @$pbs_restore_cmd, '--keyfile', $keyfile if -e
$keyfile;
6583 if (PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $volid)) {
6584 push @$pbs_restore_cmd, '--skip-zero';
6587 my $dbg_cmdstring = PVE
::Tools
::cmd2string
($pbs_restore_cmd);
6588 print "restore proxmox backup image: $dbg_cmdstring\n";
6589 run_command
($pbs_restore_cmd);
6592 $fh->seek(0, 0) || die "seek failed - $!\n";
6594 my $cookie = { netcount
=> 0 };
6595 while (defined(my $line = <$fh>)) {
6596 $new_conf_raw .= restore_update_config_line
(
6608 if ($err || !$options->{live
}) {
6609 $restore_deactivate_volumes->($storecfg, $devinfo);
6615 $restore_destroy_volumes->($storecfg, $devinfo);
6619 if ($options->{live
}) {
6620 # keep lock during live-restore
6621 $new_conf_raw .= "\nlock: create";
6624 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
6626 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6628 eval { rescan
($vmid, 1); };
6631 PVE
::AccessControl
::add_vm_to_pool
($vmid, $options->{pool
}) if $options->{pool
};
6633 if ($options->{live
}) {
6639 local $SIG{PIPE
} = sub { die "got signal ($!) - abort\n"; };
6641 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6642 die "cannot do live-restore for template\n" if PVE
::QemuConfig-
>is_template($conf);
6644 # these special drives are already restored before start
6645 delete $devinfo->{'drive-efidisk0'};
6646 delete $devinfo->{'drive-tpmstate0-backup'};
6647 pbs_live_restore
($vmid, $conf, $storecfg, $devinfo, $repo, $keyfile, $pbs_backup_name);
6649 PVE
::QemuConfig-
>remove_lock($vmid, "create");
6653 sub pbs_live_restore
{
6654 my ($vmid, $conf, $storecfg, $restored_disks, $repo, $keyfile, $snap) = @_;
6656 print "starting VM for live-restore\n";
6657 print "repository: '$repo', snapshot: '$snap'\n";
6659 my $pbs_backing = {};
6660 for my $ds (keys %$restored_disks) {
6661 $ds =~ m/^drive-(.*)$/;
6663 $pbs_backing->{$confname} = {
6664 repository
=> $repo,
6666 archive
=> "$ds.img.fidx",
6668 $pbs_backing->{$confname}->{keyfile
} = $keyfile if -e
$keyfile;
6670 my $drive = parse_drive
($confname, $conf->{$confname});
6671 print "restoring '$ds' to '$drive->{file}'\n";
6674 my $drives_streamed = 0;
6676 # make sure HA doesn't interrupt our restore by stopping the VM
6677 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid)) {
6678 run_command
(['ha-manager', 'set', "vm:$vmid", '--state', 'started']);
6681 # start VM with backing chain pointing to PBS backup, environment vars for PBS driver
6682 # in QEMU (PBS_PASSWORD and PBS_FINGERPRINT) are already set by our caller
6683 vm_start_nolock
($storecfg, $vmid, $conf, {paused
=> 1, 'pbs-backing' => $pbs_backing}, {});
6685 my $qmeventd_fd = register_qmeventd_handle
($vmid);
6687 # begin streaming, i.e. data copy from PBS to target disk for every vol,
6688 # this will effectively collapse the backing image chain consisting of
6689 # [target <- alloc-track -> PBS snapshot] to just [target] (alloc-track
6690 # removes itself once all backing images vanish with 'auto-remove=on')
6692 for my $ds (sort keys %$restored_disks) {
6693 my $job_id = "restore-$ds";
6694 mon_cmd
($vmid, 'block-stream',
6695 'job-id' => $job_id,
6698 $jobs->{$job_id} = {};
6701 mon_cmd
($vmid, 'cont');
6702 qemu_drive_mirror_monitor
($vmid, undef, $jobs, 'auto', 0, 'stream');
6704 print "restore-drive jobs finished successfully, removing all tracking block devices"
6705 ." to disconnect from Proxmox Backup Server\n";
6707 for my $ds (sort keys %$restored_disks) {
6708 mon_cmd
($vmid, 'blockdev-del', 'node-name' => "$ds-pbs");
6711 close($qmeventd_fd);
6717 warn "An error occured during live-restore: $err\n";
6718 _do_vm_stop
($storecfg, $vmid, 1, 1, 10, 0, 1);
6719 die "live-restore failed\n";
6723 sub restore_vma_archive
{
6724 my ($archive, $vmid, $user, $opts, $comp) = @_;
6726 my $readfrom = $archive;
6728 my $cfg = PVE
::Storage
::config
();
6730 my $bwlimit = $opts->{bwlimit
};
6732 my $dbg_cmdstring = '';
6733 my $add_pipe = sub {
6735 push @$commands, $cmd;
6736 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
6737 $dbg_cmdstring .= PVE
::Tools
::cmd2string
($cmd);
6742 if ($archive eq '-') {
6745 # If we use a backup from a PVE defined storage we also consider that
6746 # storage's rate limit:
6747 my (undef, $volid) = PVE
::Storage
::path_to_volume_id
($cfg, $archive);
6748 if (defined($volid)) {
6749 my ($sid, undef) = PVE
::Storage
::parse_volume_id
($volid);
6750 my $readlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [$sid], $bwlimit);
6752 print STDERR
"applying read rate limit: $readlimit\n";
6753 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
6754 $add_pipe->($cstream);
6760 my $info = PVE
::Storage
::decompressor_info
('vma', $comp);
6761 my $cmd = $info->{decompressor
};
6762 push @$cmd, $readfrom;
6766 my $tmpdir = "/var/tmp/vzdumptmp$$";
6769 # disable interrupts (always do cleanups)
6773 local $SIG{HUP
} = sub { warn "got interrupt - ignored\n"; };
6775 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
6776 POSIX
::mkfifo
($mapfifo, 0600);
6778 my $openfifo = sub { open($fifofh, '>', $mapfifo) or die $! };
6780 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
6787 my $rpcenv = PVE
::RPCEnvironment
::get
();
6789 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6791 # Note: $oldconf is undef if VM does not exist
6792 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
6793 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
6794 my $new_conf_raw = '';
6798 my $print_devmap = sub {
6799 my $cfgfn = "$tmpdir/qemu-server.conf";
6801 # we can read the config - that is already extracted
6802 my $fh = IO
::File-
>new($cfgfn, "r") ||
6803 die "unable to read qemu-server.conf - $!\n";
6805 my $fwcfgfn = "$tmpdir/qemu-server.fw";
6807 my $pve_firewall_dir = '/etc/pve/firewall';
6808 mkdir $pve_firewall_dir; # make sure the dir exists
6809 PVE
::Tools
::file_copy
($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
6812 my $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $cfg, $fh, $devinfo, $opts);
6814 foreach my $info (values %{$virtdev_hash}) {
6815 my $storeid = $info->{storeid
};
6816 next if defined($storage_limits{$storeid});
6818 my $limit = PVE
::Storage
::get_bandwidth_limit
('restore', [$storeid], $bwlimit) // 0;
6819 print STDERR
"rate limit for storage $storeid: $limit KiB/s\n" if $limit;
6820 $storage_limits{$storeid} = $limit * 1024;
6823 foreach my $devname (keys %$devinfo) {
6824 die "found no device mapping information for device '$devname'\n"
6825 if !$devinfo->{$devname}->{virtdev
};
6828 # create empty/temp config
6830 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\n");
6831 $restore_cleanup_oldconf->($cfg, $vmid, $oldconf, $virtdev_hash);
6835 my $map = $restore_allocate_devices->($cfg, $virtdev_hash, $vmid);
6837 # print restore information to $fifofh
6838 foreach my $virtdev (sort keys %$virtdev_hash) {
6839 my $d = $virtdev_hash->{$virtdev};
6840 next if $d->{is_cloudinit
}; # no need to restore cloudinit
6842 my $storeid = $d->{storeid
};
6843 my $volid = $d->{volid
};
6846 if (my $limit = $storage_limits{$storeid}) {
6847 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
6850 my $write_zeros = 1;
6851 if (PVE
::Storage
::volume_has_feature
($cfg, 'sparseinit', $volid)) {
6855 my $path = PVE
::Storage
::path
($cfg, $volid);
6857 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
6859 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
6862 $fh->seek(0, 0) || die "seek failed - $!\n";
6864 my $cookie = { netcount
=> 0 };
6865 while (defined(my $line = <$fh>)) {
6866 $new_conf_raw .= restore_update_config_line
(
6883 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6884 local $SIG{ALRM
} = sub { die "got timeout\n"; };
6886 $oldtimeout = alarm($timeout);
6893 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
6894 my ($dev_id, $size, $devname) = ($1, $2, $3);
6895 $devinfo->{$devname} = { size
=> $size, dev_id
=> $dev_id };
6896 } elsif ($line =~ m/^CTIME: /) {
6897 # we correctly received the vma config, so we can disable
6898 # the timeout now for disk allocation (set to 10 minutes, so
6899 # that we always timeout if something goes wrong)
6902 print $fifofh "done\n";
6903 my $tmp = $oldtimeout || 0;
6904 $oldtimeout = undef;
6911 print "restore vma archive: $dbg_cmdstring\n";
6912 run_command
($commands, input
=> $input, outfunc
=> $parser, afterfork
=> $openfifo);
6916 alarm($oldtimeout) if $oldtimeout;
6918 $restore_deactivate_volumes->($cfg, $devinfo);
6920 close($fifofh) if $fifofh;
6925 $restore_destroy_volumes->($cfg, $devinfo);
6929 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
6931 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6933 eval { rescan
($vmid, 1); };
6936 PVE
::AccessControl
::add_vm_to_pool
($vmid, $opts->{pool
}) if $opts->{pool
};
6939 sub restore_tar_archive
{
6940 my ($archive, $vmid, $user, $opts) = @_;
6942 if ($archive ne '-') {
6943 my $firstfile = tar_archive_read_firstfile
($archive);
6944 die "ERROR: file '$archive' does not look like a QemuServer vzdump backup\n"
6945 if $firstfile ne 'qemu-server.conf';
6948 my $storecfg = PVE
::Storage
::config
();
6950 # avoid zombie disks when restoring over an existing VM -> cleanup first
6951 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
6952 # skiplock=1 because qmrestore has set the 'create' lock itself already
6953 my $vmcfgfn = PVE
::QemuConfig-
>config_file($vmid);
6954 destroy_vm
($storecfg, $vmid, 1, { lock => 'restore' }) if -f
$vmcfgfn;
6956 my $tocmd = "/usr/lib/qemu-server/qmextract";
6958 $tocmd .= " --storage " . PVE
::Tools
::shellquote
($opts->{storage
}) if $opts->{storage
};
6959 $tocmd .= " --pool " . PVE
::Tools
::shellquote
($opts->{pool
}) if $opts->{pool
};
6960 $tocmd .= ' --prealloc' if $opts->{prealloc
};
6961 $tocmd .= ' --info' if $opts->{info
};
6963 # tar option "xf" does not autodetect compression when read from STDIN,
6964 # so we pipe to zcat
6965 my $cmd = "zcat -f|tar xf " . PVE
::Tools
::shellquote
($archive) . " " .
6966 PVE
::Tools
::shellquote
("--to-command=$tocmd");
6968 my $tmpdir = "/var/tmp/vzdumptmp$$";
6971 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
6972 local $ENV{VZDUMP_VMID
} = $vmid;
6973 local $ENV{VZDUMP_USER
} = $user;
6975 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6976 my $new_conf_raw = '';
6978 # disable interrupts (always do cleanups)
6982 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
6990 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6992 if ($archive eq '-') {
6993 print "extracting archive from STDIN\n";
6994 run_command
($cmd, input
=> "<&STDIN");
6996 print "extracting archive '$archive'\n";
7000 return if $opts->{info
};
7004 my $statfile = "$tmpdir/qmrestore.stat";
7005 if (my $fd = IO
::File-
>new($statfile, "r")) {
7006 while (defined (my $line = <$fd>)) {
7007 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
7008 $map->{$1} = $2 if $1;
7010 print STDERR
"unable to parse line in statfile - $line\n";
7016 my $confsrc = "$tmpdir/qemu-server.conf";
7018 my $srcfd = IO
::File-
>new($confsrc, "r") || die "unable to open file '$confsrc'\n";
7020 my $cookie = { netcount
=> 0 };
7021 while (defined (my $line = <$srcfd>)) {
7022 $new_conf_raw .= restore_update_config_line
(
7033 tar_restore_cleanup
($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info
};
7039 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
7041 PVE
::Cluster
::cfs_update
(); # make sure we read new file
7043 eval { rescan
($vmid, 1); };
7047 sub foreach_storage_used_by_vm
{
7048 my ($conf, $func) = @_;
7052 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7053 my ($ds, $drive) = @_;
7054 return if drive_is_cdrom
($drive);
7056 my $volid = $drive->{file
};
7058 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
7059 $sidhash->{$sid} = $sid if $sid;
7062 foreach my $sid (sort keys %$sidhash) {
7067 my $qemu_snap_storage = {
7070 sub do_snapshots_with_qemu
{
7071 my ($storecfg, $volid, $deviceid) = @_;
7073 return if $deviceid =~ m/tpmstate0/;
7075 my $storage_name = PVE
::Storage
::parse_volume_id
($volid);
7076 my $scfg = $storecfg->{ids
}->{$storage_name};
7077 die "could not find storage '$storage_name'\n" if !defined($scfg);
7079 if ($qemu_snap_storage->{$scfg->{type
}} && !$scfg->{krbd
}){
7083 if ($volid =~ m/\.(qcow2|qed)$/){
7090 sub qga_check_running
{
7091 my ($vmid, $nowarn) = @_;
7093 eval { mon_cmd
($vmid, "guest-ping", timeout
=> 3); };
7095 warn "Qemu Guest Agent is not running - $@" if !$nowarn;
7101 sub template_create
{
7102 my ($vmid, $conf, $disk) = @_;
7104 my $storecfg = PVE
::Storage
::config
();
7106 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7107 my ($ds, $drive) = @_;
7109 return if drive_is_cdrom
($drive);
7110 return if $disk && $ds ne $disk;
7112 my $volid = $drive->{file
};
7113 return if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
7115 my $voliddst = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
7116 $drive->{file
} = $voliddst;
7117 $conf->{$ds} = print_drive
($drive);
7118 PVE
::QemuConfig-
>write_config($vmid, $conf);
7122 sub convert_iscsi_path
{
7125 if ($path =~ m
|^iscsi
://([^/]+)/([^/]+)/(.+)$|) {
7130 my $initiator_name = get_initiator_name
();
7132 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
7133 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
7136 die "cannot convert iscsi path '$path', unkown format\n";
7139 sub qemu_img_convert
{
7140 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
7142 my $storecfg = PVE
::Storage
::config
();
7143 my ($src_storeid, $src_volname) = PVE
::Storage
::parse_volume_id
($src_volid, 1);
7144 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid, 1);
7146 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
7150 my $src_is_iscsi = 0;
7154 PVE
::Storage
::activate_volumes
($storecfg, [$src_volid], $snapname);
7155 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
7156 $src_format = qemu_img_format
($src_scfg, $src_volname);
7157 $src_path = PVE
::Storage
::path
($storecfg, $src_volid, $snapname);
7158 $src_is_iscsi = ($src_path =~ m
|^iscsi
://|);
7159 $cachemode = 'none' if $src_scfg->{type
} eq 'zfspool';
7160 } elsif (-f
$src_volid) {
7161 $src_path = $src_volid;
7162 if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7167 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
7169 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7170 my $dst_format = qemu_img_format
($dst_scfg, $dst_volname);
7171 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7172 my $dst_is_iscsi = ($dst_path =~ m
|^iscsi
://|);
7175 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
7176 push @$cmd, '-l', "snapshot.name=$snapname"
7177 if $snapname && $src_format && $src_format eq "qcow2";
7178 push @$cmd, '-t', 'none' if $dst_scfg->{type
} eq 'zfspool';
7179 push @$cmd, '-T', $cachemode if defined($cachemode);
7181 if ($src_is_iscsi) {
7182 push @$cmd, '--image-opts';
7183 $src_path = convert_iscsi_path
($src_path);
7184 } elsif ($src_format) {
7185 push @$cmd, '-f', $src_format;
7188 if ($dst_is_iscsi) {
7189 push @$cmd, '--target-image-opts';
7190 $dst_path = convert_iscsi_path
($dst_path);
7192 push @$cmd, '-O', $dst_format;
7195 push @$cmd, $src_path;
7197 if (!$dst_is_iscsi && $is_zero_initialized) {
7198 push @$cmd, "zeroinit:$dst_path";
7200 push @$cmd, $dst_path;
7205 if($line =~ m/\((\S+)\/100\
%\)/){
7207 my $transferred = int($size * $percent / 100);
7208 my $total_h = render_bytes
($size, 1);
7209 my $transferred_h = render_bytes
($transferred, 1);
7211 print "transferred $transferred_h of $total_h ($percent%)\n";
7216 eval { run_command
($cmd, timeout
=> undef, outfunc
=> $parser); };
7218 die "copy failed: $err" if $err;
7221 sub qemu_img_format
{
7222 my ($scfg, $volname) = @_;
7224 if ($scfg->{path
} && $volname =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7231 sub qemu_drive_mirror
{
7232 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
7234 $jobs = {} if !$jobs;
7238 $jobs->{"drive-$drive"} = {};
7240 if ($dst_volid =~ /^nbd:/) {
7241 $qemu_target = $dst_volid;
7244 my $storecfg = PVE
::Storage
::config
();
7245 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid);
7247 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7249 $format = qemu_img_format
($dst_scfg, $dst_volname);
7251 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7253 $qemu_target = $is_zero_initialized ?
"zeroinit:$dst_path" : $dst_path;
7256 my $opts = { timeout
=> 10, device
=> "drive-$drive", mode
=> "existing", sync
=> "full", target
=> $qemu_target };
7257 $opts->{format
} = $format if $format;
7259 if (defined($src_bitmap)) {
7260 $opts->{sync
} = 'incremental';
7261 $opts->{bitmap
} = $src_bitmap;
7262 print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
7265 if (defined($bwlimit)) {
7266 $opts->{speed
} = $bwlimit * 1024;
7267 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
7269 print "drive mirror is starting for drive-$drive\n";
7272 # if a job already runs for this device we get an error, catch it for cleanup
7273 eval { mon_cmd
($vmid, "drive-mirror", %$opts); };
7275 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7277 die "mirroring error: $err\n";
7280 qemu_drive_mirror_monitor
($vmid, $vmiddst, $jobs, $completion, $qga);
7283 # $completion can be either
7284 # 'complete': wait until all jobs are ready, block-job-complete them (default)
7285 # 'cancel': wait until all jobs are ready, block-job-cancel them
7286 # 'skip': wait until all jobs are ready, return with block jobs in ready state
7287 # 'auto': wait until all jobs disappear, only use for jobs which complete automatically
7288 sub qemu_drive_mirror_monitor
{
7289 my ($vmid, $vmiddst, $jobs, $completion, $qga, $op) = @_;
7291 $completion //= 'complete';
7295 my $err_complete = 0;
7297 my $starttime = time ();
7299 die "block job ('$op') timed out\n" if $err_complete > 300;
7301 my $stats = mon_cmd
($vmid, "query-block-jobs");
7304 my $running_jobs = {};
7305 for my $stat (@$stats) {
7306 next if $stat->{type
} ne $op;
7307 $running_jobs->{$stat->{device
}} = $stat;
7310 my $readycounter = 0;
7312 for my $job_id (sort keys %$jobs) {
7313 my $job = $running_jobs->{$job_id};
7315 my $vanished = !defined($job);
7316 my $complete = defined($jobs->{$job_id}->{complete
}) && $vanished;
7317 if($complete || ($vanished && $completion eq 'auto')) {
7318 print "$job_id: $op-job finished\n";
7319 delete $jobs->{$job_id};
7323 die "$job_id: '$op' has been cancelled\n" if !defined($job);
7325 my $busy = $job->{busy
};
7326 my $ready = $job->{ready
};
7327 if (my $total = $job->{len
}) {
7328 my $transferred = $job->{offset
} || 0;
7329 my $remaining = $total - $transferred;
7330 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
7332 my $duration = $ctime - $starttime;
7333 my $total_h = render_bytes
($total, 1);
7334 my $transferred_h = render_bytes
($transferred, 1);
7336 my $status = sprintf(
7337 "transferred $transferred_h of $total_h ($percent%%) in %s",
7338 render_duration
($duration),
7343 $status .= ", still busy"; # shouldn't even happen? but mirror is weird
7345 $status .= ", ready";
7348 print "$job_id: $status\n" if !$jobs->{$job_id}->{ready
};
7349 $jobs->{$job_id}->{ready
} = $ready;
7352 $readycounter++ if $job->{ready
};
7355 last if scalar(keys %$jobs) == 0;
7357 if ($readycounter == scalar(keys %$jobs)) {
7358 print "all '$op' jobs are ready\n";
7360 # do the complete later (or has already been done)
7361 last if $completion eq 'skip' || $completion eq 'auto';
7363 if ($vmiddst && $vmiddst != $vmid) {
7364 my $agent_running = $qga && qga_check_running
($vmid);
7365 if ($agent_running) {
7366 print "freeze filesystem\n";
7367 eval { mon_cmd
($vmid, "guest-fsfreeze-freeze"); };
7369 print "suspend vm\n";
7370 eval { PVE
::QemuServer
::vm_suspend
($vmid, 1); };
7373 # if we clone a disk for a new target vm, we don't switch the disk
7374 PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs);
7376 if ($agent_running) {
7377 print "unfreeze filesystem\n";
7378 eval { mon_cmd
($vmid, "guest-fsfreeze-thaw"); };
7380 print "resume vm\n";
7381 eval { PVE
::QemuServer
::vm_resume
($vmid, 1, 1); };
7387 for my $job_id (sort keys %$jobs) {
7388 # try to switch the disk if source and destination are on the same guest
7389 print "$job_id: Completing block job_id...\n";
7392 if ($completion eq 'complete') {
7393 $op = 'block-job-complete';
7394 } elsif ($completion eq 'cancel') {
7395 $op = 'block-job-cancel';
7397 die "invalid completion value: $completion\n";
7399 eval { mon_cmd
($vmid, $op, device
=> $job_id) };
7400 if ($@ =~ m/cannot be completed/) {
7401 print "$job_id: block job cannot be completed, trying again.\n";
7404 print "$job_id: Completed successfully.\n";
7405 $jobs->{$job_id}->{complete
} = 1;
7416 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7417 die "block job ($op) error: $err";
7421 sub qemu_blockjobs_cancel
{
7422 my ($vmid, $jobs) = @_;
7424 foreach my $job (keys %$jobs) {
7425 print "$job: Cancelling block job\n";
7426 eval { mon_cmd
($vmid, "block-job-cancel", device
=> $job); };
7427 $jobs->{$job}->{cancel
} = 1;
7431 my $stats = mon_cmd
($vmid, "query-block-jobs");
7433 my $running_jobs = {};
7434 foreach my $stat (@$stats) {
7435 $running_jobs->{$stat->{device
}} = $stat;
7438 foreach my $job (keys %$jobs) {
7440 if (defined($jobs->{$job}->{cancel
}) && !defined($running_jobs->{$job})) {
7441 print "$job: Done.\n";
7442 delete $jobs->{$job};
7446 last if scalar(keys %$jobs) == 0;
7453 my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
7454 $newvmid, $storage, $format, $full, $newvollist, $jobs, $completion, $qga, $bwlimit, $conf) = @_;
7459 print "create linked clone of drive $drivename ($drive->{file})\n";
7460 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $drive->{file
}, $newvmid, $snapname);
7461 push @$newvollist, $newvolid;
7464 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
7465 $storeid = $storage if $storage;
7467 my $dst_format = resolve_dst_disk_format
($storecfg, $storeid, $volname, $format);
7469 print "create full clone of drive $drivename ($drive->{file})\n";
7472 if (drive_is_cloudinit
($drive)) {
7473 $name = "vm-$newvmid-cloudinit";
7474 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7475 if ($scfg->{path
}) {
7476 $name .= ".$dst_format";
7479 $size = PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
;
7480 } elsif ($drivename eq 'efidisk0') {
7481 $size = get_efivars_size
($conf);
7482 } elsif ($drivename eq 'tpmstate0') {
7483 $size = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
7485 ($size) = PVE
::Storage
::volume_size_info
($storecfg, $drive->{file
}, 10);
7487 $newvolid = PVE
::Storage
::vdisk_alloc
(
7488 $storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024)
7490 push @$newvollist, $newvolid;
7492 PVE
::Storage
::activate_volumes
($storecfg, [$newvolid]);
7494 if (drive_is_cloudinit
($drive)) {
7495 # when cloning multiple disks (e.g. during clone_vm) it might be the last disk
7496 # if this is the case, we have to complete any block-jobs still there from
7497 # previous drive-mirrors
7498 if (($completion eq 'complete') && (scalar(keys %$jobs) > 0)) {
7499 qemu_drive_mirror_monitor
($vmid, $newvmid, $jobs, $completion, $qga);
7504 my $sparseinit = PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $newvolid);
7505 if (!$running || $snapname) {
7506 # TODO: handle bwlimits
7507 if ($drivename eq 'efidisk0') {
7508 # the relevant data on the efidisk may be smaller than the source
7509 # e.g. on RBD/ZFS, so we use dd to copy only the amount
7510 # that is given by the OVMF_VARS.fd
7511 my $src_path = PVE
::Storage
::path
($storecfg, $drive->{file
});
7512 my $dst_path = PVE
::Storage
::path
($storecfg, $newvolid);
7514 # better for Ceph if block size is not too small, see bug #3324
7517 run_command
(['qemu-img', 'dd', '-n', '-O', $dst_format, "bs=$bs", "osize=$size",
7518 "if=$src_path", "of=$dst_path"]);
7520 qemu_img_convert
($drive->{file
}, $newvolid, $size, $snapname, $sparseinit);
7524 die "cannot move TPM state while VM is running\n" if $drivename eq 'tpmstate0';
7526 my $kvmver = get_running_qemu_version
($vmid);
7527 if (!min_version
($kvmver, 2, 7)) {
7528 die "drive-mirror with iothread requires qemu version 2.7 or higher\n"
7529 if $drive->{iothread
};
7532 qemu_drive_mirror
($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs,
7533 $completion, $qga, $bwlimit);
7538 my ($size) = eval { PVE
::Storage
::volume_size_info
($storecfg, $newvolid, 10) };
7541 $disk->{format
} = undef;
7542 $disk->{file
} = $newvolid;
7543 $disk->{size
} = $size if defined($size);
7548 sub get_running_qemu_version
{
7550 my $res = mon_cmd
($vmid, "query-version");
7551 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
7554 sub qemu_use_old_bios_files
{
7555 my ($machine_type) = @_;
7557 return if !$machine_type;
7559 my $use_old_bios_files = undef;
7561 if ($machine_type =~ m/^(\S+)\.pxe$/) {
7563 $use_old_bios_files = 1;
7565 my $version = extract_version
($machine_type, kvm_user_version
());
7566 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
7567 # load new efi bios files on migration. So this hack is required to allow
7568 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
7569 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
7570 $use_old_bios_files = !min_version
($version, 2, 4);
7573 return ($use_old_bios_files, $machine_type);
7576 sub get_efivars_size
{
7578 my $arch = get_vm_arch
($conf);
7579 my $efidisk = $conf->{efidisk0
} ? parse_drive
('efidisk0', $conf->{efidisk0
}) : undef;
7580 my $smm = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
7581 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
7582 die "uefi vars image '$ovmf_vars' not found\n" if ! -f
$ovmf_vars;
7583 return -s
$ovmf_vars;
7586 sub update_efidisk_size
{
7589 return if !defined($conf->{efidisk0
});
7591 my $disk = PVE
::QemuServer
::parse_drive
('efidisk0', $conf->{efidisk0
});
7592 $disk->{size
} = get_efivars_size
($conf);
7593 $conf->{efidisk0
} = print_drive
($disk);
7598 sub update_tpmstate_size
{
7601 my $disk = PVE
::QemuServer
::parse_drive
('tpmstate0', $conf->{tpmstate0
});
7602 $disk->{size
} = PVE
::QemuServer
::Drive
::TPMSTATE_DISK_SIZE
;
7603 $conf->{tpmstate0
} = print_drive
($disk);
7606 sub create_efidisk
($$$$$$$) {
7607 my ($storecfg, $storeid, $vmid, $fmt, $arch, $efidisk, $smm) = @_;
7609 my (undef, $ovmf_vars) = get_ovmf_files
($arch, $efidisk, $smm);
7610 die "EFI vars default image not found\n" if ! -f
$ovmf_vars;
7612 my $vars_size_b = -s
$ovmf_vars;
7613 my $vars_size = PVE
::Tools
::convert_size
($vars_size_b, 'b' => 'kb');
7614 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
7615 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
7617 qemu_img_convert
($ovmf_vars, $volid, $vars_size_b, undef, 0);
7618 my ($size) = PVE
::Storage
::volume_size_info
($storecfg, $volid, 3);
7620 return ($volid, $size/1024);
7623 sub vm_iothreads_list
{
7626 my $res = mon_cmd
($vmid, 'query-iothreads');
7629 foreach my $iothread (@$res) {
7630 $iothreads->{ $iothread->{id
} } = $iothread->{"thread-id"};
7637 my ($conf, $drive) = @_;
7641 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)) {
7643 } elsif ($conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single')) {
7649 my $controller = int($drive->{index} / $maxdev);
7650 my $controller_prefix = ($conf->{scsihw
} && $conf->{scsihw
} eq 'virtio-scsi-single')
7654 return ($maxdev, $controller, $controller_prefix);
7657 sub windows_version
{
7660 return 0 if !$ostype;
7664 if($ostype eq 'wxp' || $ostype eq 'w2k3' || $ostype eq 'w2k') {
7666 } elsif($ostype eq 'w2k8' || $ostype eq 'wvista') {
7668 } elsif ($ostype =~ m/^win(\d+)$/) {
7675 sub resolve_dst_disk_format
{
7676 my ($storecfg, $storeid, $src_volname, $format) = @_;
7677 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
7680 # if no target format is specified, use the source disk format as hint
7682 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7683 $format = qemu_img_format
($scfg, $src_volname);
7689 # test if requested format is supported - else use default
7690 my $supported = grep { $_ eq $format } @$validFormats;
7691 $format = $defFormat if !$supported;
7695 # NOTE: if this logic changes, please update docs & possibly gui logic
7696 sub find_vmstate_storage
{
7697 my ($conf, $storecfg) = @_;
7699 # first, return storage from conf if set
7700 return $conf->{vmstatestorage
} if $conf->{vmstatestorage
};
7702 my ($target, $shared, $local);
7704 foreach_storage_used_by_vm
($conf, sub {
7706 my $scfg = PVE
::Storage
::storage_config
($storecfg, $sid);
7707 my $dst = $scfg->{shared
} ? \
$shared : \
$local;
7708 $$dst = $sid if !$$dst || $scfg->{path
}; # prefer file based storage
7711 # second, use shared storage where VM has at least one disk
7712 # third, use local storage where VM has at least one disk
7713 # fall back to local storage
7714 $target = $shared // $local // 'local';
7720 my ($uuid, $uuid_str);
7721 UUID
::generate
($uuid);
7722 UUID
::unparse
($uuid, $uuid_str);
7726 sub generate_smbios1_uuid
{
7727 return "uuid=".generate_uuid
();
7733 mon_cmd
($vmid, 'nbd-server-stop');
7736 sub create_reboot_request
{
7738 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
7739 or die "failed to create reboot trigger file: $!\n";
7743 sub clear_reboot_request
{
7745 my $path = "/run/qemu-server/$vmid.reboot";
7748 $res = unlink($path);
7749 die "could not remove reboot request for $vmid: $!"
7750 if !$res && $! != POSIX
::ENOENT
;
7755 sub bootorder_from_legacy
{
7756 my ($conf, $bootcfg) = @_;
7758 my $boot = $bootcfg->{legacy
} || $boot_fmt->{legacy
}->{default};
7759 my $bootindex_hash = {};
7761 foreach my $o (split(//, $boot)) {
7762 $bootindex_hash->{$o} = $i*100;
7768 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7769 my ($ds, $drive) = @_;
7771 if (drive_is_cdrom
($drive, 1)) {
7772 if ($bootindex_hash->{d
}) {
7773 $bootorder->{$ds} = $bootindex_hash->{d
};
7774 $bootindex_hash->{d
} += 1;
7776 } elsif ($bootindex_hash->{c
}) {
7777 $bootorder->{$ds} = $bootindex_hash->{c
}
7778 if $conf->{bootdisk
} && $conf->{bootdisk
} eq $ds;
7779 $bootindex_hash->{c
} += 1;
7783 if ($bootindex_hash->{n
}) {
7784 for (my $i = 0; $i < $MAX_NETS; $i++) {
7785 my $netname = "net$i";
7786 next if !$conf->{$netname};
7787 $bootorder->{$netname} = $bootindex_hash->{n
};
7788 $bootindex_hash->{n
} += 1;
7795 # Generate default device list for 'boot: order=' property. Matches legacy
7796 # default boot order, but with explicit device names. This is important, since
7797 # the fallback for when neither 'order' nor the old format is specified relies
7798 # on 'bootorder_from_legacy' above, and it would be confusing if this diverges.
7799 sub get_default_bootdevices
{
7805 my $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 0);
7806 push @ret, $first if $first;
7809 $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 1);
7810 push @ret, $first if $first;
7813 for (my $i = 0; $i < $MAX_NETS; $i++) {
7814 my $netname = "net$i";
7815 next if !$conf->{$netname};
7816 push @ret, $netname;
7823 sub device_bootorder
{
7826 return bootorder_from_legacy
($conf) if !defined($conf->{boot
});
7828 my $boot = parse_property_string
($boot_fmt, $conf->{boot
});
7831 if (!defined($boot) || $boot->{legacy
}) {
7832 $bootorder = bootorder_from_legacy
($conf, $boot);
7833 } elsif ($boot->{order
}) {
7834 my $i = 100; # start at 100 to allow user to insert devices before us with -args
7835 for my $dev (PVE
::Tools
::split_list
($boot->{order
})) {
7836 $bootorder->{$dev} = $i++;
7843 sub register_qmeventd_handle
{
7847 my $peer = "/var/run/qmeventd.sock";
7852 $fh = IO
::Socket
::UNIX-
>new(Peer
=> $peer, Blocking
=> 0, Timeout
=> 1);
7854 if ($! != EINTR
&& $! != EAGAIN
) {
7855 die "unable to connect to qmeventd socket (vmid: $vmid) - $!\n";
7858 die "unable to connect to qmeventd socket (vmid: $vmid) - timeout "
7859 . "after $count retries\n";
7864 # send handshake to mark VM as backing up
7865 print $fh to_json
({vzdump
=> {vmid
=> "$vmid"}});
7867 # return handle to be closed later when inhibit is no longer required
7871 # bash completion helper
7873 sub complete_backup_archives
{
7874 my ($cmdname, $pname, $cvalue) = @_;
7876 my $cfg = PVE
::Storage
::config
();
7880 if ($cvalue =~ m/^([^:]+):/) {
7884 my $data = PVE
::Storage
::template_list
($cfg, $storeid, 'backup');
7887 foreach my $id (keys %$data) {
7888 foreach my $item (@{$data->{$id}}) {
7889 next if $item->{format
} !~ m/^vma\.(${\PVE::Storage::Plugin::COMPRESSOR_RE})$/;
7890 push @$res, $item->{volid
} if defined($item->{volid
});
7897 my $complete_vmid_full = sub {
7900 my $idlist = vmstatus
();
7904 foreach my $id (keys %$idlist) {
7905 my $d = $idlist->{$id};
7906 if (defined($running)) {
7907 next if $d->{template
};
7908 next if $running && $d->{status
} ne 'running';
7909 next if !$running && $d->{status
} eq 'running';
7918 return &$complete_vmid_full();
7921 sub complete_vmid_stopped
{
7922 return &$complete_vmid_full(0);
7925 sub complete_vmid_running
{
7926 return &$complete_vmid_full(1);
7929 sub complete_storage
{
7931 my $cfg = PVE
::Storage
::config
();
7932 my $ids = $cfg->{ids
};
7935 foreach my $sid (keys %$ids) {
7936 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, undef, 1);
7937 next if !$ids->{$sid}->{content
}->{images
};
7944 sub complete_migration_storage
{
7945 my ($cmd, $param, $current_value, $all_args) = @_;
7947 my $targetnode = @$all_args[1];
7949 my $cfg = PVE
::Storage
::config
();
7950 my $ids = $cfg->{ids
};
7953 foreach my $sid (keys %$ids) {
7954 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, $targetnode, 1);
7955 next if !$ids->{$sid}->{content
}->{images
};
7964 my $qmpstatus = eval {
7965 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid);
7966 mon_cmd
($vmid, "query-status");
7969 return $qmpstatus && $qmpstatus->{status
} eq "paused";
7972 sub check_volume_storage_type
{
7973 my ($storecfg, $vol) = @_;
7975 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($vol);
7976 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7977 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $vol);
7979 die "storage '$storeid' does not support content-type '$vtype'\n"
7980 if !$scfg->{content
}->{$vtype};