1 package PVE
::QemuServer
;
11 use File
::Copy
qw(copy);
24 use Storable
qw(dclone);
25 use Time
::HiRes
qw(gettimeofday);
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/';
67 "$EDK2_FW_BASE/OVMF_CODE.fd",
68 "$EDK2_FW_BASE/OVMF_VARS.fd"
71 "$EDK2_FW_BASE/AAVMF_CODE.fd",
72 "$EDK2_FW_BASE/AAVMF_VARS.fd"
76 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
78 # Note about locking: we use flock on the config file protect
79 # against concurent actions.
80 # Aditionaly, we have a 'lock' setting in the config file. This
81 # can be set to 'migrate', 'backup', 'snapshot' or 'rollback'. Most actions are not
82 # allowed when such lock is set. But you can ignore this kind of
83 # lock with the --skiplock flag.
85 cfs_register_file
('/qemu-server/',
89 PVE
::JSONSchema
::register_standard_option
('pve-qm-stateuri', {
90 description
=> "Some command save/restore state from this location.",
96 PVE
::JSONSchema
::register_standard_option
('pve-qemu-machine', {
97 description
=> "Specifies the Qemu machine type.",
99 pattern
=> '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
106 my ($map, $source) = @_;
108 return $source if !defined($map);
110 return $map->{entries
}->{$source}
111 if $map->{entries
} && defined($map->{entries
}->{$source});
113 return $map->{default} if $map->{default};
115 # identity (fallback)
119 PVE
::JSONSchema
::register_standard_option
('pve-targetstorage', {
120 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.",
122 format
=> 'storagepair-list',
126 #no warnings 'redefine';
130 $nodename_cache //= PVE
::INotify
::nodename
();
131 return $nodename_cache;
138 enum
=> [qw(i6300esb ib700)],
139 description
=> "Watchdog type to emulate.",
140 default => 'i6300esb',
145 enum
=> [qw(reset shutdown poweroff pause debug none)],
146 description
=> "The action to perform if after activation the guest fails to poll the watchdog in time.",
150 PVE
::JSONSchema
::register_format
('pve-qm-watchdog', $watchdog_fmt);
154 description
=> "Enable/disable Qemu GuestAgent.",
159 fstrim_cloned_disks
=> {
160 description
=> "Run fstrim after moving a disk or migrating the VM.",
166 description
=> "Select the agent type",
170 enum
=> [qw(virtio isa)],
176 description
=> "Select the VGA type.",
181 enum
=> [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio vmware)],
184 description
=> "Sets the VGA memory (in MiB). Has no effect with serial display.",
196 description
=> "The size of the file in MB.",
200 pattern
=> '[a-zA-Z0-9\-]+',
202 format_description
=> 'string',
203 description
=> "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
210 enum
=> [qw(ich9-intel-hda intel-hda AC97)],
211 description
=> "Configure an audio device."
215 enum
=> ['spice', 'none'],
218 description
=> "Driver backend for the audio device."
222 my $spice_enhancements_fmt = {
227 description
=> "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
231 enum
=> ['off', 'all', 'filter'],
234 description
=> "Enable video streaming. Uses compression for detected video streams."
241 enum
=> ['/dev/urandom', '/dev/random', '/dev/hwrng'],
243 description
=> "The file on the host to gather entropy from. In most"
244 . " cases /dev/urandom should be preferred over /dev/random"
245 . " to avoid entropy-starvation issues on the host. Using"
246 . " urandom does *not* decrease security in any meaningful"
247 . " way, as it's still seeded from real entropy, and the"
248 . " bytes provided will most likely be mixed with real"
249 . " entropy on the guest as well. /dev/hwrng can be used"
250 . " to pass through a hardware RNG from the host.",
254 description
=> "Maximum bytes of entropy injected into the guest every"
255 . " 'period' milliseconds. Prefer a lower value when using"
256 . " /dev/random as source. Use 0 to disable limiting"
257 . " (potentially dangerous!).",
260 # default is 1 KiB/s, provides enough entropy to the guest to avoid
261 # boot-starvation issues (e.g. systemd etc...) while allowing no chance
262 # of overwhelming the host, provided we're reading from /dev/urandom
267 description
=> "Every 'period' milliseconds the entropy-injection quota"
268 . " is reset, allowing the guest to retrieve another"
269 . " 'max_bytes' of entropy.",
279 description
=> "Specifies whether a VM will be started during system bootup.",
285 description
=> "Automatic restart after crash (currently ignored).",
290 type
=> 'string', format
=> 'pve-hotplug-features',
291 description
=> "Selectively enable hotplug features. This is a comma separated list of hotplug features: 'network', 'disk', 'cpu', 'memory' and 'usb'. Use '0' to disable hotplug completely. Value '1' is an alias for the default 'network,disk,usb'.",
292 default => 'network,disk,usb',
297 description
=> "Allow reboot. If set to '0' the VM exit on reboot.",
303 description
=> "Lock/unlock the VM.",
304 enum
=> [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
309 description
=> "Limit of CPU usage.",
310 verbose_description
=> "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has total of '2' CPU time. Value '0' indicates no CPU limit.",
318 description
=> "CPU weight for a VM.",
319 verbose_description
=> "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs.",
327 description
=> "Amount of RAM for the VM in MB. This is the maximum available memory when you use the balloon device.",
334 description
=> "Amount of target RAM for the VM in MB. Using zero disables the ballon driver.",
340 description
=> "Amount of memory shares for auto-ballooning. The larger the number is, the more memory this VM gets. Number is relative to weights of all other running VMs. Using zero disables auto-ballooning. Auto-ballooning is done by pvestatd.",
348 description
=> "Keybord layout for vnc server. Default is read from the '/etc/pve/datacenter.cfg' configuration file.".
349 "It should not be necessary to set it.",
350 enum
=> PVE
::Tools
::kvmkeymaplist
(),
355 type
=> 'string', format
=> 'dns-name',
356 description
=> "Set a name for the VM. Only used on the configuration web interface.",
361 description
=> "SCSI controller model",
362 enum
=> [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
368 description
=> "Description for the VM. Shown in the web-interface VM's summary."
369 ." This is saved as comment inside the configuration file.",
370 maxLength
=> 1024 * 8,
375 enum
=> [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 l24 l26 solaris)],
376 description
=> "Specify guest operating system.",
377 verbose_description
=> <<EODESC,
378 Specify guest operating system. This is used to enable special
379 optimization/features for specific operating systems:
382 other;; unspecified OS
383 wxp;; Microsoft Windows XP
384 w2k;; Microsoft Windows 2000
385 w2k3;; Microsoft Windows 2003
386 w2k8;; Microsoft Windows 2008
387 wvista;; Microsoft Windows Vista
388 win7;; Microsoft Windows 7
389 win8;; Microsoft Windows 8/2012/2012r2
390 win10;; Microsoft Windows 10/2016/2019
391 l24;; Linux 2.4 Kernel
392 l26;; Linux 2.6 - 5.X Kernel
393 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
398 type
=> 'string', format
=> 'pve-qm-boot',
399 description
=> "Specify guest boot order. Use with 'order=', usage with"
400 . " no key or 'legacy=' is deprecated.",
404 type
=> 'string', format
=> 'pve-qm-bootdisk',
405 description
=> "Enable booting from specified disk. Deprecated: Use 'boot: order=foo;bar' instead.",
406 pattern
=> '(ide|sata|scsi|virtio)\d+',
411 description
=> "The number of CPUs. Please use option -sockets instead.",
418 description
=> "The number of CPU sockets.",
425 description
=> "The number of cores per socket.",
432 description
=> "Enable/disable NUMA.",
438 description
=> "Enable/disable hugepages memory.",
439 enum
=> [qw(any 2 1024)],
445 description
=> "Use together with hugepages. If enabled, hugepages will not not be deleted"
446 ." after VM shutdown and can be used for subsequent starts.",
451 description
=> "Number of hotplugged vcpus.",
458 description
=> "Enable/disable ACPI.",
463 description
=> "Enable/disable Qemu GuestAgent and its properties.",
465 format
=> $agent_fmt,
470 description
=> "Enable/disable KVM hardware virtualization.",
476 description
=> "Enable/disable time drift fix.",
482 description
=> "Set the real time clock to local time. This is enabled by default if ostype"
483 ." indicates a Microsoft OS.",
488 description
=> "Freeze CPU at startup (use 'c' monitor command to start execution).",
492 type
=> 'string', format
=> $vga_fmt,
493 description
=> "Configure the VGA hardware.",
494 verbose_description
=> "Configure the VGA Hardware. If you want to use high resolution"
495 ." modes (>= 1280x1024x16) you may need to increase the vga memory option. Since QEMU"
496 ." 2.9 the default VGA display type is 'std' for all OS types besides some Windows"
497 ." versions (XP and older) which use 'cirrus'. The 'qxl' option enables the SPICE"
498 ." display server. For win* OS you can select how many independent displays you want,"
499 ." Linux guests can add displays them self.\nYou can also run without any graphic card,"
500 ." using a serial device as terminal.",
504 type
=> 'string', format
=> 'pve-qm-watchdog',
505 description
=> "Create a virtual hardware watchdog device.",
506 verbose_description
=> "Create a virtual hardware watchdog device. Once enabled (by a guest"
507 ." action), the watchdog must be periodically polled by an agent inside the guest or"
508 ." else the watchdog will reset the guest (or execute the respective action specified)",
513 typetext
=> "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
514 description
=> "Set the initial date of the real time clock. Valid format for date are:"
515 ."'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
516 pattern
=> '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
519 startup
=> get_standard_option
('pve-startup-order'),
523 description
=> "Enable/disable Template.",
529 description
=> "Arbitrary arguments passed to kvm.",
530 verbose_description
=> <<EODESCR,
531 Arbitrary arguments passed to kvm, for example:
533 args: -no-reboot -no-hpet
535 NOTE: this option is for experts only.
542 description
=> "Enable/disable the USB tablet device.",
543 verbose_description
=> "Enable/disable the USB tablet device. This device is usually needed"
544 ." to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with"
545 ." normal VNC clients. If you're running lots of console-only guests on one host, you"
546 ." may consider disabling this to save some context switches. This is turned off by"
547 ." default if you use spice (`qm set <vmid> --vga qxl`).",
552 description
=> "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
556 migrate_downtime
=> {
559 description
=> "Set maximum tolerated downtime (in seconds) for migrations.",
565 type
=> 'string', format
=> 'pve-qm-ide',
566 typetext
=> '<volume>',
567 description
=> "This is an alias for option -ide2",
571 description
=> "Emulated CPU type.",
573 format
=> 'pve-vm-cpu-conf',
575 parent
=> get_standard_option
('pve-snapshot-name', {
577 description
=> "Parent snapshot name. This is used internally, and should not be modified.",
581 description
=> "Timestamp for snapshots.",
587 type
=> 'string', format
=> 'pve-volume-id',
588 description
=> "Reference to a volume which stores the VM state. This is used internally"
591 vmstatestorage
=> get_standard_option
('pve-storage-id', {
592 description
=> "Default storage for VM state volumes/files.",
595 runningmachine
=> get_standard_option
('pve-qemu-machine', {
596 description
=> "Specifies the QEMU machine type of the running vm. This is used internally"
600 description
=> "Specifies the QEMU '-cpu' parameter of the running vm. This is used"
601 ." internally for snapshots.",
604 pattern
=> $PVE::QemuServer
::CPUConfig
::qemu_cmdline_cpu_re
,
605 format_description
=> 'QEMU -cpu parameter'
607 machine
=> get_standard_option
('pve-qemu-machine'),
609 description
=> "Virtual processor architecture. Defaults to the host.",
612 enum
=> [qw(x86_64 aarch64)],
615 description
=> "Specify SMBIOS type 1 fields.",
616 type
=> 'string', format
=> 'pve-qm-smbios1',
623 description
=> "Sets the protection flag of the VM. This will disable the remove VM and"
624 ." remove disk operations.",
630 enum
=> [ qw(seabios ovmf) ],
631 description
=> "Select BIOS implementation.",
632 default => 'seabios',
636 pattern
=> '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
637 format_description
=> 'UUID',
638 description
=> "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0'"
639 ." to disable explicitly.",
640 verbose_description
=> "The VM generation ID (vmgenid) device exposes a 128-bit integer"
641 ." value identifier to the guest OS. This allows to notify the guest operating system"
642 ." when the virtual machine is executed with a different configuration (e.g. snapshot"
643 ." execution or creation from a template). The guest operating system notices the"
644 ." change, and is then able to react as appropriate by marking its copies of"
645 ." distributed databases as dirty, re-initializing its random number generator, etc.\n"
646 ."Note that auto-creation only works when done through API/CLI create or update methods"
647 .", but not when manually editing the config file.",
648 default => "1 (autogenerated)",
653 format
=> 'pve-volume-id',
655 description
=> "Script that will be executed during various steps in the vms lifetime.",
659 format
=> $ivshmem_fmt,
660 description
=> "Inter-VM shared memory. Useful for direct communication between VMs, or to"
666 format
=> $audio_fmt,
667 description
=> "Configure a audio device, useful in combination with QXL/Spice.",
670 spice_enhancements
=> {
672 format
=> $spice_enhancements_fmt,
673 description
=> "Configure additional enhancements for SPICE.",
677 type
=> 'string', format
=> 'pve-tag-list',
678 description
=> 'Tags of the VM. This is only meta information.',
684 description
=> "Configure a VirtIO-based Random Number Generator.",
693 description
=> 'Specify a custom file containing all meta data passed to the VM via"
694 ." cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
695 format
=> 'pve-volume-id',
696 format_description
=> 'volume',
701 description
=> 'Specify a custom file containing all network data passed to the VM via'
703 format
=> 'pve-volume-id',
704 format_description
=> 'volume',
709 description
=> 'Specify a custom file containing all user data passed to the VM via'
711 format
=> 'pve-volume-id',
712 format_description
=> 'volume',
715 PVE
::JSONSchema
::register_format
('pve-qm-cicustom', $cicustom_fmt);
717 my $confdesc_cloudinit = {
721 description
=> 'Specifies the cloud-init configuration format. The default depends on the'
722 .' configured operating system type (`ostype`. We use the `nocloud` format for Linux,'
723 .' and `configdrive2` for windows.',
724 enum
=> ['configdrive2', 'nocloud', 'opennebula'],
729 description
=> "cloud-init: User name to change ssh keys and password for instead of the"
730 ." image's configured default user.",
735 description
=> 'cloud-init: Password to assign the user. Using this is generally not'
736 .' recommended. Use ssh keys instead. Also note that older cloud-init versions do not'
737 .' support hashed passwords.',
742 description
=> 'cloud-init: Specify custom files to replace the automatically generated'
744 format
=> 'pve-qm-cicustom',
749 description
=> "cloud-init: Sets DNS search domains for a container. Create will'
750 .' automatically use the setting from the host if neither searchdomain nor nameserver'
755 type
=> 'string', format
=> 'address-list',
756 description
=> "cloud-init: Sets DNS server IP address for a container. Create will'
757 .' automatically use the setting from the host if neither searchdomain nor nameserver'
763 format
=> 'urlencoded',
764 description
=> "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
768 # what about other qemu settings ?
770 #machine => 'string',
783 ##soundhw => 'string',
785 while (my ($k, $v) = each %$confdesc) {
786 PVE
::JSONSchema
::register_standard_option
("pve-qm-$k", $v);
789 my $MAX_USB_DEVICES = 5;
791 my $MAX_SERIAL_PORTS = 4;
792 my $MAX_PARALLEL_PORTS = 3;
798 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
799 description
=> "CPUs accessing this NUMA node.",
800 format_description
=> "id[-id];...",
804 description
=> "Amount of memory this NUMA node provides.",
809 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
810 description
=> "Host NUMA nodes to use.",
811 format_description
=> "id[-id];...",
816 enum
=> [qw(preferred bind interleave)],
817 description
=> "NUMA allocation policy.",
821 PVE
::JSONSchema
::register_format
('pve-qm-numanode', $numa_fmt);
824 type
=> 'string', format
=> $numa_fmt,
825 description
=> "NUMA topology.",
827 PVE
::JSONSchema
::register_standard_option
("pve-qm-numanode", $numadesc);
829 for (my $i = 0; $i < $MAX_NUMA; $i++) {
830 $confdesc->{"numa$i"} = $numadesc;
833 my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000', 'pcnet', 'virtio',
834 'ne2k_isa', 'i82551', 'i82557b', 'i82559er', 'vmxnet3',
835 'e1000-82540em', 'e1000-82544gc', 'e1000-82545em'];
836 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
838 my $net_fmt_bridge_descr = <<__EOD__;
839 Bridge to attach the network device to. The Proxmox VE standard bridge
842 If you do not specify a bridge, we create a kvm user (NATed) network
843 device, which provides DHCP and DNS services. The following addresses
850 The DHCP server assign addresses to the guest starting from 10.0.2.15.
854 macaddr
=> get_standard_option
('mac-addr', {
855 description
=> "MAC address. That address must be unique withing your network. This is"
856 ." automatically generated if not specified.",
860 description
=> "Network Card Model. The 'virtio' model provides the best performance with"
861 ." very low CPU overhead. If your guest does not support this driver, it is usually"
862 ." best to use 'e1000'.",
863 enum
=> $nic_model_list,
866 (map { $_ => { keyAlias
=> 'model', alias
=> 'macaddr' }} @$nic_model_list),
869 description
=> $net_fmt_bridge_descr,
870 format_description
=> 'bridge',
871 pattern
=> '[-_.\w\d]+',
876 minimum
=> 0, maximum
=> 16,
877 description
=> 'Number of packet queues to be used on the device.',
883 description
=> "Rate limit in mbps (megabytes per second) as floating point number.",
888 minimum
=> 1, maximum
=> 4094,
889 description
=> 'VLAN tag to apply to packets on this interface.',
894 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
895 description
=> 'VLAN trunks to pass through this interface.',
896 format_description
=> 'vlanid[;vlanid...]',
901 description
=> 'Whether this interface should be protected by the firewall.',
906 description
=> 'Whether this interface should be disconnected (like pulling the plug).',
911 minimum
=> 1, maximum
=> 65520,
912 description
=> "Force MTU, for VirtIO only. Set to '1' to use the bridge MTU",
919 type
=> 'string', format
=> $net_fmt,
920 description
=> "Specify network devices.",
923 PVE
::JSONSchema
::register_standard_option
("pve-qm-net", $netdesc);
928 format
=> 'pve-ipv4-config',
929 format_description
=> 'IPv4Format/CIDR',
930 description
=> 'IPv4 address in CIDR format.',
937 format_description
=> 'GatewayIPv4',
938 description
=> 'Default gateway for IPv4 traffic.',
944 format
=> 'pve-ipv6-config',
945 format_description
=> 'IPv6Format/CIDR',
946 description
=> 'IPv6 address in CIDR format.',
953 format_description
=> 'GatewayIPv6',
954 description
=> 'Default gateway for IPv6 traffic.',
959 PVE
::JSONSchema
::register_format
('pve-qm-ipconfig', $ipconfig_fmt);
962 type
=> 'string', format
=> 'pve-qm-ipconfig',
963 description
=> <<'EODESCR',
964 cloud-init: Specify IP addresses and gateways for the corresponding interface.
966 IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
968 The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit
969 gateway should be provided.
970 For IPv6 the special string 'auto' can be used to use stateless autoconfiguration. This requires
971 cloud-init 19.4 or newer.
973 If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using
977 PVE
::JSONSchema
::register_standard_option
("pve-qm-ipconfig", $netdesc);
979 for (my $i = 0; $i < $MAX_NETS; $i++) {
980 $confdesc->{"net$i"} = $netdesc;
981 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
984 foreach my $key (keys %$confdesc_cloudinit) {
985 $confdesc->{$key} = $confdesc_cloudinit->{$key};
988 PVE
::JSONSchema
::register_format
('pve-volume-id-or-qm-path', \
&verify_volume_id_or_qm_path
);
989 sub verify_volume_id_or_qm_path
{
990 my ($volid, $noerr) = @_;
992 if ($volid eq 'none' || $volid eq 'cdrom' || $volid =~ m
|^/|) {
996 # if its neither 'none' nor 'cdrom' nor a path, check if its a volume-id
997 $volid = eval { PVE
::JSONSchema
::check_format
('pve-volume-id', $volid, '') };
1008 type
=> 'string', format
=> 'pve-qm-usb-device',
1009 format_description
=> 'HOSTUSBDEVICE|spice',
1010 description
=> <<EODESCR,
1011 The Host USB device or port or the value 'spice'. HOSTUSBDEVICE syntax is:
1013 'bus-port(.port)*' (decimal numbers) or
1014 'vendor_id:product_id' (hexadeciaml numbers) or
1017 You can use the 'lsusb -t' command to list existing usb devices.
1019 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1020 machines - use with special care.
1022 The value 'spice' can be used to add a usb redirection devices for spice.
1028 description
=> "Specifies whether if given host option is a USB3 device or port.",
1035 type
=> 'string', format
=> $usb_fmt,
1036 description
=> "Configure an USB device (n is 0 to 4).",
1038 PVE
::JSONSchema
::register_standard_option
("pve-qm-usb", $usbdesc);
1043 pattern
=> '(/dev/.+|socket)',
1044 description
=> "Create a serial device inside the VM (n is 0 to 3)",
1045 verbose_description
=> <<EODESCR,
1046 Create a serial device inside the VM (n is 0 to 3), and pass through a
1047 host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1048 host side (use 'qm terminal' to open a terminal connection).
1050 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines -
1051 use with special care.
1053 CAUTION: Experimental! User reported problems with this option.
1060 pattern
=> '/dev/parport\d+|/dev/usb/lp\d+',
1061 description
=> "Map host parallel devices (n is 0 to 2).",
1062 verbose_description
=> <<EODESCR,
1063 Map host parallel devices (n is 0 to 2).
1065 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1066 machines - use with special care.
1068 CAUTION: Experimental! User reported problems with this option.
1072 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1073 $confdesc->{"parallel$i"} = $paralleldesc;
1076 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1077 $confdesc->{"serial$i"} = $serialdesc;
1080 for (my $i = 0; $i < $PVE::QemuServer
::PCI
::MAX_HOSTPCI_DEVICES
; $i++) {
1081 $confdesc->{"hostpci$i"} = $PVE::QemuServer
::PCI
::hostpcidesc
;
1084 for my $key (keys %{$PVE::QemuServer
::Drive
::drivedesc_hash
}) {
1085 $confdesc->{$key} = $PVE::QemuServer
::Drive
::drivedesc_hash-
>{$key};
1088 for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
1089 $confdesc->{"usb$i"} = $usbdesc;
1097 description
=> "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)."
1098 . " Deprecated, use 'order=' instead.",
1099 pattern
=> '[acdn]{1,4}',
1100 format_description
=> "[acdn]{1,4}",
1102 # note: this is also the fallback if boot: is not given at all
1108 format
=> 'pve-qm-bootdev-list',
1109 format_description
=> "device[;device...]",
1110 description
=> <<EODESC,
1111 The guest will attempt to boot from devices in the order they appear here.
1113 Disks, optical drives and passed-through storage USB devices will be directly
1114 booted from, NICs will load PXE, and PCIe devices will either behave like disks
1115 (e.g. NVMe) or load an option ROM (e.g. RAID controller, hardware NIC).
1117 Note that only devices in this list will be marked as bootable and thus loaded
1118 by the guest firmware (BIOS/UEFI). If you require multiple disks for booting
1119 (e.g. software-raid), you need to specify all of them here.
1121 Overrides the deprecated 'legacy=[acdn]*' value when given.
1125 PVE
::JSONSchema
::register_format
('pve-qm-boot', $boot_fmt);
1127 PVE
::JSONSchema
::register_format
('pve-qm-bootdev', \
&verify_bootdev
);
1128 sub verify_bootdev
{
1129 my ($dev, $noerr) = @_;
1131 return $dev if PVE
::QemuServer
::Drive
::is_valid_drivename
($dev) && $dev !~ m/^efidisk/;
1135 return 0 if $dev !~ m/^$base\d+$/;
1136 return 0 if !$confdesc->{$dev};
1140 return $dev if $check->("net");
1141 return $dev if $check->("usb");
1142 return $dev if $check->("hostpci");
1145 die "invalid boot device '$dev'\n";
1148 sub print_bootorder
{
1150 return "" if !@$devs;
1151 my $data = { order
=> join(';', @$devs) };
1152 return PVE
::JSONSchema
::print_property_string
($data, $boot_fmt);
1155 my $kvm_api_version = 0;
1158 return $kvm_api_version if $kvm_api_version;
1160 open my $fh, '<', '/dev/kvm' or return;
1162 # 0xae00 => KVM_GET_API_VERSION
1163 $kvm_api_version = ioctl($fh, 0xae00, 0);
1166 return $kvm_api_version;
1169 my $kvm_user_version = {};
1172 sub kvm_user_version
{
1175 $binary //= get_command_for_arch
(get_host_arch
()); # get the native arch by default
1176 my $st = stat($binary);
1178 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1179 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1180 $cachedmtime == $st->mtime;
1182 $kvm_user_version->{$binary} = 'unknown';
1183 $kvm_mtime->{$binary} = $st->mtime;
1187 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1188 $kvm_user_version->{$binary} = $2;
1192 eval { run_command
([$binary, '--version'], outfunc
=> $code); };
1195 return $kvm_user_version->{$binary};
1198 my sub extract_version
{
1199 my ($machine_type, $version) = @_;
1200 $version = kvm_user_version
() if !defined($version);
1201 PVE
::QemuServer
::Machine
::extract_version
($machine_type, $version)
1204 sub kernel_has_vhost_net
{
1205 return -c
'/dev/vhost-net';
1210 return defined($confdesc->{$key});
1214 sub get_cdrom_path
{
1216 return $cdrom_path if $cdrom_path;
1218 return $cdrom_path = "/dev/cdrom" if -l
"/dev/cdrom";
1219 return $cdrom_path = "/dev/cdrom1" if -l
"/dev/cdrom1";
1220 return $cdrom_path = "/dev/cdrom2" if -l
"/dev/cdrom2";
1224 my ($storecfg, $vmid, $cdrom) = @_;
1226 if ($cdrom eq 'cdrom') {
1227 return get_cdrom_path
();
1228 } elsif ($cdrom eq 'none') {
1230 } elsif ($cdrom =~ m
|^/|) {
1233 return PVE
::Storage
::path
($storecfg, $cdrom);
1237 # try to convert old style file names to volume IDs
1238 sub filename_to_volume_id
{
1239 my ($vmid, $file, $media) = @_;
1241 if (!($file eq 'none' || $file eq 'cdrom' ||
1242 $file =~ m
|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1244 return if $file =~ m
|/|;
1246 if ($media && $media eq 'cdrom') {
1247 $file = "local:iso/$file";
1249 $file = "local:$vmid/$file";
1256 sub verify_media_type
{
1257 my ($opt, $vtype, $media) = @_;
1262 if ($media eq 'disk') {
1264 } elsif ($media eq 'cdrom') {
1267 die "internal error";
1270 return if ($vtype eq $etype);
1272 raise_param_exc
({ $opt => "unexpected media type ($vtype != $etype)" });
1275 sub cleanup_drive_path
{
1276 my ($opt, $storecfg, $drive) = @_;
1278 # try to convert filesystem paths to volume IDs
1280 if (($drive->{file
} !~ m/^(cdrom|none)$/) &&
1281 ($drive->{file
} !~ m
|^/dev/.+|) &&
1282 ($drive->{file
} !~ m/^([^:]+):(.+)$/) &&
1283 ($drive->{file
} !~ m/^\d+$/)) {
1284 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($storecfg, $drive->{file
});
1285 raise_param_exc
({ $opt => "unable to associate path '$drive->{file}' to any storage"})
1287 $drive->{media
} = 'cdrom' if !$drive->{media
} && $vtype eq 'iso';
1288 verify_media_type
($opt, $vtype, $drive->{media
});
1289 $drive->{file
} = $volid;
1292 $drive->{media
} = 'cdrom' if !$drive->{media
} && $drive->{file
} =~ m/^(cdrom|none)$/;
1295 sub parse_hotplug_features
{
1300 return $res if $data eq '0';
1302 $data = $confdesc->{hotplug
}->{default} if $data eq '1';
1304 foreach my $feature (PVE
::Tools
::split_list
($data)) {
1305 if ($feature =~ m/^(network|disk|cpu|memory|usb)$/) {
1308 die "invalid hotplug feature '$feature'\n";
1314 PVE
::JSONSchema
::register_format
('pve-hotplug-features', \
&pve_verify_hotplug_features
);
1315 sub pve_verify_hotplug_features
{
1316 my ($value, $noerr) = @_;
1318 return $value if parse_hotplug_features
($value);
1322 die "unable to parse hotplug option\n";
1326 my($fh, $noerr) = @_;
1329 my $SG_GET_VERSION_NUM = 0x2282;
1331 my $versionbuf = "\x00" x
8;
1332 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1334 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
1337 my $version = unpack("I", $versionbuf);
1338 if ($version < 30000) {
1339 die "scsi generic interface too old\n" if !$noerr;
1343 my $buf = "\x00" x
36;
1344 my $sensebuf = "\x00" x
8;
1345 my $cmd = pack("C x3 C x1", 0x12, 36);
1347 # see /usr/include/scsi/sg.h
1348 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";
1350 my $packet = pack($sg_io_hdr_t, ord('S'), -3, length($cmd),
1351 length($sensebuf), 0, length($buf), $buf,
1352 $cmd, $sensebuf, 6000);
1354 $ret = ioctl($fh, $SG_IO, $packet);
1356 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
1360 my @res = unpack($sg_io_hdr_t, $packet);
1361 if ($res[17] || $res[18]) {
1362 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
1367 (my $byte0, my $byte1, $res->{vendor
},
1368 $res->{product
}, $res->{revision
}) = unpack("C C x6 A8 A16 A4", $buf);
1370 $res->{removable
} = $byte1 & 128 ?
1 : 0;
1371 $res->{type
} = $byte0 & 31;
1379 my $fh = IO
::File-
>new("+<$path") || return;
1380 my $res = scsi_inquiry
($fh, 1);
1386 sub print_tabletdevice_full
{
1387 my ($conf, $arch) = @_;
1389 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1391 # we use uhci for old VMs because tablet driver was buggy in older qemu
1393 if (PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf) || $arch eq 'aarch64') {
1399 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1402 sub print_keyboarddevice_full
{
1403 my ($conf, $arch, $machine) = @_;
1405 return if $arch ne 'aarch64';
1407 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1410 my sub get_drive_id
{
1412 return "$drive->{interface}$drive->{index}";
1415 sub print_drivedevice_full
{
1416 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1421 my $drive_id = get_drive_id
($drive);
1422 if ($drive->{interface
} eq 'virtio') {
1423 my $pciaddr = print_pci_addr
("$drive_id", $bridges, $arch, $machine_type);
1424 $device = "virtio-blk-pci,drive=drive-$drive_id,id=${drive_id}${pciaddr}";
1425 $device .= ",iothread=iothread-$drive_id" if $drive->{iothread
};
1426 } elsif ($drive->{interface
} eq 'scsi') {
1428 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
1429 my $unit = $drive->{index} % $maxdev;
1430 my $devicetype = 'hd';
1432 if (drive_is_cdrom
($drive)) {
1435 if ($drive->{file
} =~ m
|^/|) {
1436 $path = $drive->{file
};
1437 if (my $info = path_is_scsi
($path)) {
1438 if ($info->{type
} == 0 && $drive->{scsiblock
}) {
1439 $devicetype = 'block';
1440 } elsif ($info->{type
} == 1) { # tape
1441 $devicetype = 'generic';
1445 $path = PVE
::Storage
::path
($storecfg, $drive->{file
});
1448 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1449 my $version = extract_version
($machine_type, kvm_user_version
());
1450 if ($path =~ m/^iscsi\:\/\
// &&
1451 !min_version
($version, 4, 1)) {
1452 $devicetype = 'generic';
1456 if (!$conf->{scsihw
} || $conf->{scsihw
} =~ m/^lsi/ || $conf->{scsihw
} eq 'pvscsi') {
1457 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
1459 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
1460 .",lun=$drive->{index}";
1462 $device .= ",drive=drive-$drive_id,id=$drive_id";
1464 if ($drive->{ssd
} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1465 $device .= ",rotation_rate=1";
1467 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1469 } elsif ($drive->{interface
} eq 'ide' || $drive->{interface
} eq 'sata') {
1470 my $maxdev = ($drive->{interface
} eq 'sata') ?
$PVE::QemuServer
::Drive
::MAX_SATA_DISKS
: 2;
1471 my $controller = int($drive->{index} / $maxdev);
1472 my $unit = $drive->{index} % $maxdev;
1473 my $devicetype = ($drive->{media
} && $drive->{media
} eq 'cdrom') ?
"cd" : "hd";
1475 $device = "ide-$devicetype";
1476 if ($drive->{interface
} eq 'ide') {
1477 $device .= ",bus=ide.$controller,unit=$unit";
1479 $device .= ",bus=ahci$controller.$unit";
1481 $device .= ",drive=drive-$drive_id,id=$drive_id";
1483 if ($devicetype eq 'hd') {
1484 if (my $model = $drive->{model
}) {
1485 $model = URI
::Escape
::uri_unescape
($model);
1486 $device .= ",model=$model";
1488 if ($drive->{ssd
}) {
1489 $device .= ",rotation_rate=1";
1492 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1493 } elsif ($drive->{interface
} eq 'usb') {
1495 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1497 die "unsupported interface type";
1500 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex
};
1502 if (my $serial = $drive->{serial
}) {
1503 $serial = URI
::Escape
::uri_unescape
($serial);
1504 $device .= ",serial=$serial";
1511 sub get_initiator_name
{
1514 my $fh = IO
::File-
>new('/etc/iscsi/initiatorname.iscsi') || return;
1515 while (defined(my $line = <$fh>)) {
1516 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1525 sub print_drive_commandline_full
{
1526 my ($storecfg, $vmid, $drive, $pbs_name, $io_uring) = @_;
1529 my $volid = $drive->{file
};
1530 my $format = $drive->{format
};
1531 my $drive_id = get_drive_id
($drive);
1533 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1534 my $scfg = $storeid ? PVE
::Storage
::storage_config
($storecfg, $storeid) : undef;
1536 if (drive_is_cdrom
($drive)) {
1537 $path = get_iso_path
($storecfg, $vmid, $volid);
1538 die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
1541 $path = PVE
::Storage
::path
($storecfg, $volid);
1542 $format //= qemu_img_format
($scfg, $volname);
1549 my $is_rbd = $path =~ m/^rbd:/;
1552 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1553 foreach my $o (@qemu_drive_options) {
1554 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1557 # snapshot only accepts on|off
1558 if (defined($drive->{snapshot
})) {
1559 my $v = $drive->{snapshot
} ?
'on' : 'off';
1560 $opts .= ",snapshot=$v";
1563 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1564 my ($dir, $qmpname) = @$type;
1565 if (my $v = $drive->{"mbps$dir"}) {
1566 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1568 if (my $v = $drive->{"mbps${dir}_max"}) {
1569 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1571 if (my $v = $drive->{"bps${dir}_max_length"}) {
1572 $opts .= ",throttling.bps$qmpname-max-length=$v";
1574 if (my $v = $drive->{"iops${dir}"}) {
1575 $opts .= ",throttling.iops$qmpname=$v";
1577 if (my $v = $drive->{"iops${dir}_max"}) {
1578 $opts .= ",throttling.iops$qmpname-max=$v";
1580 if (my $v = $drive->{"iops${dir}_max_length"}) {
1581 $opts .= ",throttling.iops$qmpname-max-length=$v";
1586 $format = "rbd" if $is_rbd;
1587 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1589 $opts .= ",format=alloc-track,file.driver=$format";
1591 $opts .= ",format=$format";
1594 my $cache_direct = 0;
1596 if (my $cache = $drive->{cache
}) {
1597 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1598 } elsif (!drive_is_cdrom
($drive) && !($scfg && $scfg->{type
} eq 'btrfs' && !$scfg->{nocow
})) {
1599 $opts .= ",cache=none";
1603 if (!$drive->{aio
}) {
1605 # io_uring supports all cache modes
1606 $opts .= ",aio=io_uring";
1608 # aio native works only with O_DIRECT
1610 $opts .= ",aio=native";
1612 $opts .= ",aio=threads";
1617 if (!drive_is_cdrom
($drive)) {
1619 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
1620 $detectzeroes = 'off';
1621 } elsif ($drive->{discard
}) {
1622 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
1624 # This used to be our default with discard not being specified:
1625 $detectzeroes = 'on';
1628 # note: 'detect-zeroes' works per blockdev and we want it to persist
1629 # after the alloc-track is removed, so put it on 'file' directly
1630 my $dz_param = $pbs_name ?
"file.detect-zeroes" : "detect-zeroes";
1631 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
1635 $opts .= ",backing=$pbs_name";
1636 $opts .= ",auto-remove=on";
1639 # my $file_param = $pbs_name ? "file.file.filename" : "file";
1640 my $file_param = "file";
1642 # non-rbd drivers require the underlying file to be a seperate block
1643 # node, so add a second .file indirection
1644 $file_param .= ".file" if !$is_rbd;
1645 $file_param .= ".filename";
1647 my $pathinfo = $path ?
"$file_param=$path," : '';
1649 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1652 sub print_pbs_blockdev
{
1653 my ($pbs_conf, $pbs_name) = @_;
1654 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1655 $blockdev .= ",repository=$pbs_conf->{repository}";
1656 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1657 $blockdev .= ",archive=$pbs_conf->{archive}";
1658 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile
};
1662 sub print_netdevice_full
{
1663 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type) = @_;
1665 my $device = $net->{model
};
1666 if ($net->{model
} eq 'virtio') {
1667 $device = 'virtio-net-pci';
1670 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
1671 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
1672 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
1673 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1674 # and out of each queue plus one config interrupt and control vector queue
1675 my $vectors = $net->{queues
} * 2 + 2;
1676 $tmpstr .= ",vectors=$vectors,mq=on";
1678 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
1680 if (my $mtu = $net->{mtu
}) {
1681 if ($net->{model
} eq 'virtio' && $net->{bridge
}) {
1682 my $bridge_mtu = PVE
::Network
::read_bridge_mtu
($net->{bridge
});
1685 } elsif ($mtu < 576) {
1686 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1687 } elsif ($mtu > $bridge_mtu) {
1688 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1690 $tmpstr .= ",host_mtu=$mtu";
1692 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
1696 if ($use_old_bios_files) {
1698 if ($device eq 'virtio-net-pci') {
1699 $romfile = 'pxe-virtio.rom';
1700 } elsif ($device eq 'e1000') {
1701 $romfile = 'pxe-e1000.rom';
1702 } elsif ($device eq 'ne2k') {
1703 $romfile = 'pxe-ne2k_pci.rom';
1704 } elsif ($device eq 'pcnet') {
1705 $romfile = 'pxe-pcnet.rom';
1706 } elsif ($device eq 'rtl8139') {
1707 $romfile = 'pxe-rtl8139.rom';
1709 $tmpstr .= ",romfile=$romfile" if $romfile;
1715 sub print_netdev_full
{
1716 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
1719 if ($netid =~ m/^net(\d+)$/) {
1723 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
1725 my $ifname = "tap${vmid}i$i";
1727 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1728 die "interface name '$ifname' is too long (max 15 character)\n"
1729 if length($ifname) >= 16;
1731 my $vhostparam = '';
1732 if (is_native
($arch)) {
1733 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
1736 my $vmname = $conf->{name
} || "vm$vmid";
1739 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
1741 if ($net->{bridge
}) {
1742 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1743 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
1745 $netdev = "type=user,id=$netid,hostname=$vmname";
1748 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
1754 'cirrus' => 'cirrus-vga',
1756 'vmware' => 'vmware-svga',
1757 'virtio' => 'virtio-vga',
1760 sub print_vga_device
{
1761 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
1763 my $type = $vga_map->{$vga->{type
}};
1764 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
1765 $type = 'virtio-gpu';
1767 my $vgamem_mb = $vga->{memory
};
1769 my $max_outputs = '';
1771 $type = $id ?
'qxl' : 'qxl-vga';
1773 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
1774 # set max outputs so linux can have up to 4 qxl displays with one device
1775 if (min_version
($machine_version, 4, 1)) {
1776 $max_outputs = ",max_outputs=4";
1781 die "no devicetype for $vga->{type}\n" if !$type;
1785 if ($vga->{type
} eq 'virtio') {
1786 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
1787 $memory = ",max_hostmem=$bytes";
1789 # from https://www.spice-space.org/multiple-monitors.html
1790 $memory = ",vgamem_mb=$vga->{memory}";
1791 my $ram = $vgamem_mb * 4;
1792 my $vram = $vgamem_mb * 2;
1793 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1795 $memory = ",vgamem_mb=$vga->{memory}";
1797 } elsif ($qxlnum && $id) {
1798 $memory = ",ram_size=67108864,vram_size=33554432";
1802 if ($type eq 'VGA' && windows_version
($conf->{ostype
})) {
1803 $edidoff=",edid=off" if (!defined($conf->{bios
}) || $conf->{bios
} ne 'ovmf');
1806 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1807 my $vgaid = "vga" . ($id // '');
1810 if ($q35 && $vgaid eq 'vga') {
1811 # the first display uses pcie.0 bus on q35 machines
1812 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
1814 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
1817 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
1820 sub parse_number_sets
{
1823 foreach my $part (split(/;/, $set)) {
1824 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
1825 die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1;
1826 push @$res, [ $1, $2 ];
1828 die "invalid range: $part\n";
1837 my $res = parse_property_string
($numa_fmt, $data);
1838 $res->{cpus
} = parse_number_sets
($res->{cpus
}) if defined($res->{cpus
});
1839 $res->{hostnodes
} = parse_number_sets
($res->{hostnodes
}) if defined($res->{hostnodes
});
1843 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1847 my $res = eval { parse_property_string
($net_fmt, $data) };
1852 if (!defined($res->{macaddr
})) {
1853 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1854 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1859 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1860 sub parse_ipconfig
{
1863 my $res = eval { parse_property_string
($ipconfig_fmt, $data) };
1869 if ($res->{gw
} && !$res->{ip
}) {
1870 warn 'gateway specified without specifying an IP address';
1873 if ($res->{gw6
} && !$res->{ip6
}) {
1874 warn 'IPv6 gateway specified without specifying an IPv6 address';
1877 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
1878 warn 'gateway specified together with DHCP';
1881 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
1883 warn "IPv6 gateway specified together with $res->{ip6} address";
1887 if (!$res->{ip
} && !$res->{ip6
}) {
1888 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
1897 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
1900 sub add_random_macs
{
1901 my ($settings) = @_;
1903 foreach my $opt (keys %$settings) {
1904 next if $opt !~ m/^net(\d+)$/;
1905 my $net = parse_net
($settings->{$opt});
1907 $settings->{$opt} = print_net
($net);
1911 sub vm_is_volid_owner
{
1912 my ($storecfg, $vmid, $volid) = @_;
1914 if ($volid !~ m
|^/|) {
1916 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
1917 if ($owner && ($owner == $vmid)) {
1925 sub vmconfig_register_unused_drive
{
1926 my ($storecfg, $vmid, $conf, $drive) = @_;
1928 if (drive_is_cloudinit
($drive)) {
1929 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
1931 } elsif (!drive_is_cdrom
($drive)) {
1932 my $volid = $drive->{file
};
1933 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
1934 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
1939 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
1943 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
1944 format_description
=> 'UUID',
1945 description
=> "Set SMBIOS1 UUID.",
1950 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1951 format_description
=> 'Base64 encoded string',
1952 description
=> "Set SMBIOS1 version.",
1957 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1958 format_description
=> 'Base64 encoded string',
1959 description
=> "Set SMBIOS1 serial number.",
1964 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1965 format_description
=> 'Base64 encoded string',
1966 description
=> "Set SMBIOS1 manufacturer.",
1971 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1972 format_description
=> 'Base64 encoded string',
1973 description
=> "Set SMBIOS1 product ID.",
1978 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1979 format_description
=> 'Base64 encoded string',
1980 description
=> "Set SMBIOS1 SKU string.",
1985 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1986 format_description
=> 'Base64 encoded string',
1987 description
=> "Set SMBIOS1 family string.",
1992 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
2000 my $res = eval { parse_property_string
($smbios1_fmt, $data) };
2007 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2010 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2012 sub parse_watchdog
{
2017 my $res = eval { parse_property_string
($watchdog_fmt, $value) };
2022 sub parse_guest_agent
{
2025 return {} if !defined($conf->{agent
});
2027 my $res = eval { parse_property_string
($agent_fmt, $conf->{agent
}) };
2030 # if the agent is disabled ignore the other potentially set properties
2031 return {} if !$res->{enabled
};
2036 my ($conf, $key) = @_;
2037 return undef if !defined($conf->{agent
});
2039 my $agent = parse_guest_agent
($conf);
2040 return $agent->{$key};
2046 return {} if !$value;
2047 my $res = eval { parse_property_string
($vga_fmt, $value) };
2057 my $res = eval { parse_property_string
($rng_fmt, $value) };
2062 PVE
::JSONSchema
::register_format
('pve-qm-usb-device', \
&verify_usb_device
);
2063 sub verify_usb_device
{
2064 my ($value, $noerr) = @_;
2066 return $value if parse_usb_device
($value);
2070 die "unable to parse usb device\n";
2073 # add JSON properties for create and set function
2074 sub json_config_properties
{
2077 foreach my $opt (keys %$confdesc) {
2078 next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'vmstate' ||
2079 $opt eq 'runningmachine' || $opt eq 'runningcpu';
2080 $prop->{$opt} = $confdesc->{$opt};
2086 # return copy of $confdesc_cloudinit to generate documentation
2087 sub cloudinit_config_properties
{
2089 return dclone
($confdesc_cloudinit);
2093 my ($key, $value) = @_;
2095 die "unknown setting '$key'\n" if !$confdesc->{$key};
2097 my $type = $confdesc->{$key}->{type
};
2099 if (!defined($value)) {
2100 die "got undefined value\n";
2103 if ($value =~ m/[\n\r]/) {
2104 die "property contains a line feed\n";
2107 if ($type eq 'boolean') {
2108 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2109 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2110 die "type check ('boolean') failed - got '$value'\n";
2111 } elsif ($type eq 'integer') {
2112 return int($1) if $value =~ m/^(\d+)$/;
2113 die "type check ('integer') failed - got '$value'\n";
2114 } elsif ($type eq 'number') {
2115 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2116 die "type check ('number') failed - got '$value'\n";
2117 } elsif ($type eq 'string') {
2118 if (my $fmt = $confdesc->{$key}->{format
}) {
2119 PVE
::JSONSchema
::check_format
($fmt, $value);
2122 $value =~ s/^\"(.*)\"$/$1/;
2125 die "internal error"
2130 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
2132 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2134 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
2136 if ($conf->{template
}) {
2137 # check if any base image is still used by a linked clone
2138 PVE
::QemuConfig-
>foreach_volume_full($conf, { include_unused
=> 1 }, sub {
2139 my ($ds, $drive) = @_;
2140 return if drive_is_cdrom
($drive);
2142 my $volid = $drive->{file
};
2143 return if !$volid || $volid =~ m
|^/|;
2145 die "base volume '$volid' is still in use by linked cloned\n"
2146 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2151 my $remove_owned_drive = sub {
2152 my ($ds, $drive) = @_;
2153 return if drive_is_cdrom
($drive, 1);
2155 my $volid = $drive->{file
};
2156 return if !$volid || $volid =~ m
|^/|;
2158 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2159 return if !$path || !$owner || ($owner != $vmid);
2161 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2162 warn "Could not remove disk '$volid', check manually: $@" if $@;
2165 # only remove disks owned by this VM (referenced in the config)
2166 my $include_opts = {
2167 include_unused
=> 1,
2168 extra_keys
=> ['vmstate'],
2170 PVE
::QemuConfig-
>foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2172 for my $snap (values %{$conf->{snapshots
}}) {
2173 next if !defined($snap->{vmstate
});
2174 my $drive = PVE
::QemuConfig-
>parse_volume('vmstate', $snap->{vmstate
}, 1);
2175 next if !defined($drive);
2176 $remove_owned_drive->('vmstate', $drive);
2179 if ($purge_unreferenced) { # also remove unreferenced disk
2180 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid, undef, 'images');
2181 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2182 my ($volid, $sid, $volname, $d) = @_;
2183 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2188 if (defined $replacement_conf) {
2189 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2191 PVE
::QemuConfig-
>destroy_config($vmid);
2195 sub parse_vm_config
{
2196 my ($filename, $raw) = @_;
2198 return if !defined($raw);
2201 digest
=> Digest
::SHA
::sha1_hex
($raw),
2206 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2207 || die "got strange filename '$filename'";
2215 my @lines = split(/\n/, $raw);
2216 foreach my $line (@lines) {
2217 next if $line =~ m/^\s*$/;
2219 if ($line =~ m/^\[PENDING\]\s*$/i) {
2220 $section = 'pending';
2221 if (defined($descr)) {
2223 $conf->{description
} = $descr;
2226 $conf = $res->{$section} = {};
2229 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2231 if (defined($descr)) {
2233 $conf->{description
} = $descr;
2236 $conf = $res->{snapshots
}->{$section} = {};
2240 if ($line =~ m/^\#(.*)\s*$/) {
2241 $descr = '' if !defined($descr);
2242 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2246 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2247 $descr = '' if !defined($descr);
2248 $descr .= PVE
::Tools
::decode_text
($2);
2249 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2250 $conf->{snapstate
} = $1;
2251 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2254 $conf->{$key} = $value;
2255 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2257 if ($section eq 'pending') {
2258 $conf->{delete} = $value; # we parse this later
2260 warn "vm $vmid - propertry 'delete' is only allowed in [PENDING]\n";
2262 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2265 eval { $value = check_type
($key, $value); };
2267 warn "vm $vmid - unable to parse value of '$key' - $@";
2269 $key = 'ide2' if $key eq 'cdrom';
2270 my $fmt = $confdesc->{$key}->{format
};
2271 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2272 my $v = parse_drive
($key, $value);
2273 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2274 $v->{file
} = $volid;
2275 $value = print_drive
($v);
2277 warn "vm $vmid - unable to parse value of '$key'\n";
2282 $conf->{$key} = $value;
2285 warn "vm $vmid - unable to parse config: $line\n";
2289 if (defined($descr)) {
2291 $conf->{description
} = $descr;
2293 delete $res->{snapstate
}; # just to be sure
2298 sub write_vm_config
{
2299 my ($filename, $conf) = @_;
2301 delete $conf->{snapstate
}; # just to be sure
2303 if ($conf->{cdrom
}) {
2304 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2305 $conf->{ide2
} = $conf->{cdrom
};
2306 delete $conf->{cdrom
};
2309 # we do not use 'smp' any longer
2310 if ($conf->{sockets
}) {
2311 delete $conf->{smp
};
2312 } elsif ($conf->{smp
}) {
2313 $conf->{sockets
} = $conf->{smp
};
2314 delete $conf->{cores
};
2315 delete $conf->{smp
};
2318 my $used_volids = {};
2320 my $cleanup_config = sub {
2321 my ($cref, $pending, $snapname) = @_;
2323 foreach my $key (keys %$cref) {
2324 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2325 $key eq 'snapstate' || $key eq 'pending';
2326 my $value = $cref->{$key};
2327 if ($key eq 'delete') {
2328 die "propertry 'delete' is only allowed in [PENDING]\n"
2330 # fixme: check syntax?
2333 eval { $value = check_type
($key, $value); };
2334 die "unable to parse value of '$key' - $@" if $@;
2336 $cref->{$key} = $value;
2338 if (!$snapname && is_valid_drivename
($key)) {
2339 my $drive = parse_drive
($key, $value);
2340 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2345 &$cleanup_config($conf);
2347 &$cleanup_config($conf->{pending
}, 1);
2349 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2350 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2351 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2354 # remove 'unusedX' settings if we re-add a volume
2355 foreach my $key (keys %$conf) {
2356 my $value = $conf->{$key};
2357 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2358 delete $conf->{$key};
2362 my $generate_raw_config = sub {
2363 my ($conf, $pending) = @_;
2367 # add description as comment to top of file
2368 if (defined(my $descr = $conf->{description
})) {
2370 foreach my $cl (split(/\n/, $descr)) {
2371 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2374 $raw .= "#\n" if $pending;
2378 foreach my $key (sort keys %$conf) {
2379 next if $key =~ /^(digest|description|pending|snapshots)$/;
2380 $raw .= "$key: $conf->{$key}\n";
2385 my $raw = &$generate_raw_config($conf);
2387 if (scalar(keys %{$conf->{pending
}})){
2388 $raw .= "\n[PENDING]\n";
2389 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2392 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2393 $raw .= "\n[$snapname]\n";
2394 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2404 # we use static defaults from our JSON schema configuration
2405 foreach my $key (keys %$confdesc) {
2406 if (defined(my $default = $confdesc->{$key}->{default})) {
2407 $res->{$key} = $default;
2415 my $vmlist = PVE
::Cluster
::get_vmlist
();
2417 return $res if !$vmlist || !$vmlist->{ids
};
2418 my $ids = $vmlist->{ids
};
2419 my $nodename = nodename
();
2421 foreach my $vmid (keys %$ids) {
2422 my $d = $ids->{$vmid};
2423 next if !$d->{node
} || $d->{node
} ne $nodename;
2424 next if !$d->{type
} || $d->{type
} ne 'qemu';
2425 $res->{$vmid}->{exists} = 1;
2430 # test if VM uses local resources (to prevent migration)
2431 sub check_local_resources
{
2432 my ($conf, $noerr) = @_;
2436 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2437 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2439 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2441 foreach my $k (keys %$conf) {
2442 next if $k =~ m/^usb/ && ($conf->{$k} =~ m/^spice(?![^,])/);
2443 # sockets are safe: they will recreated be on the target side post-migrate
2444 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2445 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2448 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2453 # check if used storages are available on all nodes (use by migrate)
2454 sub check_storage_availability
{
2455 my ($storecfg, $conf, $node) = @_;
2457 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2458 my ($ds, $drive) = @_;
2460 my $volid = $drive->{file
};
2463 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2466 # check if storage is available on both nodes
2467 my $scfg = PVE
::Storage
::storage_check_enabled
($storecfg, $sid);
2468 PVE
::Storage
::storage_check_enabled
($storecfg, $sid, $node);
2470 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $volid);
2472 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2473 if !$scfg->{content
}->{$vtype};
2477 # list nodes where all VM images are available (used by has_feature API)
2479 my ($conf, $storecfg) = @_;
2481 my $nodelist = PVE
::Cluster
::get_nodelist
();
2482 my $nodehash = { map { $_ => 1 } @$nodelist };
2483 my $nodename = nodename
();
2485 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2486 my ($ds, $drive) = @_;
2488 my $volid = $drive->{file
};
2491 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2493 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2494 if ($scfg->{disable
}) {
2496 } elsif (my $avail = $scfg->{nodes
}) {
2497 foreach my $node (keys %$nodehash) {
2498 delete $nodehash->{$node} if !$avail->{$node};
2500 } elsif (!$scfg->{shared
}) {
2501 foreach my $node (keys %$nodehash) {
2502 delete $nodehash->{$node} if $node ne $nodename
2511 sub check_local_storage_availability
{
2512 my ($conf, $storecfg) = @_;
2514 my $nodelist = PVE
::Cluster
::get_nodelist
();
2515 my $nodehash = { map { $_ => {} } @$nodelist };
2517 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2518 my ($ds, $drive) = @_;
2520 my $volid = $drive->{file
};
2523 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2525 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2527 if ($scfg->{disable
}) {
2528 foreach my $node (keys %$nodehash) {
2529 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2531 } elsif (my $avail = $scfg->{nodes
}) {
2532 foreach my $node (keys %$nodehash) {
2533 if (!$avail->{$node}) {
2534 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2541 foreach my $node (values %$nodehash) {
2542 if (my $unavail = $node->{unavailable_storages
}) {
2543 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2550 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2552 my ($vmid, $nocheck, $node) = @_;
2554 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2555 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2560 my $vzlist = config_list
();
2562 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2564 while (defined(my $de = $fd->read)) {
2565 next if $de !~ m/^(\d+)\.pid$/;
2567 next if !defined($vzlist->{$vmid});
2568 if (my $pid = check_running
($vmid)) {
2569 $vzlist->{$vmid}->{pid
} = $pid;
2576 our $vmstatus_return_properties = {
2577 vmid
=> get_standard_option
('pve-vmid'),
2579 description
=> "Qemu process status.",
2581 enum
=> ['stopped', 'running'],
2584 description
=> "Maximum memory in bytes.",
2587 renderer
=> 'bytes',
2590 description
=> "Root disk size in bytes.",
2593 renderer
=> 'bytes',
2596 description
=> "VM name.",
2601 description
=> "Qemu QMP agent status.",
2606 description
=> "PID of running qemu process.",
2611 description
=> "Uptime.",
2614 renderer
=> 'duration',
2617 description
=> "Maximum usable CPUs.",
2622 description
=> "The current config lock, if any.",
2627 description
=> "The current configured tags, if any",
2631 'running-machine' => {
2632 description
=> "The currently running machine type (if running).",
2637 description
=> "The currently running QEMU version (if running).",
2643 my $last_proc_pid_stat;
2645 # get VM status information
2646 # This must be fast and should not block ($full == false)
2647 # We only query KVM using QMP if $full == true (this can be slow)
2649 my ($opt_vmid, $full) = @_;
2653 my $storecfg = PVE
::Storage
::config
();
2655 my $list = vzlist
();
2656 my $defaults = load_defaults
();
2658 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
(1);
2660 my $cpucount = $cpuinfo->{cpus
} || 1;
2662 foreach my $vmid (keys %$list) {
2663 next if $opt_vmid && ($vmid ne $opt_vmid);
2665 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2667 my $d = { vmid
=> int($vmid) };
2668 $d->{pid
} = int($list->{$vmid}->{pid
}) if $list->{$vmid}->{pid
};
2670 # fixme: better status?
2671 $d->{status
} = $list->{$vmid}->{pid
} ?
'running' : 'stopped';
2673 my $size = PVE
::QemuServer
::Drive
::bootdisk_size
($storecfg, $conf);
2674 if (defined($size)) {
2675 $d->{disk
} = 0; # no info available
2676 $d->{maxdisk
} = $size;
2682 $d->{cpus
} = ($conf->{sockets
} || $defaults->{sockets
})
2683 * ($conf->{cores
} || $defaults->{cores
});
2684 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
2685 $d->{cpus
} = $conf->{vcpus
} if $conf->{vcpus
};
2687 $d->{name
} = $conf->{name
} || "VM $vmid";
2688 $d->{maxmem
} = $conf->{memory
} ?
$conf->{memory
}*(1024*1024)
2689 : $defaults->{memory
}*(1024*1024);
2691 if ($conf->{balloon
}) {
2692 $d->{balloon_min
} = $conf->{balloon
}*(1024*1024);
2693 $d->{shares
} = defined($conf->{shares
}) ?
$conf->{shares
}
2694 : $defaults->{shares
};
2705 $d->{diskwrite
} = 0;
2707 $d->{template
} = 1 if PVE
::QemuConfig-
>is_template($conf);
2709 $d->{serial
} = 1 if conf_has_serial
($conf);
2710 $d->{lock} = $conf->{lock} if $conf->{lock};
2711 $d->{tags
} = $conf->{tags
} if defined($conf->{tags
});
2716 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
2717 foreach my $dev (keys %$netdev) {
2718 next if $dev !~ m/^tap([1-9]\d*)i/;
2720 my $d = $res->{$vmid};
2723 $d->{netout
} += $netdev->{$dev}->{receive
};
2724 $d->{netin
} += $netdev->{$dev}->{transmit
};
2727 $d->{nics
}->{$dev}->{netout
} = int($netdev->{$dev}->{receive
});
2728 $d->{nics
}->{$dev}->{netin
} = int($netdev->{$dev}->{transmit
});
2733 my $ctime = gettimeofday
;
2735 foreach my $vmid (keys %$list) {
2737 my $d = $res->{$vmid};
2738 my $pid = $d->{pid
};
2741 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
2742 next if !$pstat; # not running
2744 my $used = $pstat->{utime} + $pstat->{stime
};
2746 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
2748 if ($pstat->{vsize
}) {
2749 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
2752 my $old = $last_proc_pid_stat->{$pid};
2754 $last_proc_pid_stat->{$pid} = {
2762 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
2764 if ($dtime > 1000) {
2765 my $dutime = $used - $old->{used
};
2767 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
2768 $last_proc_pid_stat->{$pid} = {
2774 $d->{cpu
} = $old->{cpu
};
2778 return $res if !$full;
2780 my $qmpclient = PVE
::QMPClient-
>new();
2782 my $ballooncb = sub {
2783 my ($vmid, $resp) = @_;
2785 my $info = $resp->{'return'};
2786 return if !$info->{max_mem
};
2788 my $d = $res->{$vmid};
2790 # use memory assigned to VM
2791 $d->{maxmem
} = $info->{max_mem
};
2792 $d->{balloon
} = $info->{actual
};
2794 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
2795 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
2796 $d->{freemem
} = $info->{free_mem
};
2799 $d->{ballooninfo
} = $info;
2802 my $blockstatscb = sub {
2803 my ($vmid, $resp) = @_;
2804 my $data = $resp->{'return'} || [];
2805 my $totalrdbytes = 0;
2806 my $totalwrbytes = 0;
2808 for my $blockstat (@$data) {
2809 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
2810 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
2812 $blockstat->{device
} =~ s/drive-//;
2813 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
2815 $res->{$vmid}->{diskread
} = $totalrdbytes;
2816 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
2819 my $machinecb = sub {
2820 my ($vmid, $resp) = @_;
2821 my $data = $resp->{'return'} || [];
2823 $res->{$vmid}->{'running-machine'} =
2824 PVE
::QemuServer
::Machine
::current_from_query_machines
($data);
2827 my $versioncb = sub {
2828 my ($vmid, $resp) = @_;
2829 my $data = $resp->{'return'} // {};
2830 my $version = 'unknown';
2832 if (my $v = $data->{qemu
}) {
2833 $version = $v->{major
} . "." . $v->{minor
} . "." . $v->{micro
};
2836 $res->{$vmid}->{'running-qemu'} = $version;
2839 my $statuscb = sub {
2840 my ($vmid, $resp) = @_;
2842 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
2843 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
2844 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
2845 # this fails if ballon driver is not loaded, so this must be
2846 # the last commnand (following command are aborted if this fails).
2847 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
2849 my $status = 'unknown';
2850 if (!defined($status = $resp->{'return'}->{status
})) {
2851 warn "unable to get VM status\n";
2855 $res->{$vmid}->{qmpstatus
} = $resp->{'return'}->{status
};
2858 foreach my $vmid (keys %$list) {
2859 next if $opt_vmid && ($vmid ne $opt_vmid);
2860 next if !$res->{$vmid}->{pid
}; # not running
2861 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
2864 $qmpclient->queue_execute(undef, 2);
2866 foreach my $vmid (keys %$list) {
2867 next if $opt_vmid && ($vmid ne $opt_vmid);
2868 next if !$res->{$vmid}->{pid
}; #not running
2870 # we can't use the $qmpclient since it might have already aborted on
2871 # 'query-balloon', but this might also fail for older versions...
2872 my $qemu_support = eval { mon_cmd
($vmid, "query-proxmox-support") };
2873 $res->{$vmid}->{'proxmox-support'} = $qemu_support // {};
2876 foreach my $vmid (keys %$list) {
2877 next if $opt_vmid && ($vmid ne $opt_vmid);
2878 $res->{$vmid}->{qmpstatus
} = $res->{$vmid}->{status
} if !$res->{$vmid}->{qmpstatus
};
2884 sub conf_has_serial
{
2887 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
2888 if ($conf->{"serial$i"}) {
2896 sub conf_has_audio
{
2897 my ($conf, $id) = @_;
2900 my $audio = $conf->{"audio$id"};
2901 return if !defined($audio);
2903 my $audioproperties = parse_property_string
($audio_fmt, $audio);
2904 my $audiodriver = $audioproperties->{driver
} // 'spice';
2907 dev
=> $audioproperties->{device
},
2908 dev_id
=> "audiodev$id",
2909 backend
=> $audiodriver,
2910 backend_id
=> "$audiodriver-backend${id}",
2915 my ($audio, $audiopciaddr, $machine_version) = @_;
2919 my $id = $audio->{dev_id
};
2921 if (min_version
($machine_version, 4, 2)) {
2922 $audiodev = ",audiodev=$audio->{backend_id}";
2925 if ($audio->{dev
} eq 'AC97') {
2926 push @$devs, '-device', "AC97,id=${id}${audiopciaddr}$audiodev";
2927 } elsif ($audio->{dev
} =~ /intel\-hda$/) {
2928 push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
2929 push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0$audiodev";
2930 push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1$audiodev";
2932 die "unkown audio device '$audio->{dev}', implement me!";
2935 push @$devs, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
2940 sub vga_conf_has_spice
{
2943 my $vgaconf = parse_vga
($vga);
2944 my $vgatype = $vgaconf->{type
};
2945 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
2952 return get_host_arch
() eq $arch;
2957 return $conf->{arch
} // get_host_arch
();
2960 my $default_machines = {
2965 sub get_installed_machine_version
{
2966 my ($kvmversion) = @_;
2967 $kvmversion = kvm_user_version
() if !defined($kvmversion);
2968 $kvmversion =~ m/^(\d+\.\d+)/;
2972 sub windows_get_pinned_machine_version
{
2973 my ($machine, $base_version, $kvmversion) = @_;
2975 my $pin_version = $base_version;
2976 if (!defined($base_version) ||
2977 !PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($base_version, $kvmversion)
2979 $pin_version = get_installed_machine_version
($kvmversion);
2981 if (!$machine || $machine eq 'pc') {
2982 $machine = "pc-i440fx-$pin_version";
2983 } elsif ($machine eq 'q35') {
2984 $machine = "pc-q35-$pin_version";
2985 } elsif ($machine eq 'virt') {
2986 $machine = "virt-$pin_version";
2988 warn "unknown machine type '$machine', not touching that!\n";
2994 sub get_vm_machine
{
2995 my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
2997 my $machine = $forcemachine || $conf->{machine
};
2999 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
3000 $kvmversion //= kvm_user_version
();
3001 # we must pin Windows VMs without a specific version to 5.1, as 5.2 fixed a bug in ACPI
3002 # layout which confuses windows quite a bit and may result in various regressions..
3003 # see: https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg08484.html
3004 if (windows_version
($conf->{ostype
})) {
3005 $machine = windows_get_pinned_machine_version
($machine, '5.1', $kvmversion);
3008 $machine ||= $default_machines->{$arch};
3009 if ($add_pve_version) {
3010 my $pvever = PVE
::QemuServer
::Machine
::get_pve_version
($kvmversion);
3011 $machine .= "+pve$pvever";
3015 if ($add_pve_version && $machine !~ m/\+pve\d+?(?:\.pxe)?$/) {
3016 my $is_pxe = $machine =~ m/^(.*?)\.pxe$/;
3017 $machine = $1 if $is_pxe;
3019 # for version-pinned machines that do not include a pve-version (e.g.
3020 # pc-q35-4.1), we assume 0 to keep them stable in case we bump
3021 $machine .= '+pve0';
3023 $machine .= '.pxe' if $is_pxe;
3029 sub get_ovmf_files
($) {
3032 my $ovmf = $OVMF->{$arch}
3033 or die "no OVMF images known for architecture '$arch'\n";
3039 aarch64
=> '/usr/bin/qemu-system-aarch64',
3040 x86_64
=> '/usr/bin/qemu-system-x86_64',
3042 sub get_command_for_arch
($) {
3044 return '/usr/bin/kvm' if is_native
($arch);
3046 my $cmd = $Arch2Qemu->{$arch}
3047 or die "don't know how to emulate architecture '$arch'\n";
3051 # To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
3052 # to use in a QEMU command line (-cpu element), first array_intersect the result
3053 # of query_supported_ with query_understood_. This is necessary because:
3055 # a) query_understood_ returns flags the host cannot use and
3056 # b) query_supported_ (rather the QMP call) doesn't actually return CPU
3057 # flags, but CPU settings - with most of them being flags. Those settings
3058 # (and some flags, curiously) cannot be specified as a "-cpu" argument.
3060 # query_supported_ needs to start up to 2 temporary VMs and is therefore rather
3061 # expensive. If you need the value returned from this, you can get it much
3062 # cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags-$accel') with
3063 # $accel being 'kvm' or 'tcg'.
3065 # pvestatd calls this function on startup and whenever the QEMU/KVM version
3066 # changes, automatically populating pmxcfs.
3068 # Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
3069 # since kvm and tcg machines support different flags
3071 sub query_supported_cpu_flags
{
3074 $arch //= get_host_arch
();
3075 my $default_machine = $default_machines->{$arch};
3079 # FIXME: Once this is merged, the code below should work for ARM as well:
3080 # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
3081 die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
3084 my $kvm_supported = defined(kvm_version
());
3085 my $qemu_cmd = get_command_for_arch
($arch);
3087 my $pidfile = PVE
::QemuServer
::Helpers
::pidfile_name
($fakevmid);
3089 # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
3090 my $query_supported_run_qemu = sub {
3096 '-machine', $default_machine,
3098 '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
3099 '-mon', 'chardev=qmp,mode=control',
3100 '-pidfile', $pidfile,
3105 push @$cmd, '-accel', 'tcg';
3108 my $rc = run_command
($cmd, noerr
=> 1, quiet
=> 0);
3109 die "QEMU flag querying VM exited with code " . $rc if $rc;
3112 my $cmd_result = mon_cmd
(
3114 'query-cpu-model-expansion',
3116 model
=> { name
=> 'host' }
3119 my $props = $cmd_result->{model
}->{props
};
3120 foreach my $prop (keys %$props) {
3121 next if $props->{$prop} ne '1';
3122 # QEMU returns some flags multiple times, with '_', '.' or '-'
3123 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
3124 # We only keep those with underscores, to match /proc/cpuinfo
3125 $prop =~ s/\.|-/_/g;
3126 $flags->{$prop} = 1;
3131 # force stop with 10 sec timeout and 'nocheck'
3132 # always stop, even if QMP failed
3133 vm_stop
(undef, $fakevmid, 1, 1, 10, 0, 1);
3137 return [ sort keys %$flags ];
3140 # We need to query QEMU twice, since KVM and TCG have different supported flags
3141 PVE
::QemuConfig-
>lock_config($fakevmid, sub {
3142 $flags->{tcg
} = eval { $query_supported_run_qemu->(0) };
3143 warn "warning: failed querying supported tcg flags: $@\n" if $@;
3145 if ($kvm_supported) {
3146 $flags->{kvm
} = eval { $query_supported_run_qemu->(1) };
3147 warn "warning: failed querying supported kvm flags: $@\n" if $@;
3154 # Understood CPU flags are written to a file at 'pve-qemu' compile time
3155 my $understood_cpu_flag_dir = "/usr/share/kvm";
3156 sub query_understood_cpu_flags
{
3157 my $arch = get_host_arch
();
3158 my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
3160 die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
3163 my $raw = file_get_contents
($filepath);
3164 $raw =~ s/^\s+|\s+$//g;
3165 my @flags = split(/\s+/, $raw);
3170 sub config_to_command
{
3171 my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
3175 my $globalFlags = [];
3176 my $machineFlags = [];
3181 my $ostype = $conf->{ostype
};
3182 my $winversion = windows_version
($ostype);
3183 my $kvm = $conf->{kvm
};
3184 my $nodename = nodename
();
3186 my $arch = get_vm_arch
($conf);
3187 my $kvm_binary = get_command_for_arch
($arch);
3188 my $kvmver = kvm_user_version
($kvm_binary);
3190 if (!$kvmver || $kvmver !~ m/^(\d+)\.(\d+)/ || $1 < 3) {
3191 $kvmver //= "undefined";
3192 die "Detected old QEMU binary ('$kvmver', at least 3.0 is required)\n";
3195 my $add_pve_version = min_version
($kvmver, 4, 1);
3197 my $machine_type = get_vm_machine
($conf, $forcemachine, $arch, $add_pve_version);
3198 my $machine_version = extract_version
($machine_type, $kvmver);
3199 $kvm //= 1 if is_native
($arch);
3201 $machine_version =~ m/(\d+)\.(\d+)/;
3202 my ($machine_major, $machine_minor) = ($1, $2);
3204 if ($kvmver =~ m/^\d+\.\d+\.(\d+)/ && $1 >= 90) {
3205 warn "warning: Installed QEMU version ($kvmver) is a release candidate, ignoring version checks\n";
3206 } elsif (!min_version
($kvmver, $machine_major, $machine_minor)) {
3207 die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type',"
3208 ." please upgrade node '$nodename'\n"
3209 } elsif (!PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($machine_version, $kvmver)) {
3210 my $max_pve_version = PVE
::QemuServer
::Machine
::get_pve_version
($machine_version);
3211 die "Installed qemu-server (max feature level for $machine_major.$machine_minor is"
3212 ." pve$max_pve_version) is too old to run machine type '$machine_type', please upgrade"
3213 ." node '$nodename'\n";
3216 # if a specific +pve version is required for a feature, use $version_guard
3217 # instead of min_version to allow machines to be run with the minimum
3219 my $required_pve_version = 0;
3220 my $version_guard = sub {
3221 my ($major, $minor, $pve) = @_;
3222 return 0 if !min_version
($machine_version, $major, $minor, $pve);
3223 my $max_pve = PVE
::QemuServer
::Machine
::get_pve_version
("$major.$minor");
3224 return 1 if min_version
($machine_version, $major, $minor, $max_pve+1);
3225 $required_pve_version = $pve if $pve && $pve > $required_pve_version;
3229 if ($kvm && !defined kvm_version
()) {
3230 die "KVM virtualisation configured, but not available. Either disable in VM configuration"
3231 ." or enable in BIOS.\n";
3234 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3235 my $hotplug_features = parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
3236 my $use_old_bios_files = undef;
3237 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3239 my $cpuunits = defined($conf->{cpuunits
}) ?
3240 $conf->{cpuunits
} : $defaults->{cpuunits
};
3242 push @$cmd, $kvm_binary;
3244 push @$cmd, '-id', $vmid;
3246 my $vmname = $conf->{name
} || "vm$vmid";
3248 push @$cmd, '-name', $vmname;
3250 push @$cmd, '-no-shutdown';
3254 my $qmpsocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid);
3255 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server=on,wait=off";
3256 push @$cmd, '-mon', "chardev=qmp,mode=control";
3258 if (min_version
($machine_version, 2, 12)) {
3259 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3260 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3263 push @$cmd, '-pidfile' , PVE
::QemuServer
::Helpers
::pidfile_name
($vmid);
3265 push @$cmd, '-daemonize';
3267 if ($conf->{smbios1
}) {
3268 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
3269 if ($smbios_conf->{base64
}) {
3270 # Do not pass base64 flag to qemu
3271 delete $smbios_conf->{base64
};
3272 my $smbios_string = "";
3273 foreach my $key (keys %$smbios_conf) {
3275 if ($key eq "uuid") {
3276 $value = $smbios_conf->{uuid
}
3278 $value = decode_base64
($smbios_conf->{$key});
3280 # qemu accepts any binary data, only commas need escaping by double comma
3282 $smbios_string .= "," . $key . "=" . $value if $value;
3284 push @$cmd, '-smbios', "type=1" . $smbios_string;
3286 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3290 if ($conf->{bios
} && $conf->{bios
} eq 'ovmf') {
3291 my ($ovmf_code, $ovmf_vars) = get_ovmf_files
($arch);
3292 die "uefi base image '$ovmf_code' not found\n" if ! -f
$ovmf_code;
3294 my ($path, $format);
3295 my $read_only_str = '';
3296 if (my $efidisk = $conf->{efidisk0
}) {
3297 my $d = parse_drive
('efidisk0', $efidisk);
3298 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($d->{file
}, 1);
3299 $format = $d->{format
};
3301 $path = PVE
::Storage
::path
($storecfg, $d->{file
});
3302 if (!defined($format)) {
3303 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
3304 $format = qemu_img_format
($scfg, $volname);
3308 die "efidisk format must be specified\n"
3309 if !defined($format);
3312 $read_only_str = ',readonly=on' if drive_is_read_only
($conf, $d);
3314 warn "no efidisk configured! Using temporary efivars disk.\n";
3315 $path = "/tmp/$vmid-ovmf.fd";
3316 PVE
::Tools
::file_copy
($ovmf_vars, $path, -s
$ovmf_vars);
3322 if ($format eq 'raw' && $version_guard->(4, 1, 2)) {
3323 $size_str = ",size=" . (-s
$ovmf_vars);
3326 # on slower ceph clusters, booting without cache on efidisk can take a while, see #3329
3327 my $cache = $path =~ m/^rbd:/ ?
',cache=writeback' : '';
3329 push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code";
3330 push @$cmd, '-drive', "if=pflash,unit=1$cache,format=$format,id=drive-efidisk0$size_str,file=${path}${read_only_str}";
3335 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3336 if (min_version
($machine_version, 4, 0)) {
3337 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3339 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3343 if ($conf->{vmgenid
}) {
3344 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid
};
3347 # add usb controllers
3348 my @usbcontrollers = PVE
::QemuServer
::USB
::get_usb_controllers
(
3349 $conf, $bridges, $arch, $machine_type, $usbdesc->{format
}, $MAX_USB_DEVICES);
3350 push @$devices, @usbcontrollers if @usbcontrollers;
3351 my $vga = parse_vga
($conf->{vga
});
3353 my $qxlnum = vga_conf_has_spice
($conf->{vga
});
3354 $vga->{type
} = 'qxl' if $qxlnum;
3356 if (!$vga->{type
}) {
3357 if ($arch eq 'aarch64') {
3358 $vga->{type
} = 'virtio';
3359 } elsif (min_version
($machine_version, 2, 9)) {
3360 $vga->{type
} = (!$winversion || $winversion >= 6) ?
'std' : 'cirrus';
3362 $vga->{type
} = ($winversion >= 6) ?
'std' : 'cirrus';
3366 # enable absolute mouse coordinates (needed by vnc)
3368 if (defined($conf->{tablet
})) {
3369 $tablet = $conf->{tablet
};
3371 $tablet = $defaults->{tablet
};
3372 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3373 $tablet = 0 if $vga->{type
} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3377 push @$devices, '-device', print_tabletdevice_full
($conf, $arch) if $tablet;
3378 my $kbd = print_keyboarddevice_full
($conf, $arch);
3379 push @$devices, '-device', $kbd if defined($kbd);
3382 my $bootorder = device_bootorder
($conf);
3384 # host pci device passthrough
3385 my ($kvm_off, $gpu_passthrough, $legacy_igd) = PVE
::QemuServer
::PCI
::print_hostpci_devices
(
3386 $vmid, $conf, $devices, $vga, $winversion, $q35, $bridges, $arch, $machine_type, $bootorder);
3389 my $usb_dev_features = {};
3390 $usb_dev_features->{spice_usb3
} = 1 if min_version
($machine_version, 4, 0);
3392 my @usbdevices = PVE
::QemuServer
::USB
::get_usb_devices
(
3393 $conf, $usbdesc->{format
}, $MAX_USB_DEVICES, $usb_dev_features, $bootorder);
3394 push @$devices, @usbdevices if @usbdevices;
3397 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3398 if (my $path = $conf->{"serial$i"}) {
3399 if ($path eq 'socket') {
3400 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3401 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
3402 # On aarch64, serial0 is the UART device. Qemu only allows
3403 # connecting UART devices via the '-serial' command line, as
3404 # the device has a fixed slot on the hardware...
3405 if ($arch eq 'aarch64' && $i == 0) {
3406 push @$devices, '-serial', "chardev:serial$i";
3408 push @$devices, '-device', "isa-serial,chardev=serial$i";
3411 die "no such serial device\n" if ! -c
$path;
3412 push @$devices, '-chardev', "tty,id=serial$i,path=$path";
3413 push @$devices, '-device', "isa-serial,chardev=serial$i";
3419 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3420 if (my $path = $conf->{"parallel$i"}) {
3421 die "no such parallel device\n" if ! -c
$path;
3422 my $devtype = $path =~ m!^/dev/usb/lp! ?
'tty' : 'parport';
3423 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3424 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3428 if (min_version
($machine_version, 4, 0) && (my $audio = conf_has_audio
($conf))) {
3429 my $audiopciaddr = print_pci_addr
("audio0", $bridges, $arch, $machine_type);
3430 my $audio_devs = audio_devs
($audio, $audiopciaddr, $machine_version);
3431 push @$devices, @$audio_devs;
3435 $sockets = $conf->{smp
} if $conf->{smp
}; # old style - no longer iused
3436 $sockets = $conf->{sockets
} if $conf->{sockets
};
3438 my $cores = $conf->{cores
} || 1;
3440 my $maxcpus = $sockets * $cores;
3442 my $vcpus = $conf->{vcpus
} ?
$conf->{vcpus
} : $maxcpus;
3444 my $allowed_vcpus = $cpuinfo->{cpus
};
3446 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n"
3447 if ($allowed_vcpus < $maxcpus);
3449 if($hotplug_features->{cpu
} && min_version
($machine_version, 2, 7)) {
3451 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3452 for (my $i = 2; $i <= $vcpus; $i++) {
3453 my $cpustr = print_cpu_device
($conf,$i);
3454 push @$cmd, '-device', $cpustr;
3459 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3461 push @$cmd, '-nodefaults';
3463 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3465 push @$cmd, '-no-acpi' if defined($conf->{acpi
}) && $conf->{acpi
} == 0;
3467 push @$cmd, '-no-reboot' if defined($conf->{reboot
}) && $conf->{reboot
} == 0;
3469 if ($vga->{type
} && $vga->{type
} !~ m/^serial\d+$/ && $vga->{type
} ne 'none'){
3470 push @$devices, '-device', print_vga_device
(
3471 $conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
3472 my $socket = PVE
::QemuServer
::Helpers
::vnc_socket
($vmid);
3473 push @$cmd, '-vnc', "unix:$socket,password=on";
3475 push @$cmd, '-vga', 'none' if $vga->{type
} eq 'none';
3476 push @$cmd, '-nographic';
3480 my $tdf = defined($conf->{tdf
}) ?
$conf->{tdf
} : $defaults->{tdf
};
3481 my $useLocaltime = $conf->{localtime};
3483 if ($winversion >= 5) { # windows
3484 $useLocaltime = 1 if !defined($conf->{localtime});
3486 # use time drift fix when acpi is enabled
3487 if (!(defined($conf->{acpi
}) && $conf->{acpi
} == 0)) {
3488 $tdf = 1 if !defined($conf->{tdf
});
3492 if ($winversion >= 6) {
3493 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3494 push @$cmd, '-no-hpet';
3497 push @$rtcFlags, 'driftfix=slew' if $tdf;
3499 if ($conf->{startdate
} && $conf->{startdate
} ne 'now') {
3500 push @$rtcFlags, "base=$conf->{startdate}";
3501 } elsif ($useLocaltime) {
3502 push @$rtcFlags, 'base=localtime';
3506 push @$cmd, '-cpu', $forcecpu;
3508 push @$cmd, get_cpu_options
($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
3511 PVE
::QemuServer
::Memory
::config
($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
3513 push @$cmd, '-S' if $conf->{freeze
};
3515 push @$cmd, '-k', $conf->{keyboard
} if defined($conf->{keyboard
});
3517 my $guest_agent = parse_guest_agent
($conf);
3519 if ($guest_agent->{enabled
}) {
3520 my $qgasocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid, 1);
3521 push @$devices, '-chardev', "socket,path=$qgasocket,server=on,wait=off,id=qga0";
3523 if (!$guest_agent->{type
} || $guest_agent->{type
} eq 'virtio') {
3524 my $pciaddr = print_pci_addr
("qga0", $bridges, $arch, $machine_type);
3525 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3526 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3527 } elsif ($guest_agent->{type
} eq 'isa') {
3528 push @$devices, '-device', "isa-serial,chardev=qga0";
3532 my $rng = $conf->{rng0
} ? parse_rng
($conf->{rng0
}) : undef;
3533 if ($rng && $version_guard->(4, 1, 2)) {
3534 check_rng_source
($rng->{source
});
3536 my $max_bytes = $rng->{max_bytes
} // $rng_fmt->{max_bytes
}->{default};
3537 my $period = $rng->{period
} // $rng_fmt->{period
}->{default};
3538 my $limiter_str = "";
3540 $limiter_str = ",max-bytes=$max_bytes,period=$period";
3543 my $rng_addr = print_pci_addr
("rng0", $bridges, $arch, $machine_type);
3544 push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
3545 push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
3553 for (my $i = 1; $i < $qxlnum; $i++){
3554 push @$devices, '-device', print_vga_device
(
3555 $conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
3558 # assume other OS works like Linux
3559 my ($ram, $vram) = ("134217728", "67108864");
3560 if ($vga->{memory
}) {
3561 $ram = PVE
::Tools
::convert_size
($qxlnum*4*$vga->{memory
}, 'mb' => 'b');
3562 $vram = PVE
::Tools
::convert_size
($qxlnum*2*$vga->{memory
}, 'mb' => 'b');
3564 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3565 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3569 my $pciaddr = print_pci_addr
("spice", $bridges, $arch, $machine_type);
3571 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
3572 my @nodeaddrs = PVE
::Tools
::getaddrinfo_all
('localhost', family
=> $pfamily);
3573 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3575 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3576 push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
3577 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3579 my $localhost = PVE
::Network
::addr_to_ip
($nodeaddrs[0]->{addr
});
3580 $spice_port = PVE
::Tools
::next_spice_port
($pfamily, $localhost);
3582 my $spice_enhancement_str = $conf->{spice_enhancements
} // '';
3583 my $spice_enhancement = parse_property_string
($spice_enhancements_fmt, $spice_enhancement_str);
3584 if ($spice_enhancement->{foldersharing
}) {
3585 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3586 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3589 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3590 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}"
3591 if $spice_enhancement->{videostreaming
};
3593 push @$devices, '-spice', "$spice_opts";
3596 # enable balloon by default, unless explicitly disabled
3597 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
3598 $pciaddr = print_pci_addr
("balloon0", $bridges, $arch, $machine_type);
3599 push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr";
3602 if ($conf->{watchdog
}) {
3603 my $wdopts = parse_watchdog
($conf->{watchdog
});
3604 $pciaddr = print_pci_addr
("watchdog", $bridges, $arch, $machine_type);
3605 my $watchdog = $wdopts->{model
} || 'i6300esb';
3606 push @$devices, '-device', "$watchdog$pciaddr";
3607 push @$devices, '-watchdog-action', $wdopts->{action
} if $wdopts->{action
};
3611 my $scsicontroller = {};
3612 my $ahcicontroller = {};
3613 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : $defaults->{scsihw
};
3615 # Add iscsi initiator name if available
3616 if (my $initiator = get_initiator_name
()) {
3617 push @$devices, '-iscsi', "initiator-name=$initiator";
3620 PVE
::QemuConfig-
>foreach_volume($conf, sub {
3621 my ($ds, $drive) = @_;
3623 if (PVE
::Storage
::parse_volume_id
($drive->{file
}, 1)) {
3624 check_volume_storage_type
($storecfg, $drive->{file
});
3625 push @$vollist, $drive->{file
};
3628 # ignore efidisk here, already added in bios/fw handling code above
3629 return if $drive->{interface
} eq 'efidisk';
3631 $use_virtio = 1 if $ds =~ m/^virtio/;
3633 $drive->{bootindex
} = $bootorder->{$ds} if $bootorder->{$ds};
3635 if ($drive->{interface
} eq 'virtio'){
3636 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread
};
3639 if ($drive->{interface
} eq 'scsi') {
3641 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
3643 die "scsi$drive->{index}: machine version 4.1~pve2 or higher is required to use more than 14 SCSI disks\n"
3644 if $drive->{index} > 13 && !&$version_guard(4, 1, 2);
3646 $pciaddr = print_pci_addr
("$controller_prefix$controller", $bridges, $arch, $machine_type);
3647 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ?
"virtio-scsi-pci" : $scsihw;
3650 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{iothread
}){
3651 $iothread .= ",iothread=iothread-$controller_prefix$controller";
3652 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
3653 } elsif ($drive->{iothread
}) {
3654 warn "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n";
3658 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{queues
}){
3659 $queues = ",num_queues=$drive->{queues}";
3662 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues"
3663 if !$scsicontroller->{$controller};
3664 $scsicontroller->{$controller}=1;
3667 if ($drive->{interface
} eq 'sata') {
3668 my $controller = int($drive->{index} / $PVE::QemuServer
::Drive
::MAX_SATA_DISKS
);
3669 $pciaddr = print_pci_addr
("ahci$controller", $bridges, $arch, $machine_type);
3670 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr"
3671 if !$ahcicontroller->{$controller};
3672 $ahcicontroller->{$controller}=1;
3675 my $pbs_conf = $pbs_backing->{$ds};
3676 my $pbs_name = undef;
3678 $pbs_name = "drive-$ds-pbs";
3679 push @$devices, '-blockdev', print_pbs_blockdev
($pbs_conf, $pbs_name);
3682 my $drive_cmd = print_drive_commandline_full
(
3683 $storecfg, $vmid, $drive, $pbs_name, min_version
($kvmver, 6, 0));
3685 # extra protection for templates, but SATA and IDE don't support it..
3686 $drive_cmd .= ',readonly=on' if drive_is_read_only
($conf, $drive);
3688 push @$devices, '-drive',$drive_cmd;
3689 push @$devices, '-device', print_drivedevice_full
(
3690 $storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
3693 for (my $i = 0; $i < $MAX_NETS; $i++) {
3694 my $netname = "net$i";
3696 next if !$conf->{$netname};
3697 my $d = parse_net
($conf->{$netname});
3700 $use_virtio = 1 if $d->{model
} eq 'virtio';
3702 $d->{bootindex
} = $bootorder->{$netname} if $bootorder->{$netname};
3704 my $netdevfull = print_netdev_full
($vmid, $conf, $arch, $d, $netname);
3705 push @$devices, '-netdev', $netdevfull;
3707 my $netdevicefull = print_netdevice_full
(
3708 $vmid, $conf, $d, $netname, $bridges, $use_old_bios_files, $arch, $machine_type);
3710 push @$devices, '-device', $netdevicefull;
3713 if ($conf->{ivshmem
}) {
3714 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
3718 $bus = print_pcie_addr
("ivshmem");
3720 $bus = print_pci_addr
("ivshmem", $bridges, $arch, $machine_type);
3723 my $ivshmem_name = $ivshmem->{name
} // $vmid;
3724 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
3726 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
3727 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path"
3728 .",size=$ivshmem->{size}M";
3731 # pci.4 is nested in pci.1
3732 $bridges->{1} = 1 if $bridges->{4};
3736 if (min_version
($machine_version, 2, 3)) {
3741 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
3745 for my $k (sort {$b cmp $a} keys %$bridges) {
3746 next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
3749 if ($k == 2 && $legacy_igd) {
3752 $pciaddr = print_pci_addr
("pci.$k_name", undef, $arch, $machine_type);
3754 my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
3756 # add after -readconfig pve-q35.cfg
3757 splice @$devices, 2, 0, '-device', $devstr;
3759 unshift @$devices, '-device', $devstr if $k > 0;
3764 push @$machineFlags, 'accel=tcg';
3767 my $machine_type_min = $machine_type;
3768 if ($add_pve_version) {
3769 $machine_type_min =~ s/\+pve\d+$//;
3770 $machine_type_min .= "+pve$required_pve_version";
3772 push @$machineFlags, "type=${machine_type_min}";
3774 push @$cmd, @$devices;
3775 push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
3776 push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
3777 push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags);
3779 if (my $vmstate = $conf->{vmstate
}) {
3780 my $statepath = PVE
::Storage
::path
($storecfg, $vmstate);
3781 push @$vollist, $vmstate;
3782 push @$cmd, '-loadstate', $statepath;
3783 print "activating and using '$vmstate' as vmstate\n";
3786 if (PVE
::QemuConfig-
>is_template($conf)) {
3787 # needed to workaround base volumes being read-only
3788 push @$cmd, '-snapshot';
3792 if ($conf->{args
}) {
3793 my $aa = PVE
::Tools
::split_args
($conf->{args
});
3797 return wantarray ?
($cmd, $vollist, $spice_port) : $cmd;
3800 sub check_rng_source
{
3803 # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
3804 die "cannot create VirtIO RNG device: source file '$source' doesn't exist\n"
3807 my $rng_current = '/sys/devices/virtual/misc/hw_random/rng_current';
3808 if ($source eq '/dev/hwrng' && file_read_firstline
($rng_current) eq 'none') {
3809 # Needs to abort, otherwise QEMU crashes on first rng access. Note that rng_current cannot
3810 # be changed to 'none' manually, so once the VM is past this point, it's no longer an issue.
3811 die "Cannot start VM with passed-through RNG device: '/dev/hwrng' exists, but"
3812 ." '$rng_current' is set to 'none'. Ensure that a compatible hardware-RNG is attached"
3820 my $res = mon_cmd
($vmid, 'query-spice');
3822 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
3825 sub vm_devices_list
{
3828 my $res = mon_cmd
($vmid, 'query-pci');
3829 my $devices_to_check = [];
3831 foreach my $pcibus (@$res) {
3832 push @$devices_to_check, @{$pcibus->{devices
}},
3835 while (@$devices_to_check) {
3837 for my $d (@$devices_to_check) {
3838 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
3839 next if !$d->{'pci_bridge'};
3841 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices
}});
3842 push @$to_check, @{$d->{'pci_bridge'}->{devices
}};
3844 $devices_to_check = $to_check;
3847 my $resblock = mon_cmd
($vmid, 'query-block');
3848 foreach my $block (@$resblock) {
3849 if($block->{device
} =~ m/^drive-(\S+)/){
3854 my $resmice = mon_cmd
($vmid, 'query-mice');
3855 foreach my $mice (@$resmice) {
3856 if ($mice->{name
} eq 'QEMU HID Tablet') {
3857 $devices->{tablet
} = 1;
3862 # for usb devices there is no query-usb
3863 # but we can iterate over the entries in
3864 # qom-list path=/machine/peripheral
3865 my $resperipheral = mon_cmd
($vmid, 'qom-list', path
=> '/machine/peripheral');
3866 foreach my $per (@$resperipheral) {
3867 if ($per->{name
} =~ m/^usb\d+$/) {
3868 $devices->{$per->{name
}} = 1;
3876 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
3878 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3880 my $devices_list = vm_devices_list
($vmid);
3881 return 1 if defined($devices_list->{$deviceid});
3883 # add PCI bridge if we need it for the device
3884 qemu_add_pci_bridge
($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type);
3886 if ($deviceid eq 'tablet') {
3888 qemu_deviceadd
($vmid, print_tabletdevice_full
($conf, $arch));
3890 } elsif ($deviceid eq 'keyboard') {
3892 qemu_deviceadd
($vmid, print_keyboarddevice_full
($conf, $arch));
3894 } elsif ($deviceid =~ m/^usb(\d+)$/) {
3896 die "usb hotplug currently not reliable\n";
3897 # since we can't reliably hot unplug all added usb devices and usb
3898 # passthrough breaks live migration we disable usb hotplugging for now
3899 #qemu_deviceadd($vmid, PVE::QemuServer::USB::print_usbdevice_full($conf, $deviceid, $device));
3901 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
3903 qemu_iothread_add
($vmid, $deviceid, $device);
3905 qemu_driveadd
($storecfg, $vmid, $device);
3906 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
3908 qemu_deviceadd
($vmid, $devicefull);
3909 eval { qemu_deviceaddverify
($vmid, $deviceid); };
3911 eval { qemu_drivedel
($vmid, $deviceid); };
3916 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
3919 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : "lsi";
3920 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
3921 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ?
"virtio-scsi-pci" : $scsihw;
3923 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
3925 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread
}) {
3926 qemu_iothread_add
($vmid, $deviceid, $device);
3927 $devicefull .= ",iothread=iothread-$deviceid";
3930 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues
}) {
3931 $devicefull .= ",num_queues=$device->{queues}";
3934 qemu_deviceadd
($vmid, $devicefull);
3935 qemu_deviceaddverify
($vmid, $deviceid);
3937 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
3939 qemu_findorcreatescsihw
($storecfg,$conf, $vmid, $device, $arch, $machine_type);
3940 qemu_driveadd
($storecfg, $vmid, $device);
3942 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
3943 eval { qemu_deviceadd
($vmid, $devicefull); };
3945 eval { qemu_drivedel
($vmid, $deviceid); };
3950 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
3952 return if !qemu_netdevadd
($vmid, $conf, $arch, $device, $deviceid);
3954 my $machine_type = PVE
::QemuServer
::Machine
::qemu_machine_pxe
($vmid, $conf);
3955 my $use_old_bios_files = undef;
3956 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3958 my $netdevicefull = print_netdevice_full
(
3959 $vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type);
3960 qemu_deviceadd
($vmid, $netdevicefull);
3962 qemu_deviceaddverify
($vmid, $deviceid);
3963 qemu_set_link_status
($vmid, $deviceid, !$device->{link_down
});
3966 eval { qemu_netdevdel
($vmid, $deviceid); };
3971 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
3974 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
3975 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
3977 qemu_deviceadd
($vmid, $devicefull);
3978 qemu_deviceaddverify
($vmid, $deviceid);
3981 die "can't hotplug device '$deviceid'\n";
3987 # fixme: this should raise exceptions on error!
3988 sub vm_deviceunplug
{
3989 my ($vmid, $conf, $deviceid) = @_;
3991 my $devices_list = vm_devices_list
($vmid);
3992 return 1 if !defined($devices_list->{$deviceid});
3994 my $bootdisks = PVE
::QemuServer
::Drive
::get_bootdisks
($conf);
3995 die "can't unplug bootdisk '$deviceid'\n" if grep {$_ eq $deviceid} @$bootdisks;
3997 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard') {
3999 qemu_devicedel
($vmid, $deviceid);
4001 } elsif ($deviceid =~ m/^usb\d+$/) {
4003 die "usb hotplug currently not reliable\n";
4004 # when unplugging usb devices this way, there may be remaining usb
4005 # controllers/hubs so we disable it for now
4006 #qemu_devicedel($vmid, $deviceid);
4007 #qemu_devicedelverify($vmid, $deviceid);
4009 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4011 qemu_devicedel
($vmid, $deviceid);
4012 qemu_devicedelverify
($vmid, $deviceid);
4013 qemu_drivedel
($vmid, $deviceid);
4014 qemu_iothread_del
($conf, $vmid, $deviceid);
4016 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4018 qemu_devicedel
($vmid, $deviceid);
4019 qemu_devicedelverify
($vmid, $deviceid);
4020 qemu_iothread_del
($conf, $vmid, $deviceid);
4022 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4024 qemu_devicedel
($vmid, $deviceid);
4025 qemu_drivedel
($vmid, $deviceid);
4026 qemu_deletescsihw
($conf, $vmid, $deviceid);
4028 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4030 qemu_devicedel
($vmid, $deviceid);
4031 qemu_devicedelverify
($vmid, $deviceid);
4032 qemu_netdevdel
($vmid, $deviceid);
4035 die "can't unplug device '$deviceid'\n";
4041 sub qemu_deviceadd
{
4042 my ($vmid, $devicefull) = @_;
4044 $devicefull = "driver=".$devicefull;
4045 my %options = split(/[=,]/, $devicefull);
4047 mon_cmd
($vmid, "device_add" , %options);
4050 sub qemu_devicedel
{
4051 my ($vmid, $deviceid) = @_;
4053 my $ret = mon_cmd
($vmid, "device_del", id
=> $deviceid);
4056 sub qemu_iothread_add
{
4057 my($vmid, $deviceid, $device) = @_;
4059 if ($device->{iothread
}) {
4060 my $iothreads = vm_iothreads_list
($vmid);
4061 qemu_objectadd
($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4065 sub qemu_iothread_del
{
4066 my($conf, $vmid, $deviceid) = @_;
4068 my $confid = $deviceid;
4069 if ($deviceid =~ m/^(?:virtioscsi|scsihw)(\d+)$/) {
4070 $confid = 'scsi' . $1;
4072 my $device = parse_drive
($confid, $conf->{$confid});
4073 if ($device->{iothread
}) {
4074 my $iothreads = vm_iothreads_list
($vmid);
4075 qemu_objectdel
($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4079 sub qemu_objectadd
{
4080 my($vmid, $objectid, $qomtype) = @_;
4082 mon_cmd
($vmid, "object-add", id
=> $objectid, "qom-type" => $qomtype);
4087 sub qemu_objectdel
{
4088 my($vmid, $objectid) = @_;
4090 mon_cmd
($vmid, "object-del", id
=> $objectid);
4096 my ($storecfg, $vmid, $device) = @_;
4098 my $kvmver = get_running_qemu_version
($vmid);
4099 my $io_uring = min_version
($kvmver, 6, 0);
4100 my $drive = print_drive_commandline_full
($storecfg, $vmid, $device, undef, $io_uring);
4101 $drive =~ s/\\/\\\\/g;
4102 my $ret = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "drive_add auto \"$drive\"");
4104 # If the command succeeds qemu prints: "OK
"
4105 return 1 if $ret =~ m/OK/s;
4107 die "adding drive failed
: $ret\n";
4111 my($vmid, $deviceid) = @_;
4113 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-
$deviceid");
4116 return 1 if $ret eq "";
4118 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4119 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4121 die "deleting drive
$deviceid failed
: $ret\n";
4124 sub qemu_deviceaddverify {
4125 my ($vmid, $deviceid) = @_;
4127 for (my $i = 0; $i <= 5; $i++) {
4128 my $devices_list = vm_devices_list($vmid);
4129 return 1 if defined($devices_list->{$deviceid});
4133 die "error on hotplug device
'$deviceid'\n";
4137 sub qemu_devicedelverify {
4138 my ($vmid, $deviceid) = @_;
4140 # need to verify that the device is correctly removed as device_del
4141 # is async and empty return is not reliable
4143 for (my $i = 0; $i <= 5; $i++) {
4144 my $devices_list = vm_devices_list($vmid);
4145 return 1 if !defined($devices_list->{$deviceid});
4149 die "error on hot-unplugging device
'$deviceid'\n";
4152 sub qemu_findorcreatescsihw {
4153 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4155 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4157 my $scsihwid="$controller_prefix$controller";
4158 my $devices_list = vm_devices_list($vmid);
4160 if(!defined($devices_list->{$scsihwid})) {
4161 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4167 sub qemu_deletescsihw {
4168 my ($conf, $vmid, $opt) = @_;
4170 my $device = parse_drive($opt, $conf->{$opt});
4172 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4173 vm_deviceunplug($vmid, $conf, "virtioscsi
$device->{index}");
4177 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4179 my $devices_list = vm_devices_list($vmid);
4180 foreach my $opt (keys %{$devices_list}) {
4181 if (is_valid_drivename($opt)) {
4182 my $drive = parse_drive($opt, $conf->{$opt});
4183 if($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4189 my $scsihwid="scsihw
$controller";
4191 vm_deviceunplug($vmid, $conf, $scsihwid);
4196 sub qemu_add_pci_bridge {
4197 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4203 print_pci_addr($device, $bridges, $arch, $machine_type);
4205 while (my ($k, $v) = each %$bridges) {
4208 return 1 if !defined($bridgeid) || $bridgeid < 1;
4210 my $bridge = "pci
.$bridgeid";
4211 my $devices_list = vm_devices_list($vmid);
4213 if (!defined($devices_list->{$bridge})) {
4214 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4220 sub qemu_set_link_status {
4221 my ($vmid, $device, $up) = @_;
4223 mon_cmd($vmid, "set_link
", name => $device,
4224 up => $up ? JSON::true : JSON::false);
4227 sub qemu_netdevadd {
4228 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4230 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4231 my %options = split(/[=,]/, $netdev);
4233 if (defined(my $vhost = $options{vhost})) {
4234 $options{vhost} = JSON::boolean(PVE::JSONSchema::parse_boolean($vhost));
4237 if (defined(my $queues = $options{queues})) {
4238 $options{queues} = $queues + 0;
4241 mon_cmd($vmid, "netdev_add
", %options);
4245 sub qemu_netdevdel {
4246 my ($vmid, $deviceid) = @_;
4248 mon_cmd($vmid, "netdev_del
", id => $deviceid);
4251 sub qemu_usb_hotplug {
4252 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4256 # remove the old one first
4257 vm_deviceunplug($vmid, $conf, $deviceid);
4259 # check if xhci controller is necessary and available
4260 if ($device->{usb3}) {
4262 my $devicelist = vm_devices_list($vmid);
4264 if (!$devicelist->{xhci}) {
4265 my $pciaddr = print_pci_addr("xhci
", undef, $arch, $machine_type);
4266 qemu_deviceadd($vmid, "nec-usb-xhci
,id
=xhci
$pciaddr");
4269 my $d = parse_usb_device($device->{host});
4270 $d->{usb3} = $device->{usb3};
4273 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d, $arch, $machine_type);
4276 sub qemu_cpu_hotplug {
4277 my ($vmid, $conf, $vcpus) = @_;
4279 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
4282 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4283 $sockets = $conf->{sockets} if $conf->{sockets};
4284 my $cores = $conf->{cores} || 1;
4285 my $maxcpus = $sockets * $cores;
4287 $vcpus = $maxcpus if !$vcpus;
4289 die "you can
't add more vcpus than maxcpus\n"
4290 if $vcpus > $maxcpus;
4292 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4294 if ($vcpus < $currentvcpus) {
4296 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4298 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4299 qemu_devicedel($vmid, "cpu$i");
4301 my $currentrunningvcpus = undef;
4303 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4304 last if scalar(@{$currentrunningvcpus}) == $i-1;
4305 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4309 #update conf after each succesfull cpu unplug
4310 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4311 PVE::QemuConfig->write_config($vmid, $conf);
4314 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4320 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4321 die "vcpus in running vm does not match its configuration\n"
4322 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4324 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4326 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4327 my $cpustr = print_cpu_device($conf, $i);
4328 qemu_deviceadd($vmid, $cpustr);
4331 my $currentrunningvcpus = undef;
4333 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4334 last if scalar(@{$currentrunningvcpus}) == $i;
4335 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4339 #update conf after each succesfull cpu hotplug
4340 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4341 PVE::QemuConfig->write_config($vmid, $conf);
4345 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4346 mon_cmd($vmid, "cpu-add", id => int($i));
4351 sub qemu_block_set_io_throttle {
4352 my ($vmid, $deviceid,
4353 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4354 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4355 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4356 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4358 return if !check_running($vmid) ;
4360 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4362 bps_rd => int($bps_rd),
4363 bps_wr => int($bps_wr),
4365 iops_rd => int($iops_rd),
4366 iops_wr => int($iops_wr),
4367 bps_max => int($bps_max),
4368 bps_rd_max => int($bps_rd_max),
4369 bps_wr_max => int($bps_wr_max),
4370 iops_max => int($iops_max),
4371 iops_rd_max => int($iops_rd_max),
4372 iops_wr_max => int($iops_wr_max),
4373 bps_max_length => int($bps_max_length),
4374 bps_rd_max_length => int($bps_rd_max_length),
4375 bps_wr_max_length => int($bps_wr_max_length),
4376 iops_max_length => int($iops_max_length),
4377 iops_rd_max_length => int($iops_rd_max_length),
4378 iops_wr_max_length => int($iops_wr_max_length),
4383 sub qemu_block_resize {
4384 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4386 my $running = check_running($vmid);
4388 $size = 0 if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4390 return if !$running;
4392 my $padding = (1024 - $size % 1024) % 1024;
4393 $size = $size + $padding;
4398 device => $deviceid,
4404 sub qemu_volume_snapshot {
4405 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4407 my $running = check_running($vmid);
4409 if ($running && do_snapshots_with_qemu($storecfg, $volid)){
4410 mon_cmd($vmid, 'blockdev-snapshot-internal-sync
', device => $deviceid, name => $snap);
4412 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4416 sub qemu_volume_snapshot_delete {
4417 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4419 my $running = check_running($vmid);
4424 my $conf = PVE::QemuConfig->load_config($vmid);
4425 PVE::QemuConfig->foreach_volume($conf, sub {
4426 my ($ds, $drive) = @_;
4427 $running = 1 if $drive->{file} eq $volid;
4431 if ($running && do_snapshots_with_qemu($storecfg, $volid)){
4432 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync
', device => $deviceid, name => $snap);
4434 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4438 sub set_migration_caps {
4439 my ($vmid, $savevm) = @_;
4441 my $qemu_support = eval { mon_cmd($vmid, "query-proxmox-support") };
4443 my $bitmap_prop = $savevm ? 'pbs-dirty-bitmap-savevm
' : 'pbs-dirty-bitmap-migration
';
4444 my $dirty_bitmaps = $qemu_support->{$bitmap_prop} ? 1 : 0;
4449 "auto-converge" => 1,
4451 "x-rdma-pin-all" => 0,
4454 "dirty-bitmaps" => $dirty_bitmaps,
4457 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4459 for my $supported_capability (@$supported_capabilities) {
4461 capability => $supported_capability->{capability},
4462 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4466 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4470 my ($conf, $func, @param) = @_;
4474 my $test_volid = sub {
4475 my ($key, $drive, $snapname) = @_;
4477 my $volid = $drive->{file};
4480 $volhash->{$volid}->{cdrom} //= 1;
4481 $volhash->{$volid}->{cdrom} = 0 if !drive_is_cdrom($drive);
4483 my $replicate = $drive->{replicate} // 1;
4484 $volhash->{$volid}->{replicate} //= 0;
4485 $volhash->{$volid}->{replicate} = 1 if $replicate;
4487 $volhash->{$volid}->{shared} //= 0;
4488 $volhash->{$volid}->{shared} = 1 if $drive->{shared};
4490 $volhash->{$volid}->{referenced_in_config} //= 0;
4491 $volhash->{$volid}->{referenced_in_config} = 1 if !defined($snapname);
4493 $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
4494 if defined($snapname);
4496 my $size = $drive->{size};
4497 $volhash->{$volid}->{size} //= $size if $size;
4499 $volhash->{$volid}->{is_vmstate} //= 0;
4500 $volhash->{$volid}->{is_vmstate} = 1 if $key eq 'vmstate
';
4502 $volhash->{$volid}->{is_unused} //= 0;
4503 $volhash->{$volid}->{is_unused} = 1 if $key =~ /^unused\d+$/;
4505 $volhash->{$volid}->{drivename} = $key if is_valid_drivename($key);
4508 my $include_opts = {
4509 extra_keys => ['vmstate
'],
4510 include_unused => 1,
4513 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $test_volid);
4514 foreach my $snapname (keys %{$conf->{snapshots}}) {
4515 my $snap = $conf->{snapshots}->{$snapname};
4516 PVE::QemuConfig->foreach_volume_full($snap, $include_opts, $test_volid, $snapname);
4519 foreach my $volid (keys %$volhash) {
4520 &$func($volid, $volhash->{$volid}, @param);
4524 my $fast_plug_option = {
4532 'vmstatestorage
' => 1,
4537 # hotplug changes in [PENDING]
4538 # $selection hash can be used to only apply specified options, for
4539 # example: { cores => 1 } (only apply changed 'cores
')
4540 # $errors ref is used to return error messages
4541 sub vmconfig_hotplug_pending {
4542 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4544 my $defaults = load_defaults();
4545 my $arch = get_vm_arch($conf);
4546 my $machine_type = get_vm_machine($conf, undef, $arch);
4548 # commit values which do not have any impact on running VM first
4549 # Note: those option cannot raise errors, we we do not care about
4550 # $selection and always apply them.
4552 my $add_error = sub {
4553 my ($opt, $msg) = @_;
4554 $errors->{$opt} = "hotplug problem - $msg";
4558 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4559 if ($fast_plug_option->{$opt}) {
4560 $conf->{$opt} = $conf->{pending}->{$opt};
4561 delete $conf->{pending}->{$opt};
4567 PVE::QemuConfig->write_config($vmid, $conf);
4570 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
4572 my $cgroup = PVE::QemuServer::CGroup->new($vmid);
4573 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4574 foreach my $opt (sort keys %$pending_delete_hash) {
4575 next if $selection && !$selection->{$opt};
4576 my $force = $pending_delete_hash->{$opt}->{force};
4578 if ($opt eq 'hotplug
') {
4579 die "skip\n" if ($conf->{hotplug} =~ /memory/);
4580 } elsif ($opt eq 'tablet
') {
4581 die "skip\n" if !$hotplug_features->{usb};
4582 if ($defaults->{tablet}) {
4583 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4584 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4585 if $arch eq 'aarch64
';
4587 vm_deviceunplug($vmid, $conf, 'tablet
');
4588 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4590 } elsif ($opt =~ m/^usb\d+/) {
4592 # since we cannot reliably hot unplug usb devices we are disabling it
4593 #die "skip\n" if !$hotplug_features->{usb} || $conf->{$opt} =~ m/spice/i;
4594 #vm_deviceunplug($vmid, $conf, $opt);
4595 } elsif ($opt eq 'vcpus
') {
4596 die "skip\n" if !$hotplug_features->{cpu};
4597 qemu_cpu_hotplug($vmid, $conf, undef);
4598 } elsif ($opt eq 'balloon
') {
4599 # enable balloon device is not hotpluggable
4600 die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
4601 # here we reset the ballooning value to memory
4602 my $balloon = $conf->{memory} || $defaults->{memory};
4603 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4604 } elsif ($fast_plug_option->{$opt}) {
4606 } elsif ($opt =~ m/^net(\d+)$/) {
4607 die "skip\n" if !$hotplug_features->{network};
4608 vm_deviceunplug($vmid, $conf, $opt);
4609 } elsif (is_valid_drivename($opt)) {
4610 die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
4611 vm_deviceunplug($vmid, $conf, $opt);
4612 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4613 } elsif ($opt =~ m/^memory$/) {
4614 die "skip\n" if !$hotplug_features->{memory};
4615 PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
4616 } elsif ($opt eq 'cpuunits
') {
4617 $cgroup->change_cpu_shares(undef, $defaults->{cpuunits});
4618 } elsif ($opt eq 'cpulimit
') {
4619 $cgroup->change_cpu_quota(-1, 100000);
4625 &$add_error($opt, $err) if $err ne "skip\n";
4627 delete $conf->{$opt};
4628 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4632 my ($apply_pending_cloudinit, $apply_pending_cloudinit_done);
4633 $apply_pending_cloudinit = sub {
4634 return if $apply_pending_cloudinit_done; # once is enough
4635 $apply_pending_cloudinit_done = 1; # once is enough
4637 my ($key, $value) = @_;
4639 my @cloudinit_opts = keys %$confdesc_cloudinit;
4640 foreach my $opt (keys %{$conf->{pending}}) {
4641 next if !grep { $_ eq $opt } @cloudinit_opts;
4642 $conf->{$opt} = delete $conf->{pending}->{$opt};
4645 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4646 foreach my $opt (sort keys %$pending_delete_hash) {
4647 next if !grep { $_ eq $opt } @cloudinit_opts;
4648 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4649 delete $conf->{$opt};
4652 my $new_conf = { %$conf };
4653 $new_conf->{$key} = $value;
4654 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($new_conf, $vmid);
4657 foreach my $opt (keys %{$conf->{pending}}) {
4658 next if $selection && !$selection->{$opt};
4659 my $value = $conf->{pending}->{$opt};
4661 if ($opt eq 'hotplug
') {
4662 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/);
4663 } elsif ($opt eq 'tablet
') {
4664 die "skip\n" if !$hotplug_features->{usb};
4666 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4667 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4668 if $arch eq 'aarch64
';
4669 } elsif ($value == 0) {
4670 vm_deviceunplug($vmid, $conf, 'tablet
');
4671 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4673 } elsif ($opt =~ m/^usb\d+$/) {
4675 # since we cannot reliably hot unplug usb devices we disable it for now
4676 #die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
4677 #my $d = eval { parse_property_string($usbdesc->{format}, $value) };
4678 #die "skip\n" if !$d;
4679 #qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d, $arch, $machine_type);
4680 } elsif ($opt eq 'vcpus
') {
4681 die "skip\n" if !$hotplug_features->{cpu};
4682 qemu_cpu_hotplug($vmid, $conf, $value);
4683 } elsif ($opt eq 'balloon
') {
4684 # enable/disable balloning device is not hotpluggable
4685 my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon});
4686 my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon});
4687 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
4689 # allow manual ballooning if shares is set to zero
4690 if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
4691 my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
4692 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4694 } elsif ($opt =~ m/^net(\d+)$/) {
4695 # some changes can be done without hotplug
4696 vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
4697 $vmid, $opt, $value, $arch, $machine_type);
4698 } elsif (is_valid_drivename($opt)) {
4699 die "skip\n" if $opt eq 'efidisk0
';
4700 # some changes can be done without hotplug
4701 my $drive = parse_drive($opt, $value);
4702 if (drive_is_cloudinit($drive)) {
4703 &$apply_pending_cloudinit($opt, $value);
4705 vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
4706 $vmid, $opt, $value, $arch, $machine_type);
4707 } elsif ($opt =~ m/^memory$/) { #dimms
4708 die "skip\n" if !$hotplug_features->{memory};
4709 $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
4710 } elsif ($opt eq 'cpuunits
') {
4711 $cgroup->change_cpu_shares($conf->{pending}->{$opt}, $defaults->{cpuunits});
4712 } elsif ($opt eq 'cpulimit
') {
4713 my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
4714 $cgroup->change_cpu_quota($cpulimit, 100000);
4716 die "skip\n"; # skip non-hot-pluggable options
4720 &$add_error($opt, $err) if $err ne "skip\n";
4722 $conf->{$opt} = $value;
4723 delete $conf->{pending}->{$opt};
4727 PVE::QemuConfig->write_config($vmid, $conf);
4730 sub try_deallocate_drive {
4731 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
4733 if (($force || $key =~ /^unused/) && !drive_is_cdrom($drive, 1)) {
4734 my $volid = $drive->{file};
4735 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
4736 my $sid = PVE::Storage::parse_volume_id($volid);
4737 $rpcenv->check($authuser, "/storage/$sid", ['Datastore
.AllocateSpace
']);
4739 # check if the disk is really unused
4740 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
4741 if PVE::QemuServer::Drive::is_volume_in_use($storecfg, $conf, $key, $volid);
4742 PVE::Storage::vdisk_free($storecfg, $volid);
4745 # If vm is not owner of this disk remove from config
4753 sub vmconfig_delete_or_detach_drive {
4754 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
4756 my $drive = parse_drive($opt, $conf->{$opt});
4758 my $rpcenv = PVE::RPCEnvironment::get();
4759 my $authuser = $rpcenv->get_user();
4762 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM
.Config
.Disk
']);
4763 try_deallocate_drive($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
4765 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $drive);
4771 sub vmconfig_apply_pending {
4772 my ($vmid, $conf, $storecfg, $errors) = @_;
4774 my $add_apply_error = sub {
4775 my ($opt, $msg) = @_;
4776 my $err_msg = "unable to apply pending change $opt : $msg";
4777 $errors->{$opt} = $err_msg;
4783 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4784 foreach my $opt (sort keys %$pending_delete_hash) {
4785 my $force = $pending_delete_hash->{$opt}->{force};
4787 if ($opt =~ m/^unused/) {
4788 die "internal error";
4789 } elsif (defined($conf->{$opt}) && is_valid_drivename($opt)) {
4790 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4794 $add_apply_error->($opt, $err);
4796 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4797 delete $conf->{$opt};
4801 PVE::QemuConfig->cleanup_pending($conf);
4803 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4804 next if $opt eq 'delete'; # just to be sure
4806 if (defined($conf->{$opt}) && is_valid_drivename($opt)) {
4807 vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
4811 $add_apply_error->($opt, $err);
4813 $conf->{$opt} = delete $conf->{pending}->{$opt};
4817 # write all changes at once to avoid unnecessary i/o
4818 PVE::QemuConfig->write_config($vmid, $conf);
4821 sub vmconfig_update_net {
4822 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
4824 my $newnet = parse_net($value);
4826 if ($conf->{$opt}) {
4827 my $oldnet = parse_net($conf->{$opt});
4829 if (safe_string_ne($oldnet->{model}, $newnet->{model}) ||
4830 safe_string_ne($oldnet->{macaddr}, $newnet->{macaddr}) ||
4831 safe_num_ne($oldnet->{queues}, $newnet->{queues}) ||
4832 !($newnet->{bridge} && $oldnet->{bridge})) { # bridge/nat mode change
4834 # for non online change, we try to hot-unplug
4835 die "skip\n" if !$hotplug;
4836 vm_deviceunplug($vmid, $conf, $opt);
4839 die "internal error" if $opt !~ m/net(\d+)/;
4840 my $iface = "tap${vmid}i$1";
4842 if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
4843 safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
4844 safe_string_ne($oldnet->{trunks}, $newnet->{trunks}) ||
4845 safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
4846 PVE::Network::tap_unplug($iface);
4849 PVE::Network::SDN::Zones::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
4851 PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
4853 } elsif (safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
4854 # Rate can be applied on its own but any change above needs to
4855 # include the rate in tap_plug since OVS resets everything.
4856 PVE::Network::tap_rate_limit($iface, $newnet->{rate});
4859 if (safe_string_ne($oldnet->{link_down}, $newnet->{link_down})) {
4860 qemu_set_link_status($vmid, $opt, !$newnet->{link_down});
4868 vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
4874 sub vmconfig_update_disk {
4875 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
4877 my $drive = parse_drive($opt, $value);
4879 if ($conf->{$opt} && (my $old_drive = parse_drive($opt, $conf->{$opt}))) {
4880 my $media = $drive->{media} || 'disk
';
4881 my $oldmedia = $old_drive->{media} || 'disk
';
4882 die "unable to change media type\n" if $media ne $oldmedia;
4884 if (!drive_is_cdrom($old_drive)) {
4886 if ($drive->{file} ne $old_drive->{file}) {
4888 die "skip\n" if !$hotplug;
4890 # unplug and register as unused
4891 vm_deviceunplug($vmid, $conf, $opt);
4892 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive)
4895 # update existing disk
4897 # skip non hotpluggable value
4898 if (safe_string_ne($drive->{discard}, $old_drive->{discard}) ||
4899 safe_string_ne($drive->{iothread}, $old_drive->{iothread}) ||
4900 safe_string_ne($drive->{queues}, $old_drive->{queues}) ||
4901 safe_string_ne($drive->{cache}, $old_drive->{cache}) ||
4902 safe_string_ne($drive->{ssd}, $old_drive->{ssd})) {
4907 if (safe_num_ne($drive->{mbps}, $old_drive->{mbps}) ||
4908 safe_num_ne($drive->{mbps_rd}, $old_drive->{mbps_rd}) ||
4909 safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) ||
4910 safe_num_ne($drive->{iops}, $old_drive->{iops}) ||
4911 safe_num_ne($drive->{iops_rd}, $old_drive->{iops_rd}) ||
4912 safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr}) ||
4913 safe_num_ne($drive->{mbps_max}, $old_drive->{mbps_max}) ||
4914 safe_num_ne($drive->{mbps_rd_max}, $old_drive->{mbps_rd_max}) ||
4915 safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) ||
4916 safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) ||
4917 safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) ||
4918 safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max}) ||
4919 safe_num_ne($drive->{bps_max_length}, $old_drive->{bps_max_length}) ||
4920 safe_num_ne($drive->{bps_rd_max_length}, $old_drive->{bps_rd_max_length}) ||
4921 safe_num_ne($drive->{bps_wr_max_length}, $old_drive->{bps_wr_max_length}) ||
4922 safe_num_ne($drive->{iops_max_length}, $old_drive->{iops_max_length}) ||
4923 safe_num_ne($drive->{iops_rd_max_length}, $old_drive->{iops_rd_max_length}) ||
4924 safe_num_ne($drive->{iops_wr_max_length}, $old_drive->{iops_wr_max_length})) {
4926 qemu_block_set_io_throttle(
4928 ($drive->{mbps} || 0)*1024*1024,
4929 ($drive->{mbps_rd} || 0)*1024*1024,
4930 ($drive->{mbps_wr} || 0)*1024*1024,
4931 $drive->{iops} || 0,
4932 $drive->{iops_rd} || 0,
4933 $drive->{iops_wr} || 0,
4934 ($drive->{mbps_max} || 0)*1024*1024,
4935 ($drive->{mbps_rd_max} || 0)*1024*1024,
4936 ($drive->{mbps_wr_max} || 0)*1024*1024,
4937 $drive->{iops_max} || 0,
4938 $drive->{iops_rd_max} || 0,
4939 $drive->{iops_wr_max} || 0,
4940 $drive->{bps_max_length} || 1,
4941 $drive->{bps_rd_max_length} || 1,
4942 $drive->{bps_wr_max_length} || 1,
4943 $drive->{iops_max_length} || 1,
4944 $drive->{iops_rd_max_length} || 1,
4945 $drive->{iops_wr_max_length} || 1,
4955 if ($drive->{file} eq 'none
') {
4956 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
4957 if (drive_is_cloudinit($old_drive)) {
4958 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive);
4961 my $path = get_iso_path($storecfg, $vmid, $drive->{file});
4963 # force eject if locked
4964 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
4967 mon_cmd($vmid, "blockdev-change-medium",
4968 id => "$opt", filename => "$path");
4976 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
4978 PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
4979 vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
4982 # called in locked context by incoming migration
4983 sub vm_migrate_get_nbd_disks {
4984 my ($storecfg, $conf, $replicated_volumes) = @_;
4986 my $local_volumes = {};
4987 PVE::QemuConfig->foreach_volume($conf, sub {
4988 my ($ds, $drive) = @_;
4990 return if drive_is_cdrom($drive);
4992 my $volid = $drive->{file};
4996 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
4998 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
4999 return if $scfg->{shared};
5001 # replicated disks re-use existing state via bitmap
5002 my $use_existing = $replicated_volumes->{$volid} ? 1 : 0;
5003 $local_volumes->{$ds} = [$volid, $storeid, $volname, $drive, $use_existing];
5005 return $local_volumes;
5008 # called in locked context by incoming migration
5009 sub vm_migrate_alloc_nbd_disks {
5010 my ($storecfg, $vmid, $source_volumes, $storagemap) = @_;
5015 foreach my $opt (sort keys %$source_volumes) {
5016 my ($volid, $storeid, $volname, $drive, $use_existing) = @{$source_volumes->{$opt}};
5018 if ($use_existing) {
5019 $nbd->{$opt}->{drivestr} = print_drive($drive);
5020 $nbd->{$opt}->{volid} = $volid;
5021 $nbd->{$opt}->{replicated} = 1;
5025 # If a remote storage is specified and the format of the original
5026 # volume is not available there, fall back to the default format.
5027 # Otherwise use the same format as the original.
5028 if (!$storagemap->{identity}) {
5029 $storeid = map_storage($storagemap, $storeid);
5030 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
5031 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5032 my $fileFormat = qemu_img_format($scfg, $volname);
5033 $format = (grep {$fileFormat eq $_} @{$validFormats}) ? $fileFormat : $defFormat;
5035 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5036 $format = qemu_img_format($scfg, $volname);
5039 my $size = $drive->{size} / 1024;
5040 my $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $format, undef, $size);
5041 my $newdrive = $drive;
5042 $newdrive->{format} = $format;
5043 $newdrive->{file} = $newvolid;
5044 my $drivestr = print_drive($newdrive);
5045 $nbd->{$opt}->{drivestr} = $drivestr;
5046 $nbd->{$opt}->{volid} = $newvolid;
5052 # see vm_start_nolock for parameters, additionally:
5054 # storagemap = parsed storage map for allocating NBD disks
5056 my ($storecfg, $vmid, $params, $migrate_opts) = @_;
5058 return PVE::QemuConfig->lock_config($vmid, sub {
5059 my $conf = PVE::QemuConfig->load_config($vmid, $migrate_opts->{migratedfrom});
5061 die "you can't start a vm
if it
's a template\n"
5062 if !$params->{skiptemplate} && PVE::QemuConfig->is_template($conf);
5064 my $has_suspended_lock = PVE::QemuConfig->has_lock($conf, 'suspended
');
5065 my $has_backup_lock = PVE::QemuConfig->has_lock($conf, 'backup
');
5067 my $running = check_running($vmid, undef, $migrate_opts->{migratedfrom});
5069 if ($has_backup_lock && $running) {
5070 # a backup is currently running, attempt to start the guest in the
5071 # existing QEMU instance
5072 return vm_resume($vmid);
5075 PVE::QemuConfig->check_lock($conf)
5076 if !($params->{skiplock} || $has_suspended_lock);
5078 $params->{resume} = $has_suspended_lock || defined($conf->{vmstate});
5080 die "VM $vmid already running\n" if $running;
5082 if (my $storagemap = $migrate_opts->{storagemap}) {
5083 my $replicated = $migrate_opts->{replicated_volumes};
5084 my $disks = vm_migrate_get_nbd_disks($storecfg, $conf, $replicated);
5085 $migrate_opts->{nbd} = vm_migrate_alloc_nbd_disks($storecfg, $vmid, $disks, $storagemap);
5087 foreach my $opt (keys %{$migrate_opts->{nbd}}) {
5088 $conf->{$opt} = $migrate_opts->{nbd}->{$opt}->{drivestr};
5092 return vm_start_nolock($storecfg, $vmid, $conf, $params, $migrate_opts);
5098 # statefile => 'tcp
', 'unix
' for migration or path/volid for RAM state
5099 # skiplock => 0/1, skip checking for config lock
5100 # skiptemplate => 0/1, skip checking whether VM is template
5101 # forcemachine => to force Qemu machine (rollback/migration)
5102 # forcecpu => a QEMU '-cpu
' argument string to override get_cpu_options
5103 # timeout => in seconds
5104 # paused => start VM in paused state (backup)
5105 # resume => resume from hibernation
5116 # nbd => volumes for NBD exports (vm_migrate_alloc_nbd_disks)
5117 # migratedfrom => source node
5118 # spice_ticket => used for spice migration, passed via tunnel/stdin
5119 # network => CIDR of migration network
5120 # type => secure/insecure - tunnel over encrypted connection or plain-text
5121 # nbd_proto_version => int, 0 for TCP, 1 for UNIX
5122 # replicated_volumes = which volids should be re-used with bitmaps for nbd migration
5123 sub vm_start_nolock {
5124 my ($storecfg, $vmid, $conf, $params, $migrate_opts) = @_;
5126 my $statefile = $params->{statefile};
5127 my $resume = $params->{resume};
5129 my $migratedfrom = $migrate_opts->{migratedfrom};
5130 my $migration_type = $migrate_opts->{type};
5134 # clean up leftover reboot request files
5135 eval { clear_reboot_request($vmid); };
5138 if (!$statefile && scalar(keys %{$conf->{pending}})) {
5139 vmconfig_apply_pending($vmid, $conf, $storecfg);
5140 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
5143 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
5145 my $defaults = load_defaults();
5147 # set environment variable useful inside network script
5148 $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
5150 PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-start
', 1);
5152 my $forcemachine = $params->{forcemachine};
5153 my $forcecpu = $params->{forcecpu};
5155 # enforce machine and CPU type on suspended vm to ensure HW compatibility
5156 $forcemachine = $conf->{runningmachine};
5157 $forcecpu = $conf->{runningcpu};
5158 print "Resuming suspended VM\n";
5161 my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid,
5162 $conf, $defaults, $forcemachine, $forcecpu, $params->{'pbs-backing
'});
5165 my $get_migration_ip = sub {
5166 my ($nodename) = @_;
5168 return $migration_ip if defined($migration_ip);
5170 my $cidr = $migrate_opts->{network};
5172 if (!defined($cidr)) {
5173 my $dc_conf = PVE::Cluster::cfs_read_file('datacenter
.cfg
');
5174 $cidr = $dc_conf->{migration}->{network};
5177 if (defined($cidr)) {
5178 my $ips = PVE::Network::get_local_ip_from_cidr($cidr);
5180 die "could not get IP: no address configured on local " .
5181 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5183 die "could not get IP: multiple addresses configured on local " .
5184 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5186 $migration_ip = @$ips[0];
5189 $migration_ip = PVE::Cluster::remote_node_ip($nodename, 1)
5190 if !defined($migration_ip);
5192 return $migration_ip;
5197 if ($statefile eq 'tcp
') {
5198 my $localip = "localhost";
5199 my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter
.cfg
');
5200 my $nodename = nodename();
5202 if (!defined($migration_type)) {
5203 if (defined($datacenterconf->{migration}->{type})) {
5204 $migration_type = $datacenterconf->{migration}->{type};
5206 $migration_type = 'secure
';
5210 if ($migration_type eq 'insecure
') {
5211 $localip = $get_migration_ip->($nodename);
5212 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5215 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5216 my $migrate_port = PVE::Tools::next_migrate_port($pfamily);
5217 $migrate_uri = "tcp:${localip}:${migrate_port}";
5218 push @$cmd, '-incoming
', $migrate_uri;
5221 } elsif ($statefile eq 'unix
') {
5222 # should be default for secure migrations as a ssh TCP forward
5223 # tunnel is not deterministic reliable ready and fails regurarly
5224 # to set up in time, so use UNIX socket forwards
5225 my $socket_addr = "/run/qemu-server/$vmid.migrate";
5226 unlink $socket_addr;
5228 $migrate_uri = "unix:$socket_addr";
5230 push @$cmd, '-incoming
', $migrate_uri;
5233 } elsif (-e $statefile) {
5234 push @$cmd, '-loadstate
', $statefile;
5236 my $statepath = PVE::Storage::path($storecfg, $statefile);
5237 push @$vollist, $statefile;
5238 push @$cmd, '-loadstate
', $statepath;
5240 } elsif ($params->{paused}) {
5245 for (my $i = 0; $i < $PVE::QemuServer::PCI::MAX_HOSTPCI_DEVICES; $i++) {
5246 my $d = parse_hostpci($conf->{"hostpci$i"});
5248 my $pcidevices = $d->{pciid};
5249 foreach my $pcidevice (@$pcidevices) {
5250 my $pciid = $pcidevice->{id};
5252 my $info = PVE::SysFSTools::pci_device_info("$pciid");
5253 die "IOMMU not present\n" if !PVE::SysFSTools::check_iommu_support();
5254 die "no pci device info for device '$pciid'\n" if !$info;
5257 my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
5258 PVE::SysFSTools::pci_create_mdev_device($pciid, $uuid, $d->{mdev});
5260 die "can't unbind
/bind PCI group to VFIO
'$pciid'\n"
5261 if !PVE::SysFSTools::pci_dev_group_bind_to_vfio($pciid);
5262 die "can
't reset PCI device '$pciid'\n"
5263 if $info->{has_fl_reset} && !PVE::SysFSTools::pci_dev_reset($info);
5268 PVE::Storage::activate_volumes($storecfg, $vollist);
5271 run_command(['/bin/systemctl
', 'stop
', "$vmid.scope"],
5272 outfunc => sub {}, errfunc => sub {});
5274 # Issues with the above 'stop
' not being fully completed are extremely rare, a very low
5275 # timeout should be more than enough here...
5276 PVE::Systemd::wait_for_unit_removed("$vmid.scope", 5);
5278 my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
5279 : $defaults->{cpuunits};
5281 my $start_timeout = $params->{timeout} // config_aware_timeout($conf, $resume);
5283 timeout => $statefile ? undef : $start_timeout,
5288 # when migrating, prefix QEMU output so other side can pick up any
5289 # errors that might occur and show the user
5290 if ($migratedfrom) {
5291 $run_params{quiet} = 1;
5292 $run_params{logfunc} = sub { print "QEMU: $_[0]\n" };
5296 Slice => 'qemu
.slice
',
5297 KillMode => 'process
',
5299 TimeoutStopUSec => ULONG_MAX, # infinity
5302 if (PVE::CGroup::cgroup_mode() == 2) {
5303 $properties{CPUWeight} = $cpuunits;
5305 $properties{CPUShares} = $cpuunits;
5308 if (my $cpulimit = $conf->{cpulimit}) {
5309 $properties{CPUQuota} = int($cpulimit * 100);
5311 $properties{timeout} = 10 if $statefile; # setting up the scope shoul be quick
5313 my $run_qemu = sub {
5314 PVE::Tools::run_fork sub {
5315 PVE::Systemd::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %properties);
5317 my $exitcode = run_command($cmd, %run_params);
5318 die "QEMU exited with code $exitcode\n" if $exitcode;
5322 if ($conf->{hugepages}) {
5325 my $hugepages_topology = PVE::QemuServer::Memory::hugepages_topology($conf);
5326 my $hugepages_host_topology = PVE::QemuServer::Memory::hugepages_host_topology();
5328 PVE::QemuServer::Memory::hugepages_mount();
5329 PVE::QemuServer::Memory::hugepages_allocate($hugepages_topology, $hugepages_host_topology);
5331 eval { $run_qemu->() };
5333 PVE::QemuServer::Memory::hugepages_reset($hugepages_host_topology)
5334 if !$conf->{keephugepages};
5338 PVE::QemuServer::Memory::hugepages_pre_deallocate($hugepages_topology)
5339 if !$conf->{keephugepages};
5341 eval { PVE::QemuServer::Memory::hugepages_update_locked($code); };
5344 eval { $run_qemu->() };
5348 # deactivate volumes if start fails
5349 eval { PVE::Storage::deactivate_volumes($storecfg, $vollist); };
5350 die "start failed: $err";
5353 print "migration listens on $migrate_uri\n" if $migrate_uri;
5354 $res->{migrate_uri} = $migrate_uri;
5356 if ($statefile && $statefile ne 'tcp
' && $statefile ne 'unix
') {
5357 eval { mon_cmd($vmid, "cont"); };
5361 #start nbd server for storage migration
5362 if (my $nbd = $migrate_opts->{nbd}) {
5363 my $nbd_protocol_version = $migrate_opts->{nbd_proto_version} // 0;
5365 my $migrate_storage_uri;
5366 # nbd_protocol_version > 0 for unix socket support
5367 if ($nbd_protocol_version > 0 && $migration_type eq 'secure
') {
5368 my $socket_path = "/run/qemu-server/$vmid\_nbd.migrate";
5369 mon_cmd($vmid, "nbd-server-start", addr => { type => 'unix
', data => { path => $socket_path } } );
5370 $migrate_storage_uri = "nbd:unix:$socket_path";
5372 my $nodename = nodename();
5373 my $localip = $get_migration_ip->($nodename);
5374 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5375 my $storage_migrate_port = PVE::Tools::next_migrate_port($pfamily);
5377 mon_cmd($vmid, "nbd-server-start", addr => {
5380 host => "${localip}",
5381 port => "${storage_migrate_port}",
5384 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5385 $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}";
5388 $res->{migrate_storage_uri} = $migrate_storage_uri;
5390 foreach my $opt (sort keys %$nbd) {
5391 my $drivestr = $nbd->{$opt}->{drivestr};
5392 my $volid = $nbd->{$opt}->{volid};
5393 mon_cmd($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
5394 my $nbd_uri = "$migrate_storage_uri:exportname=drive-$opt";
5395 print "storage migration listens on $nbd_uri volume:$drivestr\n";
5396 print "re-using replicated volume: $opt - $volid\n"
5397 if $nbd->{$opt}->{replicated};
5399 $res->{drives}->{$opt} = $nbd->{$opt};
5400 $res->{drives}->{$opt}->{nbd_uri} = $nbd_uri;
5404 if ($migratedfrom) {
5406 set_migration_caps($vmid);
5411 print "spice listens on port $spice_port\n";
5412 $res->{spice_port} = $spice_port;
5413 if ($migrate_opts->{spice_ticket}) {
5414 mon_cmd($vmid, "set_password", protocol => 'spice
', password =>
5415 $migrate_opts->{spice_ticket});
5416 mon_cmd($vmid, "expire_password", protocol => 'spice
', time => "+30");
5421 mon_cmd($vmid, "balloon", value => $conf->{balloon}*1024*1024)
5422 if !$statefile && $conf->{balloon};
5424 foreach my $opt (keys %$conf) {
5425 next if $opt !~ m/^net\d+$/;
5426 my $nicconf = parse_net($conf->{$opt});
5427 qemu_set_link_status($vmid, $opt, 0) if $nicconf->{link_down};
5431 mon_cmd($vmid, 'qom-set
',
5432 path => "machine/peripheral/balloon0",
5433 property => "guest-stats-polling-interval",
5434 value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
5437 print "Resumed VM, removing state\n";
5438 if (my $vmstate = $conf->{vmstate}) {
5439 PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
5440 PVE::Storage::vdisk_free($storecfg, $vmstate);
5442 delete $conf->@{qw(lock vmstate runningmachine runningcpu)};
5443 PVE
::QemuConfig-
>write_config($vmid, $conf);
5446 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'post-start');
5451 sub vm_commandline
{
5452 my ($storecfg, $vmid, $snapname) = @_;
5454 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5459 my $snapshot = $conf->{snapshots
}->{$snapname};
5460 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
5462 # check for machine or CPU overrides in snapshot
5463 $forcemachine = $snapshot->{runningmachine
};
5464 $forcecpu = $snapshot->{runningcpu
};
5466 $snapshot->{digest
} = $conf->{digest
}; # keep file digest for API
5471 my $defaults = load_defaults
();
5473 my $cmd = config_to_command
($storecfg, $vmid, $conf, $defaults,
5474 $forcemachine, $forcecpu);
5476 return PVE
::Tools
::cmd2string
($cmd);
5480 my ($vmid, $skiplock) = @_;
5482 PVE
::QemuConfig-
>lock_config($vmid, sub {
5484 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5486 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5488 mon_cmd
($vmid, "system_reset");
5492 sub get_vm_volumes
{
5496 foreach_volid
($conf, sub {
5497 my ($volid, $attr) = @_;
5499 return if $volid =~ m
|^/|;
5501 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
5504 push @$vollist, $volid;
5510 sub vm_stop_cleanup
{
5511 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
5516 my $vollist = get_vm_volumes
($conf);
5517 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
5520 foreach my $ext (qw(mon qmp pid vnc qga)) {
5521 unlink "/var/run/qemu-server/${vmid}.$ext";
5524 if ($conf->{ivshmem
}) {
5525 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
5526 # just delete it for now, VMs which have this already open do not
5527 # are affected, but new VMs will get a separated one. If this
5528 # becomes an issue we either add some sort of ref-counting or just
5529 # add a "don't delete on stop" flag to the ivshmem format.
5530 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name
} // $vmid);
5533 foreach my $key (keys %$conf) {
5534 next if $key !~ m/^hostpci(\d+)$/;
5535 my $hostpciindex = $1;
5536 my $d = parse_hostpci
($conf->{$key});
5537 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $hostpciindex);
5539 foreach my $pci (@{$d->{pciid
}}) {
5540 my $pciid = $pci->{id
};
5541 PVE
::SysFSTools
::pci_cleanup_mdev_device
($pciid, $uuid);
5545 vmconfig_apply_pending
($vmid, $conf, $storecfg) if $apply_pending_changes;
5547 warn $@ if $@; # avoid errors - just warn
5550 # call only in locked context
5552 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
5554 my $pid = check_running
($vmid, $nocheck);
5559 $conf = PVE
::QemuConfig-
>load_config($vmid);
5560 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5561 if (!defined($timeout) && $shutdown && $conf->{startup
}) {
5562 my $opts = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
5563 $timeout = $opts->{down
} if $opts->{down
};
5565 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-stop');
5570 if (defined($conf) && get_qga_key
($conf, 'enabled')) {
5571 mon_cmd
($vmid, "guest-shutdown", timeout
=> $timeout);
5573 mon_cmd
($vmid, "system_powerdown");
5576 mon_cmd
($vmid, "quit");
5582 $timeout = 60 if !defined($timeout);
5585 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
5590 if ($count >= $timeout) {
5592 warn "VM still running - terminating now with SIGTERM\n";
5595 die "VM quit/powerdown failed - got timeout\n";
5598 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5602 if (!check_running
($vmid, $nocheck)) {
5603 warn "Unexpected: VM shutdown command failed, but VM not running anymore..\n";
5607 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
5610 die "VM quit/powerdown failed\n";
5618 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
5623 if ($count >= $timeout) {
5624 warn "VM still running - terminating now with SIGKILL\n";
5629 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5632 # Note: use $nocheck to skip tests if VM configuration file exists.
5633 # We need that when migration VMs to other nodes (files already moved)
5634 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
5636 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
5638 $force = 1 if !defined($force) && !$shutdown;
5641 my $pid = check_running
($vmid, $nocheck, $migratedfrom);
5642 kill 15, $pid if $pid;
5643 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migratedfrom);
5644 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 0);
5648 PVE
::QemuConfig-
>lock_config($vmid, sub {
5649 _do_vm_stop
($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
5654 my ($vmid, $timeout) = @_;
5656 PVE
::QemuConfig-
>lock_config($vmid, sub {
5659 # only reboot if running, as qmeventd starts it again on a stop event
5660 return if !check_running
($vmid);
5662 create_reboot_request
($vmid);
5664 my $storecfg = PVE
::Storage
::config
();
5665 _do_vm_stop
($storecfg, $vmid, undef, undef, $timeout, 1);
5669 # avoid that the next normal shutdown will be confused for a reboot
5670 clear_reboot_request
($vmid);
5676 # note: if using the statestorage parameter, the caller has to check privileges
5678 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
5685 PVE
::QemuConfig-
>lock_config($vmid, sub {
5687 $conf = PVE
::QemuConfig-
>load_config($vmid);
5689 my $is_backing_up = PVE
::QemuConfig-
>has_lock($conf, 'backup');
5690 PVE
::QemuConfig-
>check_lock($conf)
5691 if !($skiplock || $is_backing_up);
5693 die "cannot suspend to disk during backup\n"
5694 if $is_backing_up && $includestate;
5696 if ($includestate) {
5697 $conf->{lock} = 'suspending';
5698 my $date = strftime
("%Y-%m-%d", localtime(time()));
5699 $storecfg = PVE
::Storage
::config
();
5700 if (!$statestorage) {
5701 $statestorage = find_vmstate_storage
($conf, $storecfg);
5702 # check permissions for the storage
5703 my $rpcenv = PVE
::RPCEnvironment
::get
();
5704 if ($rpcenv->{type
} ne 'cli') {
5705 my $authuser = $rpcenv->get_user();
5706 $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
5711 $vmstate = PVE
::QemuConfig-
>__snapshot_save_vmstate(
5712 $vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
5713 $path = PVE
::Storage
::path
($storecfg, $vmstate);
5714 PVE
::QemuConfig-
>write_config($vmid, $conf);
5716 mon_cmd
($vmid, "stop");
5720 if ($includestate) {
5722 PVE
::Storage
::activate_volumes
($storecfg, [$vmstate]);
5725 set_migration_caps
($vmid, 1);
5726 mon_cmd
($vmid, "savevm-start", statefile
=> $path);
5728 my $state = mon_cmd
($vmid, "query-savevm");
5729 if (!$state->{status
}) {
5730 die "savevm not active\n";
5731 } elsif ($state->{status
} eq 'active') {
5734 } elsif ($state->{status
} eq 'completed') {
5735 print "State saved, quitting\n";
5737 } elsif ($state->{status
} eq 'failed' && $state->{error
}) {
5738 die "query-savevm failed with error '$state->{error}'\n"
5740 die "query-savevm returned status '$state->{status}'\n";
5746 PVE
::QemuConfig-
>lock_config($vmid, sub {
5747 $conf = PVE
::QemuConfig-
>load_config($vmid);
5749 # cleanup, but leave suspending lock, to indicate something went wrong
5751 mon_cmd
($vmid, "savevm-end");
5752 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
5753 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
5754 delete $conf->@{qw(vmstate runningmachine runningcpu)};
5755 PVE
::QemuConfig-
>write_config($vmid, $conf);
5761 die "lock changed unexpectedly\n"
5762 if !PVE
::QemuConfig-
>has_lock($conf, 'suspending');
5764 mon_cmd
($vmid, "quit");
5765 $conf->{lock} = 'suspended';
5766 PVE
::QemuConfig-
>write_config($vmid, $conf);
5772 my ($vmid, $skiplock, $nocheck) = @_;
5774 PVE
::QemuConfig-
>lock_config($vmid, sub {
5775 my $res = mon_cmd
($vmid, 'query-status');
5776 my $resume_cmd = 'cont';
5779 if ($res->{status
}) {
5780 return if $res->{status
} eq 'running'; # job done, go home
5781 $resume_cmd = 'system_wakeup' if $res->{status
} eq 'suspended';
5782 $reset = 1 if $res->{status
} eq 'shutdown';
5787 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5789 PVE
::QemuConfig-
>check_lock($conf)
5790 if !($skiplock || PVE
::QemuConfig-
>has_lock($conf, 'backup'));
5794 # required if a VM shuts down during a backup and we get a resume
5795 # request before the backup finishes for example
5796 mon_cmd
($vmid, "system_reset");
5798 mon_cmd
($vmid, $resume_cmd);
5803 my ($vmid, $skiplock, $key) = @_;
5805 PVE
::QemuConfig-
>lock_config($vmid, sub {
5807 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5809 # there is no qmp command, so we use the human monitor command
5810 my $res = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "sendkey $key");
5811 die $res if $res ne '';
5815 # vzdump restore implementaion
5817 sub tar_archive_read_firstfile
{
5818 my $archive = shift;
5820 die "ERROR: file '$archive' does not exist\n" if ! -f
$archive;
5822 # try to detect archive type first
5823 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
5824 die "unable to open file '$archive'\n";
5825 my $firstfile = <$fh>;
5829 die "ERROR: archive contaions no data\n" if !$firstfile;
5835 sub tar_restore_cleanup
{
5836 my ($storecfg, $statfile) = @_;
5838 print STDERR
"starting cleanup\n";
5840 if (my $fd = IO
::File-
>new($statfile, "r")) {
5841 while (defined(my $line = <$fd>)) {
5842 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
5845 if ($volid =~ m
|^/|) {
5846 unlink $volid || die 'unlink failed\n';
5848 PVE
::Storage
::vdisk_free
($storecfg, $volid);
5850 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
5852 print STDERR
"unable to cleanup '$volid' - $@" if $@;
5854 print STDERR
"unable to parse line in statfile - $line";
5861 sub restore_file_archive
{
5862 my ($archive, $vmid, $user, $opts) = @_;
5864 return restore_vma_archive
($archive, $vmid, $user, $opts)
5867 my $info = PVE
::Storage
::archive_info
($archive);
5868 my $format = $opts->{format
} // $info->{format
};
5869 my $comp = $info->{compression
};
5871 # try to detect archive format
5872 if ($format eq 'tar') {
5873 return restore_tar_archive
($archive, $vmid, $user, $opts);
5875 return restore_vma_archive
($archive, $vmid, $user, $opts, $comp);
5879 # hepler to remove disks that will not be used after restore
5880 my $restore_cleanup_oldconf = sub {
5881 my ($storecfg, $vmid, $oldconf, $virtdev_hash) = @_;
5883 PVE
::QemuConfig-
>foreach_volume($oldconf, sub {
5884 my ($ds, $drive) = @_;
5886 return if drive_is_cdrom
($drive, 1);
5888 my $volid = $drive->{file
};
5889 return if !$volid || $volid =~ m
|^/|;
5891 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
5892 return if !$path || !$owner || ($owner != $vmid);
5894 # Note: only delete disk we want to restore
5895 # other volumes will become unused
5896 if ($virtdev_hash->{$ds}) {
5897 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
5904 # delete vmstate files, after the restore we have no snapshots anymore
5905 foreach my $snapname (keys %{$oldconf->{snapshots
}}) {
5906 my $snap = $oldconf->{snapshots
}->{$snapname};
5907 if ($snap->{vmstate
}) {
5908 eval { PVE
::Storage
::vdisk_free
($storecfg, $snap->{vmstate
}); };
5916 # Helper to parse vzdump backup device hints
5918 # $rpcenv: Environment, used to ckeck storage permissions
5919 # $user: User ID, to check storage permissions
5920 # $storecfg: Storage configuration
5921 # $fh: the file handle for reading the configuration
5922 # $devinfo: should contain device sizes for all backu-up'ed devices
5923 # $options: backup options (pool, default storage)
5925 # Return: $virtdev_hash, updates $devinfo (add devname, virtdev, format, storeid)
5926 my $parse_backup_hints = sub {
5927 my ($rpcenv, $user, $storecfg, $fh, $devinfo, $options) = @_;
5929 my $virtdev_hash = {};
5931 while (defined(my $line = <$fh>)) {
5932 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
5933 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
5934 die "archive does not contain data for drive '$virtdev'\n"
5935 if !$devinfo->{$devname};
5937 if (defined($options->{storage
})) {
5938 $storeid = $options->{storage
} || 'local';
5939 } elsif (!$storeid) {
5942 $format = 'raw' if !$format;
5943 $devinfo->{$devname}->{devname
} = $devname;
5944 $devinfo->{$devname}->{virtdev
} = $virtdev;
5945 $devinfo->{$devname}->{format
} = $format;
5946 $devinfo->{$devname}->{storeid
} = $storeid;
5948 # check permission on storage
5949 my $pool = $options->{pool
}; # todo: do we need that?
5950 if ($user ne 'root@pam') {
5951 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
5954 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
5955 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
5957 my $drive = parse_drive
($virtdev, $2);
5958 if (drive_is_cloudinit
($drive)) {
5959 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
5960 $storeid = $options->{storage
} if defined ($options->{storage
});
5961 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
5962 my $format = qemu_img_format
($scfg, $volname); # has 'raw' fallback
5964 $virtdev_hash->{$virtdev} = {
5966 storeid
=> $storeid,
5967 size
=> PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
,
5974 return $virtdev_hash;
5977 # Helper to allocate and activate all volumes required for a restore
5979 # $storecfg: Storage configuration
5980 # $virtdev_hash: as returned by parse_backup_hints()
5982 # Returns: { $virtdev => $volid }
5983 my $restore_allocate_devices = sub {
5984 my ($storecfg, $virtdev_hash, $vmid) = @_;
5987 foreach my $virtdev (sort keys %$virtdev_hash) {
5988 my $d = $virtdev_hash->{$virtdev};
5989 my $alloc_size = int(($d->{size
} + 1024 - 1)/1024);
5990 my $storeid = $d->{storeid
};
5991 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
5993 # test if requested format is supported
5994 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
5995 my $supported = grep { $_ eq $d->{format
} } @$validFormats;
5996 $d->{format
} = $defFormat if !$supported;
5999 if ($d->{is_cloudinit
}) {
6000 $name = "vm-$vmid-cloudinit";
6001 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6002 if ($scfg->{path
}) {
6003 $name .= ".$d->{format}";
6007 my $volid = PVE
::Storage
::vdisk_alloc
(
6008 $storecfg, $storeid, $vmid, $d->{format
}, $name, $alloc_size);
6010 print STDERR
"new volume ID is '$volid'\n";
6011 $d->{volid
} = $volid;
6013 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
6015 $map->{$virtdev} = $volid;
6021 sub restore_update_config_line
{
6022 my ($cookie, $map, $line, $unique) = @_;
6024 return '' if $line =~ m/^\#qmdump\#/;
6025 return '' if $line =~ m/^\#vzdump\#/;
6026 return '' if $line =~ m/^lock:/;
6027 return '' if $line =~ m/^unused\d+:/;
6028 return '' if $line =~ m/^parent:/;
6032 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
6033 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
6034 # try to convert old 1.X settings
6035 my ($id, $ind, $ethcfg) = ($1, $2, $3);
6036 foreach my $devconfig (PVE
::Tools
::split_list
($ethcfg)) {
6037 my ($model, $macaddr) = split(/\=/, $devconfig);
6038 $macaddr = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if !$macaddr || $unique;
6041 bridge
=> "vmbr$ind",
6042 macaddr
=> $macaddr,
6044 my $netstr = print_net
($net);
6046 $res .= "net$cookie->{netcount}: $netstr\n";
6047 $cookie->{netcount
}++;
6049 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
6050 my ($id, $netstr) = ($1, $2);
6051 my $net = parse_net
($netstr);
6052 $net->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if $net->{macaddr
};
6053 $netstr = print_net
($net);
6054 $res .= "$id: $netstr\n";
6055 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk)\d+):\s*(\S+)\s*$/) {
6058 my $di = parse_drive
($virtdev, $value);
6059 if (defined($di->{backup
}) && !$di->{backup
}) {
6061 } elsif ($map->{$virtdev}) {
6062 delete $di->{format
}; # format can change on restore
6063 $di->{file
} = $map->{$virtdev};
6064 $value = print_drive
($di);
6065 $res .= "$virtdev: $value\n";
6069 } elsif (($line =~ m/^vmgenid: (.*)/)) {
6071 if ($vmgenid ne '0') {
6072 # always generate a new vmgenid if there was a valid one setup
6073 $vmgenid = generate_uuid
();
6075 $res .= "vmgenid: $vmgenid\n";
6076 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
6077 my ($uuid, $uuid_str);
6078 UUID
::generate
($uuid);
6079 UUID
::unparse
($uuid, $uuid_str);
6080 my $smbios1 = parse_smbios1
($2);
6081 $smbios1->{uuid
} = $uuid_str;
6082 $res .= $1.print_smbios1
($smbios1)."\n";
6090 my $restore_deactivate_volumes = sub {
6091 my ($storecfg, $devinfo) = @_;
6094 foreach my $devname (keys %$devinfo) {
6095 my $volid = $devinfo->{$devname}->{volid
};
6096 push @$vollist, $volid if $volid;
6099 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
6102 my $restore_destroy_volumes = sub {
6103 my ($storecfg, $devinfo) = @_;
6105 foreach my $devname (keys %$devinfo) {
6106 my $volid = $devinfo->{$devname}->{volid
};
6109 if ($volid =~ m
|^/|) {
6110 unlink $volid || die 'unlink failed\n';
6112 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6114 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6116 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6121 my ($cfg, $vmid) = @_;
6123 my $info = PVE
::Storage
::vdisk_list
($cfg, undef, $vmid, undef, 'images');
6125 my $volid_hash = {};
6126 foreach my $storeid (keys %$info) {
6127 foreach my $item (@{$info->{$storeid}}) {
6128 next if !($item->{volid
} && $item->{size
});
6129 $item->{path
} = PVE
::Storage
::path
($cfg, $item->{volid
});
6130 $volid_hash->{$item->{volid
}} = $item;
6137 sub update_disk_config
{
6138 my ($vmid, $conf, $volid_hash) = @_;
6141 my $prefix = "VM $vmid";
6143 # used and unused disks
6144 my $referenced = {};
6146 # Note: it is allowed to define multiple storages with same path (alias), so
6147 # we need to check both 'volid' and real 'path' (two different volid can point
6148 # to the same path).
6150 my $referencedpath = {};
6153 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6154 my ($opt, $drive) = @_;
6156 my $volid = $drive->{file
};
6158 my $volume = $volid_hash->{$volid};
6160 # mark volid as "in-use" for next step
6161 $referenced->{$volid} = 1;
6162 if ($volume && (my $path = $volume->{path
})) {
6163 $referencedpath->{$path} = 1;
6166 return if drive_is_cdrom
($drive);
6169 my ($updated, $msg) = PVE
::QemuServer
::Drive
::update_disksize
($drive, $volume->{size
});
6170 if (defined($updated)) {
6172 $conf->{$opt} = print_drive
($updated);
6173 print "$prefix ($opt): $msg\n";
6177 # remove 'unusedX' entry if volume is used
6178 PVE
::QemuConfig-
>foreach_unused_volume($conf, sub {
6179 my ($opt, $drive) = @_;
6181 my $volid = $drive->{file
};
6185 $path = $volid_hash->{$volid}->{path
} if $volid_hash->{$volid};
6186 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6187 print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
6189 delete $conf->{$opt};
6192 $referenced->{$volid} = 1;
6193 $referencedpath->{$path} = 1 if $path;
6196 foreach my $volid (sort keys %$volid_hash) {
6197 next if $volid =~ m/vm-$vmid-state-/;
6198 next if $referenced->{$volid};
6199 my $path = $volid_hash->{$volid}->{path
};
6200 next if !$path; # just to be sure
6201 next if $referencedpath->{$path};
6203 my $key = PVE
::QemuConfig-
>add_unused_volume($conf, $volid);
6204 print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
6205 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6212 my ($vmid, $nolock, $dryrun) = @_;
6214 my $cfg = PVE
::Storage
::config
();
6216 print "rescan volumes...\n";
6217 my $volid_hash = scan_volids
($cfg, $vmid);
6219 my $updatefn = sub {
6222 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6224 PVE
::QemuConfig-
>check_lock($conf);
6227 foreach my $volid (keys %$volid_hash) {
6228 my $info = $volid_hash->{$volid};
6229 $vm_volids->{$volid} = $info if $info->{vmid
} && $info->{vmid
} == $vmid;
6232 my $changes = update_disk_config
($vmid, $conf, $vm_volids);
6234 PVE
::QemuConfig-
>write_config($vmid, $conf) if $changes && !$dryrun;
6237 if (defined($vmid)) {
6241 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6244 my $vmlist = config_list
();
6245 foreach my $vmid (keys %$vmlist) {
6249 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6255 sub restore_proxmox_backup_archive
{
6256 my ($archive, $vmid, $user, $options) = @_;
6258 my $storecfg = PVE
::Storage
::config
();
6260 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($archive);
6261 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6263 my $fingerprint = $scfg->{fingerprint
};
6264 my $keyfile = PVE
::Storage
::PBSPlugin
::pbs_encryption_key_file_name
($storecfg, $storeid);
6266 my $repo = PVE
::PBSClient
::get_repository
($scfg);
6268 # This is only used for `pbs-restore` and the QEMU PBS driver (live-restore)
6269 my $password = PVE
::Storage
::PBSPlugin
::pbs_get_password
($scfg, $storeid);
6270 local $ENV{PBS_PASSWORD
} = $password;
6271 local $ENV{PBS_FINGERPRINT
} = $fingerprint if defined($fingerprint);
6273 my ($vtype, $pbs_backup_name, undef, undef, undef, undef, $format) =
6274 PVE
::Storage
::parse_volname
($storecfg, $archive);
6276 die "got unexpected vtype '$vtype'\n" if $vtype ne 'backup';
6278 die "got unexpected backup format '$format'\n" if $format ne 'pbs-vm';
6280 my $tmpdir = "/var/tmp/vzdumptmp$$";
6284 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6285 # disable interrupts (always do cleanups)
6289 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
6291 # Note: $oldconf is undef if VM does not exists
6292 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
6293 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
6294 my $new_conf_raw = '';
6296 my $rpcenv = PVE
::RPCEnvironment
::get
();
6305 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6307 my $cfgfn = "$tmpdir/qemu-server.conf";
6308 my $firewall_config_fn = "$tmpdir/fw.conf";
6309 my $index_fn = "$tmpdir/index.json";
6311 my $cmd = "restore";
6313 my $param = [$pbs_backup_name, "index.json", $index_fn];
6314 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6315 my $index = PVE
::Tools
::file_get_contents
($index_fn);
6316 $index = decode_json
($index);
6318 # print Dumper($index);
6319 foreach my $info (@{$index->{files
}}) {
6320 if ($info->{filename
} =~ m/^(drive-\S+).img.fidx$/) {
6322 if ($info->{size
} =~ m/^(\d+)$/) { # untaint size
6323 $devinfo->{$devname}->{size
} = $1;
6325 die "unable to parse file size in 'index.json' - got '$info->{size}'\n";
6330 my $is_qemu_server_backup = scalar(
6331 grep { $_->{filename
} eq 'qemu-server.conf.blob' } @{$index->{files
}}
6333 if (!$is_qemu_server_backup) {
6334 die "backup does not look like a qemu-server backup (missing 'qemu-server.conf' file)\n";
6336 my $has_firewall_config = scalar(grep { $_->{filename
} eq 'fw.conf.blob' } @{$index->{files
}});
6338 $param = [$pbs_backup_name, "qemu-server.conf", $cfgfn];
6339 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6341 if ($has_firewall_config) {
6342 $param = [$pbs_backup_name, "fw.conf", $firewall_config_fn];
6343 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6345 my $pve_firewall_dir = '/etc/pve/firewall';
6346 mkdir $pve_firewall_dir; # make sure the dir exists
6347 PVE
::Tools
::file_copy
($firewall_config_fn, "${pve_firewall_dir}/$vmid.fw");
6350 my $fh = IO
::File-
>new($cfgfn, "r") ||
6351 die "unable to read qemu-server.conf - $!\n";
6353 my $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $storecfg, $fh, $devinfo, $options);
6355 # fixme: rate limit?
6357 # create empty/temp config
6358 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\nlock: create");
6360 $restore_cleanup_oldconf->($storecfg, $vmid, $oldconf, $virtdev_hash) if $oldconf;
6363 my $map = $restore_allocate_devices->($storecfg, $virtdev_hash, $vmid);
6365 if (!$options->{live
}) {
6366 foreach my $virtdev (sort keys %$virtdev_hash) {
6367 my $d = $virtdev_hash->{$virtdev};
6368 next if $d->{is_cloudinit
}; # no need to restore cloudinit
6370 my $volid = $d->{volid
};
6372 my $path = PVE
::Storage
::path
($storecfg, $volid);
6374 my $pbs_restore_cmd = [
6375 '/usr/bin/pbs-restore',
6376 '--repository', $repo,
6378 "$d->{devname}.img.fidx",
6383 push @$pbs_restore_cmd, '--format', $d->{format
} if $d->{format
};
6384 push @$pbs_restore_cmd, '--keyfile', $keyfile if -e
$keyfile;
6386 if (PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $volid)) {
6387 push @$pbs_restore_cmd, '--skip-zero';
6390 my $dbg_cmdstring = PVE
::Tools
::cmd2string
($pbs_restore_cmd);
6391 print "restore proxmox backup image: $dbg_cmdstring\n";
6392 run_command
($pbs_restore_cmd);
6396 $fh->seek(0, 0) || die "seek failed - $!\n";
6398 my $cookie = { netcount
=> 0 };
6399 while (defined(my $line = <$fh>)) {
6400 $new_conf_raw .= restore_update_config_line
(
6412 if ($err || !$options->{live
}) {
6413 $restore_deactivate_volumes->($storecfg, $devinfo);
6419 $restore_destroy_volumes->($storecfg, $devinfo);
6423 if ($options->{live
}) {
6424 # keep lock during live-restore
6425 $new_conf_raw .= "\nlock: create";
6428 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
6430 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6432 eval { rescan
($vmid, 1); };
6435 PVE
::AccessControl
::add_vm_to_pool
($vmid, $options->{pool
}) if $options->{pool
};
6437 if ($options->{live
}) {
6443 local $SIG{PIPE
} = sub { die "got signal ($!) - abort\n"; };
6445 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6446 die "cannot do live-restore for template\n" if PVE
::QemuConfig-
>is_template($conf);
6448 pbs_live_restore
($vmid, $conf, $storecfg, $devinfo, $repo, $keyfile, $pbs_backup_name);
6450 PVE
::QemuConfig-
>remove_lock($vmid, "create");
6454 sub pbs_live_restore
{
6455 my ($vmid, $conf, $storecfg, $restored_disks, $repo, $keyfile, $snap) = @_;
6457 print "starting VM for live-restore\n";
6458 print "repository: '$repo', snapshot: '$snap'\n";
6460 my $pbs_backing = {};
6461 for my $ds (keys %$restored_disks) {
6462 $ds =~ m/^drive-(.*)$/;
6464 $pbs_backing->{$confname} = {
6465 repository
=> $repo,
6467 archive
=> "$ds.img.fidx",
6469 $pbs_backing->{$confname}->{keyfile
} = $keyfile if -e
$keyfile;
6471 my $drive = parse_drive
($confname, $conf->{$confname});
6472 print "restoring '$ds' to '$drive->{file}'\n";
6475 my $drives_streamed = 0;
6477 # make sure HA doesn't interrupt our restore by stopping the VM
6478 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid)) {
6479 run_command
(['ha-manager', 'set', "vm:$vmid", '--state', 'started']);
6482 # start VM with backing chain pointing to PBS backup, environment vars for PBS driver
6483 # in QEMU (PBS_PASSWORD and PBS_FINGERPRINT) are already set by our caller
6484 vm_start_nolock
($storecfg, $vmid, $conf, {paused
=> 1, 'pbs-backing' => $pbs_backing}, {});
6486 my $qmeventd_fd = register_qmeventd_handle
($vmid);
6488 # begin streaming, i.e. data copy from PBS to target disk for every vol,
6489 # this will effectively collapse the backing image chain consisting of
6490 # [target <- alloc-track -> PBS snapshot] to just [target] (alloc-track
6491 # removes itself once all backing images vanish with 'auto-remove=on')
6493 for my $ds (sort keys %$restored_disks) {
6494 my $job_id = "restore-$ds";
6495 mon_cmd
($vmid, 'block-stream',
6496 'job-id' => $job_id,
6499 $jobs->{$job_id} = {};
6502 mon_cmd
($vmid, 'cont');
6503 qemu_drive_mirror_monitor
($vmid, undef, $jobs, 'auto', 0, 'stream');
6505 print "restore-drive jobs finished successfully, removing all tracking block devices"
6506 ." to disconnect from Proxmox Backup Server\n";
6508 for my $ds (sort keys %$restored_disks) {
6509 mon_cmd
($vmid, 'blockdev-del', 'node-name' => "$ds-pbs");
6512 close($qmeventd_fd);
6518 warn "An error occured during live-restore: $err\n";
6519 _do_vm_stop
($storecfg, $vmid, 1, 1, 10, 0, 1);
6520 die "live-restore failed\n";
6524 sub restore_vma_archive
{
6525 my ($archive, $vmid, $user, $opts, $comp) = @_;
6527 my $readfrom = $archive;
6529 my $cfg = PVE
::Storage
::config
();
6531 my $bwlimit = $opts->{bwlimit
};
6533 my $dbg_cmdstring = '';
6534 my $add_pipe = sub {
6536 push @$commands, $cmd;
6537 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
6538 $dbg_cmdstring .= PVE
::Tools
::cmd2string
($cmd);
6543 if ($archive eq '-') {
6546 # If we use a backup from a PVE defined storage we also consider that
6547 # storage's rate limit:
6548 my (undef, $volid) = PVE
::Storage
::path_to_volume_id
($cfg, $archive);
6549 if (defined($volid)) {
6550 my ($sid, undef) = PVE
::Storage
::parse_volume_id
($volid);
6551 my $readlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [$sid], $bwlimit);
6553 print STDERR
"applying read rate limit: $readlimit\n";
6554 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
6555 $add_pipe->($cstream);
6561 my $info = PVE
::Storage
::decompressor_info
('vma', $comp);
6562 my $cmd = $info->{decompressor
};
6563 push @$cmd, $readfrom;
6567 my $tmpdir = "/var/tmp/vzdumptmp$$";
6570 # disable interrupts (always do cleanups)
6574 local $SIG{HUP
} = sub { warn "got interrupt - ignored\n"; };
6576 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
6577 POSIX
::mkfifo
($mapfifo, 0600);
6579 my $openfifo = sub { open($fifofh, '>', $mapfifo) or die $! };
6581 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
6588 my $rpcenv = PVE
::RPCEnvironment
::get
();
6590 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6592 # Note: $oldconf is undef if VM does not exist
6593 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
6594 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
6595 my $new_conf_raw = '';
6599 my $print_devmap = sub {
6600 my $cfgfn = "$tmpdir/qemu-server.conf";
6602 # we can read the config - that is already extracted
6603 my $fh = IO
::File-
>new($cfgfn, "r") ||
6604 die "unable to read qemu-server.conf - $!\n";
6606 my $fwcfgfn = "$tmpdir/qemu-server.fw";
6608 my $pve_firewall_dir = '/etc/pve/firewall';
6609 mkdir $pve_firewall_dir; # make sure the dir exists
6610 PVE
::Tools
::file_copy
($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
6613 my $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $cfg, $fh, $devinfo, $opts);
6615 foreach my $info (values %{$virtdev_hash}) {
6616 my $storeid = $info->{storeid
};
6617 next if defined($storage_limits{$storeid});
6619 my $limit = PVE
::Storage
::get_bandwidth_limit
('restore', [$storeid], $bwlimit) // 0;
6620 print STDERR
"rate limit for storage $storeid: $limit KiB/s\n" if $limit;
6621 $storage_limits{$storeid} = $limit * 1024;
6624 foreach my $devname (keys %$devinfo) {
6625 die "found no device mapping information for device '$devname'\n"
6626 if !$devinfo->{$devname}->{virtdev
};
6629 # create empty/temp config
6631 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\n");
6632 $restore_cleanup_oldconf->($cfg, $vmid, $oldconf, $virtdev_hash);
6636 my $map = $restore_allocate_devices->($cfg, $virtdev_hash, $vmid);
6638 # print restore information to $fifofh
6639 foreach my $virtdev (sort keys %$virtdev_hash) {
6640 my $d = $virtdev_hash->{$virtdev};
6641 next if $d->{is_cloudinit
}; # no need to restore cloudinit
6643 my $storeid = $d->{storeid
};
6644 my $volid = $d->{volid
};
6647 if (my $limit = $storage_limits{$storeid}) {
6648 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
6651 my $write_zeros = 1;
6652 if (PVE
::Storage
::volume_has_feature
($cfg, 'sparseinit', $volid)) {
6656 my $path = PVE
::Storage
::path
($cfg, $volid);
6658 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
6660 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
6663 $fh->seek(0, 0) || die "seek failed - $!\n";
6665 my $cookie = { netcount
=> 0 };
6666 while (defined(my $line = <$fh>)) {
6667 $new_conf_raw .= restore_update_config_line
(
6684 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6685 local $SIG{ALRM
} = sub { die "got timeout\n"; };
6687 $oldtimeout = alarm($timeout);
6694 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
6695 my ($dev_id, $size, $devname) = ($1, $2, $3);
6696 $devinfo->{$devname} = { size
=> $size, dev_id
=> $dev_id };
6697 } elsif ($line =~ m/^CTIME: /) {
6698 # we correctly received the vma config, so we can disable
6699 # the timeout now for disk allocation (set to 10 minutes, so
6700 # that we always timeout if something goes wrong)
6703 print $fifofh "done\n";
6704 my $tmp = $oldtimeout || 0;
6705 $oldtimeout = undef;
6712 print "restore vma archive: $dbg_cmdstring\n";
6713 run_command
($commands, input
=> $input, outfunc
=> $parser, afterfork
=> $openfifo);
6717 alarm($oldtimeout) if $oldtimeout;
6719 $restore_deactivate_volumes->($cfg, $devinfo);
6721 close($fifofh) if $fifofh;
6726 $restore_destroy_volumes->($cfg, $devinfo);
6730 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
6732 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6734 eval { rescan
($vmid, 1); };
6737 PVE
::AccessControl
::add_vm_to_pool
($vmid, $opts->{pool
}) if $opts->{pool
};
6740 sub restore_tar_archive
{
6741 my ($archive, $vmid, $user, $opts) = @_;
6743 if ($archive ne '-') {
6744 my $firstfile = tar_archive_read_firstfile
($archive);
6745 die "ERROR: file '$archive' does not look like a QemuServer vzdump backup\n"
6746 if $firstfile ne 'qemu-server.conf';
6749 my $storecfg = PVE
::Storage
::config
();
6751 # avoid zombie disks when restoring over an existing VM -> cleanup first
6752 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
6753 # skiplock=1 because qmrestore has set the 'create' lock itself already
6754 my $vmcfgfn = PVE
::QemuConfig-
>config_file($vmid);
6755 destroy_vm
($storecfg, $vmid, 1, { lock => 'restore' }) if -f
$vmcfgfn;
6757 my $tocmd = "/usr/lib/qemu-server/qmextract";
6759 $tocmd .= " --storage " . PVE
::Tools
::shellquote
($opts->{storage
}) if $opts->{storage
};
6760 $tocmd .= " --pool " . PVE
::Tools
::shellquote
($opts->{pool
}) if $opts->{pool
};
6761 $tocmd .= ' --prealloc' if $opts->{prealloc
};
6762 $tocmd .= ' --info' if $opts->{info
};
6764 # tar option "xf" does not autodetect compression when read from STDIN,
6765 # so we pipe to zcat
6766 my $cmd = "zcat -f|tar xf " . PVE
::Tools
::shellquote
($archive) . " " .
6767 PVE
::Tools
::shellquote
("--to-command=$tocmd");
6769 my $tmpdir = "/var/tmp/vzdumptmp$$";
6772 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
6773 local $ENV{VZDUMP_VMID
} = $vmid;
6774 local $ENV{VZDUMP_USER
} = $user;
6776 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6777 my $new_conf_raw = '';
6779 # disable interrupts (always do cleanups)
6783 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
6791 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6793 if ($archive eq '-') {
6794 print "extracting archive from STDIN\n";
6795 run_command
($cmd, input
=> "<&STDIN");
6797 print "extracting archive '$archive'\n";
6801 return if $opts->{info
};
6805 my $statfile = "$tmpdir/qmrestore.stat";
6806 if (my $fd = IO
::File-
>new($statfile, "r")) {
6807 while (defined (my $line = <$fd>)) {
6808 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6809 $map->{$1} = $2 if $1;
6811 print STDERR
"unable to parse line in statfile - $line\n";
6817 my $confsrc = "$tmpdir/qemu-server.conf";
6819 my $srcfd = IO
::File-
>new($confsrc, "r") || die "unable to open file '$confsrc'\n";
6821 my $cookie = { netcount
=> 0 };
6822 while (defined (my $line = <$srcfd>)) {
6823 $new_conf_raw .= restore_update_config_line
(
6834 tar_restore_cleanup
($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info
};
6840 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
6842 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6844 eval { rescan
($vmid, 1); };
6848 sub foreach_storage_used_by_vm
{
6849 my ($conf, $func) = @_;
6853 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6854 my ($ds, $drive) = @_;
6855 return if drive_is_cdrom
($drive);
6857 my $volid = $drive->{file
};
6859 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
6860 $sidhash->{$sid} = $sid if $sid;
6863 foreach my $sid (sort keys %$sidhash) {
6868 my $qemu_snap_storage = {
6871 sub do_snapshots_with_qemu
{
6872 my ($storecfg, $volid) = @_;
6874 my $storage_name = PVE
::Storage
::parse_volume_id
($volid);
6875 my $scfg = $storecfg->{ids
}->{$storage_name};
6876 die "could not find storage '$storage_name'\n" if !defined($scfg);
6878 if ($qemu_snap_storage->{$scfg->{type
}} && !$scfg->{krbd
}){
6882 if ($volid =~ m/\.(qcow2|qed)$/){
6889 sub qga_check_running
{
6890 my ($vmid, $nowarn) = @_;
6892 eval { mon_cmd
($vmid, "guest-ping", timeout
=> 3); };
6894 warn "Qemu Guest Agent is not running - $@" if !$nowarn;
6900 sub template_create
{
6901 my ($vmid, $conf, $disk) = @_;
6903 my $storecfg = PVE
::Storage
::config
();
6905 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6906 my ($ds, $drive) = @_;
6908 return if drive_is_cdrom
($drive);
6909 return if $disk && $ds ne $disk;
6911 my $volid = $drive->{file
};
6912 return if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
6914 my $voliddst = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
6915 $drive->{file
} = $voliddst;
6916 $conf->{$ds} = print_drive
($drive);
6917 PVE
::QemuConfig-
>write_config($vmid, $conf);
6921 sub convert_iscsi_path
{
6924 if ($path =~ m
|^iscsi
://([^/]+)/([^/]+)/(.+)$|) {
6929 my $initiator_name = get_initiator_name
();
6931 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
6932 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
6935 die "cannot convert iscsi path '$path', unkown format\n";
6938 sub qemu_img_convert
{
6939 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
6941 my $storecfg = PVE
::Storage
::config
();
6942 my ($src_storeid, $src_volname) = PVE
::Storage
::parse_volume_id
($src_volid, 1);
6943 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid, 1);
6945 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
6949 my $src_is_iscsi = 0;
6953 PVE
::Storage
::activate_volumes
($storecfg, [$src_volid], $snapname);
6954 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
6955 $src_format = qemu_img_format
($src_scfg, $src_volname);
6956 $src_path = PVE
::Storage
::path
($storecfg, $src_volid, $snapname);
6957 $src_is_iscsi = ($src_path =~ m
|^iscsi
://|);
6958 $cachemode = 'none' if $src_scfg->{type
} eq 'zfspool';
6959 } elsif (-f
$src_volid) {
6960 $src_path = $src_volid;
6961 if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
6966 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
6968 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
6969 my $dst_format = qemu_img_format
($dst_scfg, $dst_volname);
6970 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
6971 my $dst_is_iscsi = ($dst_path =~ m
|^iscsi
://|);
6974 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
6975 push @$cmd, '-l', "snapshot.name=$snapname"
6976 if $snapname && $src_format && $src_format eq "qcow2";
6977 push @$cmd, '-t', 'none' if $dst_scfg->{type
} eq 'zfspool';
6978 push @$cmd, '-T', $cachemode if defined($cachemode);
6980 if ($src_is_iscsi) {
6981 push @$cmd, '--image-opts';
6982 $src_path = convert_iscsi_path
($src_path);
6983 } elsif ($src_format) {
6984 push @$cmd, '-f', $src_format;
6987 if ($dst_is_iscsi) {
6988 push @$cmd, '--target-image-opts';
6989 $dst_path = convert_iscsi_path
($dst_path);
6991 push @$cmd, '-O', $dst_format;
6994 push @$cmd, $src_path;
6996 if (!$dst_is_iscsi && $is_zero_initialized) {
6997 push @$cmd, "zeroinit:$dst_path";
6999 push @$cmd, $dst_path;
7004 if($line =~ m/\((\S+)\/100\
%\)/){
7006 my $transferred = int($size * $percent / 100);
7007 my $total_h = render_bytes
($size, 1);
7008 my $transferred_h = render_bytes
($transferred, 1);
7010 print "transferred $transferred_h of $total_h ($percent%)\n";
7015 eval { run_command
($cmd, timeout
=> undef, outfunc
=> $parser); };
7017 die "copy failed: $err" if $err;
7020 sub qemu_img_format
{
7021 my ($scfg, $volname) = @_;
7023 if ($scfg->{path
} && $volname =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7030 sub qemu_drive_mirror
{
7031 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
7033 $jobs = {} if !$jobs;
7037 $jobs->{"drive-$drive"} = {};
7039 if ($dst_volid =~ /^nbd:/) {
7040 $qemu_target = $dst_volid;
7043 my $storecfg = PVE
::Storage
::config
();
7044 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid);
7046 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7048 $format = qemu_img_format
($dst_scfg, $dst_volname);
7050 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7052 $qemu_target = $is_zero_initialized ?
"zeroinit:$dst_path" : $dst_path;
7055 my $opts = { timeout
=> 10, device
=> "drive-$drive", mode
=> "existing", sync
=> "full", target
=> $qemu_target };
7056 $opts->{format
} = $format if $format;
7058 if (defined($src_bitmap)) {
7059 $opts->{sync
} = 'incremental';
7060 $opts->{bitmap
} = $src_bitmap;
7061 print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
7064 if (defined($bwlimit)) {
7065 $opts->{speed
} = $bwlimit * 1024;
7066 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
7068 print "drive mirror is starting for drive-$drive\n";
7071 # if a job already runs for this device we get an error, catch it for cleanup
7072 eval { mon_cmd
($vmid, "drive-mirror", %$opts); };
7074 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7076 die "mirroring error: $err\n";
7079 qemu_drive_mirror_monitor
($vmid, $vmiddst, $jobs, $completion, $qga);
7082 # $completion can be either
7083 # 'complete': wait until all jobs are ready, block-job-complete them (default)
7084 # 'cancel': wait until all jobs are ready, block-job-cancel them
7085 # 'skip': wait until all jobs are ready, return with block jobs in ready state
7086 # 'auto': wait until all jobs disappear, only use for jobs which complete automatically
7087 sub qemu_drive_mirror_monitor
{
7088 my ($vmid, $vmiddst, $jobs, $completion, $qga, $op) = @_;
7090 $completion //= 'complete';
7094 my $err_complete = 0;
7096 my $starttime = time ();
7098 die "block job ('$op') timed out\n" if $err_complete > 300;
7100 my $stats = mon_cmd
($vmid, "query-block-jobs");
7103 my $running_jobs = {};
7104 for my $stat (@$stats) {
7105 next if $stat->{type
} ne $op;
7106 $running_jobs->{$stat->{device
}} = $stat;
7109 my $readycounter = 0;
7111 for my $job_id (sort keys %$jobs) {
7112 my $job = $running_jobs->{$job_id};
7114 my $vanished = !defined($job);
7115 my $complete = defined($jobs->{$job_id}->{complete
}) && $vanished;
7116 if($complete || ($vanished && $completion eq 'auto')) {
7117 print "$job_id: $op-job finished\n";
7118 delete $jobs->{$job_id};
7122 die "$job_id: '$op' has been cancelled\n" if !defined($job);
7124 my $busy = $job->{busy
};
7125 my $ready = $job->{ready
};
7126 if (my $total = $job->{len
}) {
7127 my $transferred = $job->{offset
} || 0;
7128 my $remaining = $total - $transferred;
7129 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
7131 my $duration = $ctime - $starttime;
7132 my $total_h = render_bytes
($total, 1);
7133 my $transferred_h = render_bytes
($transferred, 1);
7135 my $status = sprintf(
7136 "transferred $transferred_h of $total_h ($percent%%) in %s",
7137 render_duration
($duration),
7142 $status .= ", still busy"; # shouldn't even happen? but mirror is weird
7144 $status .= ", ready";
7147 print "$job_id: $status\n" if !$jobs->{$job_id}->{ready
};
7148 $jobs->{$job_id}->{ready
} = $ready;
7151 $readycounter++ if $job->{ready
};
7154 last if scalar(keys %$jobs) == 0;
7156 if ($readycounter == scalar(keys %$jobs)) {
7157 print "all '$op' jobs are ready\n";
7159 # do the complete later (or has already been done)
7160 last if $completion eq 'skip' || $completion eq 'auto';
7162 if ($vmiddst && $vmiddst != $vmid) {
7163 my $agent_running = $qga && qga_check_running
($vmid);
7164 if ($agent_running) {
7165 print "freeze filesystem\n";
7166 eval { mon_cmd
($vmid, "guest-fsfreeze-freeze"); };
7168 print "suspend vm\n";
7169 eval { PVE
::QemuServer
::vm_suspend
($vmid, 1); };
7172 # if we clone a disk for a new target vm, we don't switch the disk
7173 PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs);
7175 if ($agent_running) {
7176 print "unfreeze filesystem\n";
7177 eval { mon_cmd
($vmid, "guest-fsfreeze-thaw"); };
7179 print "resume vm\n";
7180 eval { PVE
::QemuServer
::vm_resume
($vmid, 1, 1); };
7186 for my $job_id (sort keys %$jobs) {
7187 # try to switch the disk if source and destination are on the same guest
7188 print "$job_id: Completing block job_id...\n";
7191 if ($completion eq 'complete') {
7192 $op = 'block-job-complete';
7193 } elsif ($completion eq 'cancel') {
7194 $op = 'block-job-cancel';
7196 die "invalid completion value: $completion\n";
7198 eval { mon_cmd
($vmid, $op, device
=> $job_id) };
7199 if ($@ =~ m/cannot be completed/) {
7200 print "$job_id: block job cannot be completed, trying again.\n";
7203 print "$job_id: Completed successfully.\n";
7204 $jobs->{$job_id}->{complete
} = 1;
7215 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7216 die "block job ($op) error: $err";
7220 sub qemu_blockjobs_cancel
{
7221 my ($vmid, $jobs) = @_;
7223 foreach my $job (keys %$jobs) {
7224 print "$job: Cancelling block job\n";
7225 eval { mon_cmd
($vmid, "block-job-cancel", device
=> $job); };
7226 $jobs->{$job}->{cancel
} = 1;
7230 my $stats = mon_cmd
($vmid, "query-block-jobs");
7232 my $running_jobs = {};
7233 foreach my $stat (@$stats) {
7234 $running_jobs->{$stat->{device
}} = $stat;
7237 foreach my $job (keys %$jobs) {
7239 if (defined($jobs->{$job}->{cancel
}) && !defined($running_jobs->{$job})) {
7240 print "$job: Done.\n";
7241 delete $jobs->{$job};
7245 last if scalar(keys %$jobs) == 0;
7252 my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
7253 $newvmid, $storage, $format, $full, $newvollist, $jobs, $completion, $qga, $bwlimit, $conf) = @_;
7258 print "create linked clone of drive $drivename ($drive->{file})\n";
7259 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $drive->{file
}, $newvmid, $snapname);
7260 push @$newvollist, $newvolid;
7263 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
7264 $storeid = $storage if $storage;
7266 my $dst_format = resolve_dst_disk_format
($storecfg, $storeid, $volname, $format);
7268 print "create full clone of drive $drivename ($drive->{file})\n";
7271 if (drive_is_cloudinit
($drive)) {
7272 $name = "vm-$newvmid-cloudinit";
7273 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7274 if ($scfg->{path
}) {
7275 $name .= ".$dst_format";
7278 $size = PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
;
7279 } elsif ($drivename eq 'efidisk0') {
7280 $size = get_efivars_size
($conf);
7282 ($size) = PVE
::Storage
::volume_size_info
($storecfg, $drive->{file
}, 10);
7284 $newvolid = PVE
::Storage
::vdisk_alloc
(
7285 $storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024)
7287 push @$newvollist, $newvolid;
7289 PVE
::Storage
::activate_volumes
($storecfg, [$newvolid]);
7291 if (drive_is_cloudinit
($drive)) {
7292 # when cloning multiple disks (e.g. during clone_vm) it might be the last disk
7293 # if this is the case, we have to complete any block-jobs still there from
7294 # previous drive-mirrors
7295 if (($completion eq 'complete') && (scalar(keys %$jobs) > 0)) {
7296 qemu_drive_mirror_monitor
($vmid, $newvmid, $jobs, $completion, $qga);
7301 my $sparseinit = PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $newvolid);
7302 if (!$running || $snapname) {
7303 # TODO: handle bwlimits
7304 if ($drivename eq 'efidisk0') {
7305 # the relevant data on the efidisk may be smaller than the source
7306 # e.g. on RBD/ZFS, so we use dd to copy only the amount
7307 # that is given by the OVMF_VARS.fd
7308 my $src_path = PVE
::Storage
::path
($storecfg, $drive->{file
});
7309 my $dst_path = PVE
::Storage
::path
($storecfg, $newvolid);
7311 # better for Ceph if block size is not too small, see bug #3324
7314 run_command
(['qemu-img', 'dd', '-n', '-O', $dst_format, "bs=$bs", "osize=$size",
7315 "if=$src_path", "of=$dst_path"]);
7317 qemu_img_convert
($drive->{file
}, $newvolid, $size, $snapname, $sparseinit);
7321 my $kvmver = get_running_qemu_version
($vmid);
7322 if (!min_version
($kvmver, 2, 7)) {
7323 die "drive-mirror with iothread requires qemu version 2.7 or higher\n"
7324 if $drive->{iothread
};
7327 qemu_drive_mirror
($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs,
7328 $completion, $qga, $bwlimit);
7333 my ($size) = eval { PVE
::Storage
::volume_size_info
($storecfg, $newvolid, 10) };
7336 $disk->{format
} = undef;
7337 $disk->{file
} = $newvolid;
7338 $disk->{size
} = $size if defined($size);
7343 sub get_running_qemu_version
{
7345 my $res = mon_cmd
($vmid, "query-version");
7346 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
7349 sub qemu_use_old_bios_files
{
7350 my ($machine_type) = @_;
7352 return if !$machine_type;
7354 my $use_old_bios_files = undef;
7356 if ($machine_type =~ m/^(\S+)\.pxe$/) {
7358 $use_old_bios_files = 1;
7360 my $version = extract_version
($machine_type, kvm_user_version
());
7361 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
7362 # load new efi bios files on migration. So this hack is required to allow
7363 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
7364 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
7365 $use_old_bios_files = !min_version
($version, 2, 4);
7368 return ($use_old_bios_files, $machine_type);
7371 sub get_efivars_size
{
7373 my $arch = get_vm_arch
($conf);
7374 my (undef, $ovmf_vars) = get_ovmf_files
($arch);
7375 die "uefi vars image '$ovmf_vars' not found\n" if ! -f
$ovmf_vars;
7376 return -s
$ovmf_vars;
7379 sub update_efidisk_size
{
7382 return if !defined($conf->{efidisk0
});
7384 my $disk = PVE
::QemuServer
::parse_drive
('efidisk0', $conf->{efidisk0
});
7385 $disk->{size
} = get_efivars_size
($conf);
7386 $conf->{efidisk0
} = print_drive
($disk);
7391 sub create_efidisk
($$$$$) {
7392 my ($storecfg, $storeid, $vmid, $fmt, $arch) = @_;
7394 my (undef, $ovmf_vars) = get_ovmf_files
($arch);
7395 die "EFI vars default image not found\n" if ! -f
$ovmf_vars;
7397 my $vars_size_b = -s
$ovmf_vars;
7398 my $vars_size = PVE
::Tools
::convert_size
($vars_size_b, 'b' => 'kb');
7399 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
7400 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
7402 qemu_img_convert
($ovmf_vars, $volid, $vars_size_b, undef, 0);
7403 my ($size) = PVE
::Storage
::volume_size_info
($storecfg, $volid, 3);
7405 return ($volid, $size/1024);
7408 sub vm_iothreads_list
{
7411 my $res = mon_cmd
($vmid, 'query-iothreads');
7414 foreach my $iothread (@$res) {
7415 $iothreads->{ $iothread->{id
} } = $iothread->{"thread-id"};
7422 my ($conf, $drive) = @_;
7426 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)) {
7428 } elsif ($conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single')) {
7434 my $controller = int($drive->{index} / $maxdev);
7435 my $controller_prefix = ($conf->{scsihw
} && $conf->{scsihw
} eq 'virtio-scsi-single')
7439 return ($maxdev, $controller, $controller_prefix);
7442 sub windows_version
{
7445 return 0 if !$ostype;
7449 if($ostype eq 'wxp' || $ostype eq 'w2k3' || $ostype eq 'w2k') {
7451 } elsif($ostype eq 'w2k8' || $ostype eq 'wvista') {
7453 } elsif ($ostype =~ m/^win(\d+)$/) {
7460 sub resolve_dst_disk_format
{
7461 my ($storecfg, $storeid, $src_volname, $format) = @_;
7462 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
7465 # if no target format is specified, use the source disk format as hint
7467 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7468 $format = qemu_img_format
($scfg, $src_volname);
7474 # test if requested format is supported - else use default
7475 my $supported = grep { $_ eq $format } @$validFormats;
7476 $format = $defFormat if !$supported;
7480 # NOTE: if this logic changes, please update docs & possibly gui logic
7481 sub find_vmstate_storage
{
7482 my ($conf, $storecfg) = @_;
7484 # first, return storage from conf if set
7485 return $conf->{vmstatestorage
} if $conf->{vmstatestorage
};
7487 my ($target, $shared, $local);
7489 foreach_storage_used_by_vm
($conf, sub {
7491 my $scfg = PVE
::Storage
::storage_config
($storecfg, $sid);
7492 my $dst = $scfg->{shared
} ? \
$shared : \
$local;
7493 $$dst = $sid if !$$dst || $scfg->{path
}; # prefer file based storage
7496 # second, use shared storage where VM has at least one disk
7497 # third, use local storage where VM has at least one disk
7498 # fall back to local storage
7499 $target = $shared // $local // 'local';
7505 my ($uuid, $uuid_str);
7506 UUID
::generate
($uuid);
7507 UUID
::unparse
($uuid, $uuid_str);
7511 sub generate_smbios1_uuid
{
7512 return "uuid=".generate_uuid
();
7518 mon_cmd
($vmid, 'nbd-server-stop');
7521 sub create_reboot_request
{
7523 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
7524 or die "failed to create reboot trigger file: $!\n";
7528 sub clear_reboot_request
{
7530 my $path = "/run/qemu-server/$vmid.reboot";
7533 $res = unlink($path);
7534 die "could not remove reboot request for $vmid: $!"
7535 if !$res && $! != POSIX
::ENOENT
;
7540 sub bootorder_from_legacy
{
7541 my ($conf, $bootcfg) = @_;
7543 my $boot = $bootcfg->{legacy
} || $boot_fmt->{legacy
}->{default};
7544 my $bootindex_hash = {};
7546 foreach my $o (split(//, $boot)) {
7547 $bootindex_hash->{$o} = $i*100;
7553 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7554 my ($ds, $drive) = @_;
7556 if (drive_is_cdrom
($drive, 1)) {
7557 if ($bootindex_hash->{d
}) {
7558 $bootorder->{$ds} = $bootindex_hash->{d
};
7559 $bootindex_hash->{d
} += 1;
7561 } elsif ($bootindex_hash->{c
}) {
7562 $bootorder->{$ds} = $bootindex_hash->{c
}
7563 if $conf->{bootdisk
} && $conf->{bootdisk
} eq $ds;
7564 $bootindex_hash->{c
} += 1;
7568 if ($bootindex_hash->{n
}) {
7569 for (my $i = 0; $i < $MAX_NETS; $i++) {
7570 my $netname = "net$i";
7571 next if !$conf->{$netname};
7572 $bootorder->{$netname} = $bootindex_hash->{n
};
7573 $bootindex_hash->{n
} += 1;
7580 # Generate default device list for 'boot: order=' property. Matches legacy
7581 # default boot order, but with explicit device names. This is important, since
7582 # the fallback for when neither 'order' nor the old format is specified relies
7583 # on 'bootorder_from_legacy' above, and it would be confusing if this diverges.
7584 sub get_default_bootdevices
{
7590 my $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 0);
7591 push @ret, $first if $first;
7594 $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 1);
7595 push @ret, $first if $first;
7598 for (my $i = 0; $i < $MAX_NETS; $i++) {
7599 my $netname = "net$i";
7600 next if !$conf->{$netname};
7601 push @ret, $netname;
7608 sub device_bootorder
{
7611 return bootorder_from_legacy
($conf) if !defined($conf->{boot
});
7613 my $boot = parse_property_string
($boot_fmt, $conf->{boot
});
7616 if (!defined($boot) || $boot->{legacy
}) {
7617 $bootorder = bootorder_from_legacy
($conf, $boot);
7618 } elsif ($boot->{order
}) {
7619 my $i = 100; # start at 100 to allow user to insert devices before us with -args
7620 for my $dev (PVE
::Tools
::split_list
($boot->{order
})) {
7621 $bootorder->{$dev} = $i++;
7628 sub register_qmeventd_handle
{
7632 my $peer = "/var/run/qmeventd.sock";
7637 $fh = IO
::Socket
::UNIX-
>new(Peer
=> $peer, Blocking
=> 0, Timeout
=> 1);
7639 if ($! != EINTR
&& $! != EAGAIN
) {
7640 die "unable to connect to qmeventd socket (vmid: $vmid) - $!\n";
7643 die "unable to connect to qmeventd socket (vmid: $vmid) - timeout "
7644 . "after $count retries\n";
7649 # send handshake to mark VM as backing up
7650 print $fh to_json
({vzdump
=> {vmid
=> "$vmid"}});
7652 # return handle to be closed later when inhibit is no longer required
7656 # bash completion helper
7658 sub complete_backup_archives
{
7659 my ($cmdname, $pname, $cvalue) = @_;
7661 my $cfg = PVE
::Storage
::config
();
7665 if ($cvalue =~ m/^([^:]+):/) {
7669 my $data = PVE
::Storage
::template_list
($cfg, $storeid, 'backup');
7672 foreach my $id (keys %$data) {
7673 foreach my $item (@{$data->{$id}}) {
7674 next if $item->{format
} !~ m/^vma\.(${\PVE::Storage::Plugin::COMPRESSOR_RE})$/;
7675 push @$res, $item->{volid
} if defined($item->{volid
});
7682 my $complete_vmid_full = sub {
7685 my $idlist = vmstatus
();
7689 foreach my $id (keys %$idlist) {
7690 my $d = $idlist->{$id};
7691 if (defined($running)) {
7692 next if $d->{template
};
7693 next if $running && $d->{status
} ne 'running';
7694 next if !$running && $d->{status
} eq 'running';
7703 return &$complete_vmid_full();
7706 sub complete_vmid_stopped
{
7707 return &$complete_vmid_full(0);
7710 sub complete_vmid_running
{
7711 return &$complete_vmid_full(1);
7714 sub complete_storage
{
7716 my $cfg = PVE
::Storage
::config
();
7717 my $ids = $cfg->{ids
};
7720 foreach my $sid (keys %$ids) {
7721 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, undef, 1);
7722 next if !$ids->{$sid}->{content
}->{images
};
7729 sub complete_migration_storage
{
7730 my ($cmd, $param, $current_value, $all_args) = @_;
7732 my $targetnode = @$all_args[1];
7734 my $cfg = PVE
::Storage
::config
();
7735 my $ids = $cfg->{ids
};
7738 foreach my $sid (keys %$ids) {
7739 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, $targetnode, 1);
7740 next if !$ids->{$sid}->{content
}->{images
};
7749 my $qmpstatus = eval {
7750 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid);
7751 mon_cmd
($vmid, "query-status");
7754 return $qmpstatus && $qmpstatus->{status
} eq "paused";
7757 sub check_volume_storage_type
{
7758 my ($storecfg, $vol) = @_;
7760 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($vol);
7761 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7762 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $vol);
7764 die "storage '$storeid' does not support content-type '$vtype'\n"
7765 if !$scfg->{content
}->{$vtype};