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 if (drive_is_cdrom
($drive)) {
1534 $path = get_iso_path
($storecfg, $vmid, $volid);
1535 die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
1537 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1539 $path = PVE
::Storage
::path
($storecfg, $volid);
1540 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
1541 $format //= qemu_img_format
($scfg, $volname);
1548 my $is_rbd = $path =~ m/^rbd:/;
1551 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1552 foreach my $o (@qemu_drive_options) {
1553 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1556 # snapshot only accepts on|off
1557 if (defined($drive->{snapshot
})) {
1558 my $v = $drive->{snapshot
} ?
'on' : 'off';
1559 $opts .= ",snapshot=$v";
1562 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1563 my ($dir, $qmpname) = @$type;
1564 if (my $v = $drive->{"mbps$dir"}) {
1565 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1567 if (my $v = $drive->{"mbps${dir}_max"}) {
1568 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1570 if (my $v = $drive->{"bps${dir}_max_length"}) {
1571 $opts .= ",throttling.bps$qmpname-max-length=$v";
1573 if (my $v = $drive->{"iops${dir}"}) {
1574 $opts .= ",throttling.iops$qmpname=$v";
1576 if (my $v = $drive->{"iops${dir}_max"}) {
1577 $opts .= ",throttling.iops$qmpname-max=$v";
1579 if (my $v = $drive->{"iops${dir}_max_length"}) {
1580 $opts .= ",throttling.iops$qmpname-max-length=$v";
1585 $format = "rbd" if $is_rbd;
1586 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1588 $opts .= ",format=alloc-track,file.driver=$format";
1590 $opts .= ",format=$format";
1593 my $cache_direct = 0;
1595 if (my $cache = $drive->{cache
}) {
1596 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1597 } elsif (!drive_is_cdrom
($drive)) {
1598 $opts .= ",cache=none";
1602 if (!$drive->{aio
}) {
1604 # io_uring supports all cache modes
1605 $opts .= ",aio=io_uring";
1607 # aio native works only with O_DIRECT
1609 $opts .= ",aio=native";
1611 $opts .= ",aio=threads";
1616 if (!drive_is_cdrom
($drive)) {
1618 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
1619 $detectzeroes = 'off';
1620 } elsif ($drive->{discard
}) {
1621 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
1623 # This used to be our default with discard not being specified:
1624 $detectzeroes = 'on';
1627 # note: 'detect-zeroes' works per blockdev and we want it to persist
1628 # after the alloc-track is removed, so put it on 'file' directly
1629 my $dz_param = $pbs_name ?
"file.detect-zeroes" : "detect-zeroes";
1630 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
1634 $opts .= ",backing=$pbs_name";
1635 $opts .= ",auto-remove=on";
1638 # my $file_param = $pbs_name ? "file.file.filename" : "file";
1639 my $file_param = "file";
1641 # non-rbd drivers require the underlying file to be a seperate block
1642 # node, so add a second .file indirection
1643 $file_param .= ".file" if !$is_rbd;
1644 $file_param .= ".filename";
1646 my $pathinfo = $path ?
"$file_param=$path," : '';
1648 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1651 sub print_pbs_blockdev
{
1652 my ($pbs_conf, $pbs_name) = @_;
1653 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1654 $blockdev .= ",repository=$pbs_conf->{repository}";
1655 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1656 $blockdev .= ",archive=$pbs_conf->{archive}";
1657 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile
};
1661 sub print_netdevice_full
{
1662 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type) = @_;
1664 my $device = $net->{model
};
1665 if ($net->{model
} eq 'virtio') {
1666 $device = 'virtio-net-pci';
1669 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
1670 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
1671 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
1672 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1673 # and out of each queue plus one config interrupt and control vector queue
1674 my $vectors = $net->{queues
} * 2 + 2;
1675 $tmpstr .= ",vectors=$vectors,mq=on";
1677 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
1679 if (my $mtu = $net->{mtu
}) {
1680 if ($net->{model
} eq 'virtio' && $net->{bridge
}) {
1681 my $bridge_mtu = PVE
::Network
::read_bridge_mtu
($net->{bridge
});
1684 } elsif ($mtu < 576) {
1685 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1686 } elsif ($mtu > $bridge_mtu) {
1687 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1689 $tmpstr .= ",host_mtu=$mtu";
1691 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
1695 if ($use_old_bios_files) {
1697 if ($device eq 'virtio-net-pci') {
1698 $romfile = 'pxe-virtio.rom';
1699 } elsif ($device eq 'e1000') {
1700 $romfile = 'pxe-e1000.rom';
1701 } elsif ($device eq 'ne2k') {
1702 $romfile = 'pxe-ne2k_pci.rom';
1703 } elsif ($device eq 'pcnet') {
1704 $romfile = 'pxe-pcnet.rom';
1705 } elsif ($device eq 'rtl8139') {
1706 $romfile = 'pxe-rtl8139.rom';
1708 $tmpstr .= ",romfile=$romfile" if $romfile;
1714 sub print_netdev_full
{
1715 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
1718 if ($netid =~ m/^net(\d+)$/) {
1722 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
1724 my $ifname = "tap${vmid}i$i";
1726 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1727 die "interface name '$ifname' is too long (max 15 character)\n"
1728 if length($ifname) >= 16;
1730 my $vhostparam = '';
1731 if (is_native
($arch)) {
1732 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
1735 my $vmname = $conf->{name
} || "vm$vmid";
1738 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
1740 if ($net->{bridge
}) {
1741 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1742 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
1744 $netdev = "type=user,id=$netid,hostname=$vmname";
1747 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
1753 'cirrus' => 'cirrus-vga',
1755 'vmware' => 'vmware-svga',
1756 'virtio' => 'virtio-vga',
1759 sub print_vga_device
{
1760 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
1762 my $type = $vga_map->{$vga->{type
}};
1763 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
1764 $type = 'virtio-gpu';
1766 my $vgamem_mb = $vga->{memory
};
1768 my $max_outputs = '';
1770 $type = $id ?
'qxl' : 'qxl-vga';
1772 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
1773 # set max outputs so linux can have up to 4 qxl displays with one device
1774 if (min_version
($machine_version, 4, 1)) {
1775 $max_outputs = ",max_outputs=4";
1780 die "no devicetype for $vga->{type}\n" if !$type;
1784 if ($vga->{type
} eq 'virtio') {
1785 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
1786 $memory = ",max_hostmem=$bytes";
1788 # from https://www.spice-space.org/multiple-monitors.html
1789 $memory = ",vgamem_mb=$vga->{memory}";
1790 my $ram = $vgamem_mb * 4;
1791 my $vram = $vgamem_mb * 2;
1792 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1794 $memory = ",vgamem_mb=$vga->{memory}";
1796 } elsif ($qxlnum && $id) {
1797 $memory = ",ram_size=67108864,vram_size=33554432";
1801 if ($type eq 'VGA' && windows_version
($conf->{ostype
})) {
1802 $edidoff=",edid=off" if (!defined($conf->{bios
}) || $conf->{bios
} ne 'ovmf');
1805 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1806 my $vgaid = "vga" . ($id // '');
1809 if ($q35 && $vgaid eq 'vga') {
1810 # the first display uses pcie.0 bus on q35 machines
1811 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
1813 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
1816 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
1819 sub parse_number_sets
{
1822 foreach my $part (split(/;/, $set)) {
1823 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
1824 die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1;
1825 push @$res, [ $1, $2 ];
1827 die "invalid range: $part\n";
1836 my $res = parse_property_string
($numa_fmt, $data);
1837 $res->{cpus
} = parse_number_sets
($res->{cpus
}) if defined($res->{cpus
});
1838 $res->{hostnodes
} = parse_number_sets
($res->{hostnodes
}) if defined($res->{hostnodes
});
1842 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1846 my $res = eval { parse_property_string
($net_fmt, $data) };
1851 if (!defined($res->{macaddr
})) {
1852 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
1853 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
1858 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1859 sub parse_ipconfig
{
1862 my $res = eval { parse_property_string
($ipconfig_fmt, $data) };
1868 if ($res->{gw
} && !$res->{ip
}) {
1869 warn 'gateway specified without specifying an IP address';
1872 if ($res->{gw6
} && !$res->{ip6
}) {
1873 warn 'IPv6 gateway specified without specifying an IPv6 address';
1876 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
1877 warn 'gateway specified together with DHCP';
1880 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
1882 warn "IPv6 gateway specified together with $res->{ip6} address";
1886 if (!$res->{ip
} && !$res->{ip6
}) {
1887 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
1896 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
1899 sub add_random_macs
{
1900 my ($settings) = @_;
1902 foreach my $opt (keys %$settings) {
1903 next if $opt !~ m/^net(\d+)$/;
1904 my $net = parse_net
($settings->{$opt});
1906 $settings->{$opt} = print_net
($net);
1910 sub vm_is_volid_owner
{
1911 my ($storecfg, $vmid, $volid) = @_;
1913 if ($volid !~ m
|^/|) {
1915 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
1916 if ($owner && ($owner == $vmid)) {
1924 sub vmconfig_register_unused_drive
{
1925 my ($storecfg, $vmid, $conf, $drive) = @_;
1927 if (drive_is_cloudinit
($drive)) {
1928 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
1930 } elsif (!drive_is_cdrom
($drive)) {
1931 my $volid = $drive->{file
};
1932 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
1933 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
1938 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
1942 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
1943 format_description
=> 'UUID',
1944 description
=> "Set SMBIOS1 UUID.",
1949 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1950 format_description
=> 'Base64 encoded string',
1951 description
=> "Set SMBIOS1 version.",
1956 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1957 format_description
=> 'Base64 encoded string',
1958 description
=> "Set SMBIOS1 serial number.",
1963 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1964 format_description
=> 'Base64 encoded string',
1965 description
=> "Set SMBIOS1 manufacturer.",
1970 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1971 format_description
=> 'Base64 encoded string',
1972 description
=> "Set SMBIOS1 product ID.",
1977 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1978 format_description
=> 'Base64 encoded string',
1979 description
=> "Set SMBIOS1 SKU string.",
1984 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
1985 format_description
=> 'Base64 encoded string',
1986 description
=> "Set SMBIOS1 family string.",
1991 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
1999 my $res = eval { parse_property_string
($smbios1_fmt, $data) };
2006 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2009 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2011 sub parse_watchdog
{
2016 my $res = eval { parse_property_string
($watchdog_fmt, $value) };
2021 sub parse_guest_agent
{
2024 return {} if !defined($conf->{agent
});
2026 my $res = eval { parse_property_string
($agent_fmt, $conf->{agent
}) };
2029 # if the agent is disabled ignore the other potentially set properties
2030 return {} if !$res->{enabled
};
2035 my ($conf, $key) = @_;
2036 return undef if !defined($conf->{agent
});
2038 my $agent = parse_guest_agent
($conf);
2039 return $agent->{$key};
2045 return {} if !$value;
2046 my $res = eval { parse_property_string
($vga_fmt, $value) };
2056 my $res = eval { parse_property_string
($rng_fmt, $value) };
2061 PVE
::JSONSchema
::register_format
('pve-qm-usb-device', \
&verify_usb_device
);
2062 sub verify_usb_device
{
2063 my ($value, $noerr) = @_;
2065 return $value if parse_usb_device
($value);
2069 die "unable to parse usb device\n";
2072 # add JSON properties for create and set function
2073 sub json_config_properties
{
2076 foreach my $opt (keys %$confdesc) {
2077 next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'vmstate' ||
2078 $opt eq 'runningmachine' || $opt eq 'runningcpu';
2079 $prop->{$opt} = $confdesc->{$opt};
2085 # return copy of $confdesc_cloudinit to generate documentation
2086 sub cloudinit_config_properties
{
2088 return dclone
($confdesc_cloudinit);
2092 my ($key, $value) = @_;
2094 die "unknown setting '$key'\n" if !$confdesc->{$key};
2096 my $type = $confdesc->{$key}->{type
};
2098 if (!defined($value)) {
2099 die "got undefined value\n";
2102 if ($value =~ m/[\n\r]/) {
2103 die "property contains a line feed\n";
2106 if ($type eq 'boolean') {
2107 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2108 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2109 die "type check ('boolean') failed - got '$value'\n";
2110 } elsif ($type eq 'integer') {
2111 return int($1) if $value =~ m/^(\d+)$/;
2112 die "type check ('integer') failed - got '$value'\n";
2113 } elsif ($type eq 'number') {
2114 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2115 die "type check ('number') failed - got '$value'\n";
2116 } elsif ($type eq 'string') {
2117 if (my $fmt = $confdesc->{$key}->{format
}) {
2118 PVE
::JSONSchema
::check_format
($fmt, $value);
2121 $value =~ s/^\"(.*)\"$/$1/;
2124 die "internal error"
2129 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
2131 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2133 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
2135 if ($conf->{template
}) {
2136 # check if any base image is still used by a linked clone
2137 PVE
::QemuConfig-
>foreach_volume_full($conf, { include_unused
=> 1 }, sub {
2138 my ($ds, $drive) = @_;
2139 return if drive_is_cdrom
($drive);
2141 my $volid = $drive->{file
};
2142 return if !$volid || $volid =~ m
|^/|;
2144 die "base volume '$volid' is still in use by linked cloned\n"
2145 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2150 my $remove_owned_drive = sub {
2151 my ($ds, $drive) = @_;
2152 return if drive_is_cdrom
($drive, 1);
2154 my $volid = $drive->{file
};
2155 return if !$volid || $volid =~ m
|^/|;
2157 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2158 return if !$path || !$owner || ($owner != $vmid);
2160 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2161 warn "Could not remove disk '$volid', check manually: $@" if $@;
2164 # only remove disks owned by this VM (referenced in the config)
2165 my $include_opts = {
2166 include_unused
=> 1,
2167 extra_keys
=> ['vmstate'],
2169 PVE
::QemuConfig-
>foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2171 for my $snap (values %{$conf->{snapshots
}}) {
2172 next if !defined($snap->{vmstate
});
2173 my $drive = PVE
::QemuConfig-
>parse_volume('vmstate', $snap->{vmstate
}, 1);
2174 next if !defined($drive);
2175 $remove_owned_drive->('vmstate', $drive);
2178 if ($purge_unreferenced) { # also remove unreferenced disk
2179 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid, undef, 'images');
2180 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2181 my ($volid, $sid, $volname, $d) = @_;
2182 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2187 if (defined $replacement_conf) {
2188 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2190 PVE
::QemuConfig-
>destroy_config($vmid);
2194 sub parse_vm_config
{
2195 my ($filename, $raw) = @_;
2197 return if !defined($raw);
2200 digest
=> Digest
::SHA
::sha1_hex
($raw),
2205 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2206 || die "got strange filename '$filename'";
2214 my @lines = split(/\n/, $raw);
2215 foreach my $line (@lines) {
2216 next if $line =~ m/^\s*$/;
2218 if ($line =~ m/^\[PENDING\]\s*$/i) {
2219 $section = 'pending';
2220 if (defined($descr)) {
2222 $conf->{description
} = $descr;
2225 $conf = $res->{$section} = {};
2228 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2230 if (defined($descr)) {
2232 $conf->{description
} = $descr;
2235 $conf = $res->{snapshots
}->{$section} = {};
2239 if ($line =~ m/^\#(.*)\s*$/) {
2240 $descr = '' if !defined($descr);
2241 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2245 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2246 $descr = '' if !defined($descr);
2247 $descr .= PVE
::Tools
::decode_text
($2);
2248 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2249 $conf->{snapstate
} = $1;
2250 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2253 $conf->{$key} = $value;
2254 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2256 if ($section eq 'pending') {
2257 $conf->{delete} = $value; # we parse this later
2259 warn "vm $vmid - propertry 'delete' is only allowed in [PENDING]\n";
2261 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2264 eval { $value = check_type
($key, $value); };
2266 warn "vm $vmid - unable to parse value of '$key' - $@";
2268 $key = 'ide2' if $key eq 'cdrom';
2269 my $fmt = $confdesc->{$key}->{format
};
2270 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2271 my $v = parse_drive
($key, $value);
2272 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2273 $v->{file
} = $volid;
2274 $value = print_drive
($v);
2276 warn "vm $vmid - unable to parse value of '$key'\n";
2281 $conf->{$key} = $value;
2284 warn "vm $vmid - unable to parse config: $line\n";
2288 if (defined($descr)) {
2290 $conf->{description
} = $descr;
2292 delete $res->{snapstate
}; # just to be sure
2297 sub write_vm_config
{
2298 my ($filename, $conf) = @_;
2300 delete $conf->{snapstate
}; # just to be sure
2302 if ($conf->{cdrom
}) {
2303 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2304 $conf->{ide2
} = $conf->{cdrom
};
2305 delete $conf->{cdrom
};
2308 # we do not use 'smp' any longer
2309 if ($conf->{sockets
}) {
2310 delete $conf->{smp
};
2311 } elsif ($conf->{smp
}) {
2312 $conf->{sockets
} = $conf->{smp
};
2313 delete $conf->{cores
};
2314 delete $conf->{smp
};
2317 my $used_volids = {};
2319 my $cleanup_config = sub {
2320 my ($cref, $pending, $snapname) = @_;
2322 foreach my $key (keys %$cref) {
2323 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2324 $key eq 'snapstate' || $key eq 'pending';
2325 my $value = $cref->{$key};
2326 if ($key eq 'delete') {
2327 die "propertry 'delete' is only allowed in [PENDING]\n"
2329 # fixme: check syntax?
2332 eval { $value = check_type
($key, $value); };
2333 die "unable to parse value of '$key' - $@" if $@;
2335 $cref->{$key} = $value;
2337 if (!$snapname && is_valid_drivename
($key)) {
2338 my $drive = parse_drive
($key, $value);
2339 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2344 &$cleanup_config($conf);
2346 &$cleanup_config($conf->{pending
}, 1);
2348 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2349 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2350 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2353 # remove 'unusedX' settings if we re-add a volume
2354 foreach my $key (keys %$conf) {
2355 my $value = $conf->{$key};
2356 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2357 delete $conf->{$key};
2361 my $generate_raw_config = sub {
2362 my ($conf, $pending) = @_;
2366 # add description as comment to top of file
2367 if (defined(my $descr = $conf->{description
})) {
2369 foreach my $cl (split(/\n/, $descr)) {
2370 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2373 $raw .= "#\n" if $pending;
2377 foreach my $key (sort keys %$conf) {
2378 next if $key =~ /^(digest|description|pending|snapshots)$/;
2379 $raw .= "$key: $conf->{$key}\n";
2384 my $raw = &$generate_raw_config($conf);
2386 if (scalar(keys %{$conf->{pending
}})){
2387 $raw .= "\n[PENDING]\n";
2388 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2391 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2392 $raw .= "\n[$snapname]\n";
2393 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2403 # we use static defaults from our JSON schema configuration
2404 foreach my $key (keys %$confdesc) {
2405 if (defined(my $default = $confdesc->{$key}->{default})) {
2406 $res->{$key} = $default;
2414 my $vmlist = PVE
::Cluster
::get_vmlist
();
2416 return $res if !$vmlist || !$vmlist->{ids
};
2417 my $ids = $vmlist->{ids
};
2418 my $nodename = nodename
();
2420 foreach my $vmid (keys %$ids) {
2421 my $d = $ids->{$vmid};
2422 next if !$d->{node
} || $d->{node
} ne $nodename;
2423 next if !$d->{type
} || $d->{type
} ne 'qemu';
2424 $res->{$vmid}->{exists} = 1;
2429 # test if VM uses local resources (to prevent migration)
2430 sub check_local_resources
{
2431 my ($conf, $noerr) = @_;
2435 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2436 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2438 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2440 foreach my $k (keys %$conf) {
2441 next if $k =~ m/^usb/ && ($conf->{$k} =~ m/^spice(?![^,])/);
2442 # sockets are safe: they will recreated be on the target side post-migrate
2443 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2444 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2447 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2452 # check if used storages are available on all nodes (use by migrate)
2453 sub check_storage_availability
{
2454 my ($storecfg, $conf, $node) = @_;
2456 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2457 my ($ds, $drive) = @_;
2459 my $volid = $drive->{file
};
2462 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2465 # check if storage is available on both nodes
2466 my $scfg = PVE
::Storage
::storage_check_enabled
($storecfg, $sid);
2467 PVE
::Storage
::storage_check_enabled
($storecfg, $sid, $node);
2469 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $volid);
2471 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2472 if !$scfg->{content
}->{$vtype};
2476 # list nodes where all VM images are available (used by has_feature API)
2478 my ($conf, $storecfg) = @_;
2480 my $nodelist = PVE
::Cluster
::get_nodelist
();
2481 my $nodehash = { map { $_ => 1 } @$nodelist };
2482 my $nodename = nodename
();
2484 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2485 my ($ds, $drive) = @_;
2487 my $volid = $drive->{file
};
2490 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2492 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2493 if ($scfg->{disable
}) {
2495 } elsif (my $avail = $scfg->{nodes
}) {
2496 foreach my $node (keys %$nodehash) {
2497 delete $nodehash->{$node} if !$avail->{$node};
2499 } elsif (!$scfg->{shared
}) {
2500 foreach my $node (keys %$nodehash) {
2501 delete $nodehash->{$node} if $node ne $nodename
2510 sub check_local_storage_availability
{
2511 my ($conf, $storecfg) = @_;
2513 my $nodelist = PVE
::Cluster
::get_nodelist
();
2514 my $nodehash = { map { $_ => {} } @$nodelist };
2516 PVE
::QemuConfig-
>foreach_volume($conf, sub {
2517 my ($ds, $drive) = @_;
2519 my $volid = $drive->{file
};
2522 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2524 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2526 if ($scfg->{disable
}) {
2527 foreach my $node (keys %$nodehash) {
2528 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2530 } elsif (my $avail = $scfg->{nodes
}) {
2531 foreach my $node (keys %$nodehash) {
2532 if (!$avail->{$node}) {
2533 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2540 foreach my $node (values %$nodehash) {
2541 if (my $unavail = $node->{unavailable_storages
}) {
2542 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2549 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2551 my ($vmid, $nocheck, $node) = @_;
2553 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2554 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2559 my $vzlist = config_list
();
2561 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2563 while (defined(my $de = $fd->read)) {
2564 next if $de !~ m/^(\d+)\.pid$/;
2566 next if !defined($vzlist->{$vmid});
2567 if (my $pid = check_running
($vmid)) {
2568 $vzlist->{$vmid}->{pid
} = $pid;
2575 our $vmstatus_return_properties = {
2576 vmid
=> get_standard_option
('pve-vmid'),
2578 description
=> "Qemu process status.",
2580 enum
=> ['stopped', 'running'],
2583 description
=> "Maximum memory in bytes.",
2586 renderer
=> 'bytes',
2589 description
=> "Root disk size in bytes.",
2592 renderer
=> 'bytes',
2595 description
=> "VM name.",
2600 description
=> "Qemu QMP agent status.",
2605 description
=> "PID of running qemu process.",
2610 description
=> "Uptime.",
2613 renderer
=> 'duration',
2616 description
=> "Maximum usable CPUs.",
2621 description
=> "The current config lock, if any.",
2626 description
=> "The current configured tags, if any",
2630 'running-machine' => {
2631 description
=> "The currently running machine type (if running).",
2636 description
=> "The currently running QEMU version (if running).",
2642 my $last_proc_pid_stat;
2644 # get VM status information
2645 # This must be fast and should not block ($full == false)
2646 # We only query KVM using QMP if $full == true (this can be slow)
2648 my ($opt_vmid, $full) = @_;
2652 my $storecfg = PVE
::Storage
::config
();
2654 my $list = vzlist
();
2655 my $defaults = load_defaults
();
2657 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
(1);
2659 my $cpucount = $cpuinfo->{cpus
} || 1;
2661 foreach my $vmid (keys %$list) {
2662 next if $opt_vmid && ($vmid ne $opt_vmid);
2664 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2666 my $d = { vmid
=> int($vmid) };
2667 $d->{pid
} = int($list->{$vmid}->{pid
}) if $list->{$vmid}->{pid
};
2669 # fixme: better status?
2670 $d->{status
} = $list->{$vmid}->{pid
} ?
'running' : 'stopped';
2672 my $size = PVE
::QemuServer
::Drive
::bootdisk_size
($storecfg, $conf);
2673 if (defined($size)) {
2674 $d->{disk
} = 0; # no info available
2675 $d->{maxdisk
} = $size;
2681 $d->{cpus
} = ($conf->{sockets
} || $defaults->{sockets
})
2682 * ($conf->{cores
} || $defaults->{cores
});
2683 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
2684 $d->{cpus
} = $conf->{vcpus
} if $conf->{vcpus
};
2686 $d->{name
} = $conf->{name
} || "VM $vmid";
2687 $d->{maxmem
} = $conf->{memory
} ?
$conf->{memory
}*(1024*1024)
2688 : $defaults->{memory
}*(1024*1024);
2690 if ($conf->{balloon
}) {
2691 $d->{balloon_min
} = $conf->{balloon
}*(1024*1024);
2692 $d->{shares
} = defined($conf->{shares
}) ?
$conf->{shares
}
2693 : $defaults->{shares
};
2704 $d->{diskwrite
} = 0;
2706 $d->{template
} = 1 if PVE
::QemuConfig-
>is_template($conf);
2708 $d->{serial
} = 1 if conf_has_serial
($conf);
2709 $d->{lock} = $conf->{lock} if $conf->{lock};
2710 $d->{tags
} = $conf->{tags
} if defined($conf->{tags
});
2715 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
2716 foreach my $dev (keys %$netdev) {
2717 next if $dev !~ m/^tap([1-9]\d*)i/;
2719 my $d = $res->{$vmid};
2722 $d->{netout
} += $netdev->{$dev}->{receive
};
2723 $d->{netin
} += $netdev->{$dev}->{transmit
};
2726 $d->{nics
}->{$dev}->{netout
} = int($netdev->{$dev}->{receive
});
2727 $d->{nics
}->{$dev}->{netin
} = int($netdev->{$dev}->{transmit
});
2732 my $ctime = gettimeofday
;
2734 foreach my $vmid (keys %$list) {
2736 my $d = $res->{$vmid};
2737 my $pid = $d->{pid
};
2740 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
2741 next if !$pstat; # not running
2743 my $used = $pstat->{utime} + $pstat->{stime
};
2745 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
2747 if ($pstat->{vsize
}) {
2748 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
2751 my $old = $last_proc_pid_stat->{$pid};
2753 $last_proc_pid_stat->{$pid} = {
2761 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
2763 if ($dtime > 1000) {
2764 my $dutime = $used - $old->{used
};
2766 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
2767 $last_proc_pid_stat->{$pid} = {
2773 $d->{cpu
} = $old->{cpu
};
2777 return $res if !$full;
2779 my $qmpclient = PVE
::QMPClient-
>new();
2781 my $ballooncb = sub {
2782 my ($vmid, $resp) = @_;
2784 my $info = $resp->{'return'};
2785 return if !$info->{max_mem
};
2787 my $d = $res->{$vmid};
2789 # use memory assigned to VM
2790 $d->{maxmem
} = $info->{max_mem
};
2791 $d->{balloon
} = $info->{actual
};
2793 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
2794 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
2795 $d->{freemem
} = $info->{free_mem
};
2798 $d->{ballooninfo
} = $info;
2801 my $blockstatscb = sub {
2802 my ($vmid, $resp) = @_;
2803 my $data = $resp->{'return'} || [];
2804 my $totalrdbytes = 0;
2805 my $totalwrbytes = 0;
2807 for my $blockstat (@$data) {
2808 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
2809 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
2811 $blockstat->{device
} =~ s/drive-//;
2812 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
2814 $res->{$vmid}->{diskread
} = $totalrdbytes;
2815 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
2818 my $machinecb = sub {
2819 my ($vmid, $resp) = @_;
2820 my $data = $resp->{'return'} || [];
2822 $res->{$vmid}->{'running-machine'} =
2823 PVE
::QemuServer
::Machine
::current_from_query_machines
($data);
2826 my $versioncb = sub {
2827 my ($vmid, $resp) = @_;
2828 my $data = $resp->{'return'} // {};
2829 my $version = 'unknown';
2831 if (my $v = $data->{qemu
}) {
2832 $version = $v->{major
} . "." . $v->{minor
} . "." . $v->{micro
};
2835 $res->{$vmid}->{'running-qemu'} = $version;
2838 my $statuscb = sub {
2839 my ($vmid, $resp) = @_;
2841 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
2842 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
2843 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
2844 # this fails if ballon driver is not loaded, so this must be
2845 # the last commnand (following command are aborted if this fails).
2846 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
2848 my $status = 'unknown';
2849 if (!defined($status = $resp->{'return'}->{status
})) {
2850 warn "unable to get VM status\n";
2854 $res->{$vmid}->{qmpstatus
} = $resp->{'return'}->{status
};
2857 foreach my $vmid (keys %$list) {
2858 next if $opt_vmid && ($vmid ne $opt_vmid);
2859 next if !$res->{$vmid}->{pid
}; # not running
2860 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
2863 $qmpclient->queue_execute(undef, 2);
2865 foreach my $vmid (keys %$list) {
2866 next if $opt_vmid && ($vmid ne $opt_vmid);
2867 next if !$res->{$vmid}->{pid
}; #not running
2869 # we can't use the $qmpclient since it might have already aborted on
2870 # 'query-balloon', but this might also fail for older versions...
2871 my $qemu_support = eval { mon_cmd
($vmid, "query-proxmox-support") };
2872 $res->{$vmid}->{'proxmox-support'} = $qemu_support // {};
2875 foreach my $vmid (keys %$list) {
2876 next if $opt_vmid && ($vmid ne $opt_vmid);
2877 $res->{$vmid}->{qmpstatus
} = $res->{$vmid}->{status
} if !$res->{$vmid}->{qmpstatus
};
2883 sub conf_has_serial
{
2886 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
2887 if ($conf->{"serial$i"}) {
2895 sub conf_has_audio
{
2896 my ($conf, $id) = @_;
2899 my $audio = $conf->{"audio$id"};
2900 return if !defined($audio);
2902 my $audioproperties = parse_property_string
($audio_fmt, $audio);
2903 my $audiodriver = $audioproperties->{driver
} // 'spice';
2906 dev
=> $audioproperties->{device
},
2907 dev_id
=> "audiodev$id",
2908 backend
=> $audiodriver,
2909 backend_id
=> "$audiodriver-backend${id}",
2914 my ($audio, $audiopciaddr, $machine_version) = @_;
2918 my $id = $audio->{dev_id
};
2920 if (min_version
($machine_version, 4, 2)) {
2921 $audiodev = ",audiodev=$audio->{backend_id}";
2924 if ($audio->{dev
} eq 'AC97') {
2925 push @$devs, '-device', "AC97,id=${id}${audiopciaddr}$audiodev";
2926 } elsif ($audio->{dev
} =~ /intel\-hda$/) {
2927 push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
2928 push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0$audiodev";
2929 push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1$audiodev";
2931 die "unkown audio device '$audio->{dev}', implement me!";
2934 push @$devs, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
2939 sub vga_conf_has_spice
{
2942 my $vgaconf = parse_vga
($vga);
2943 my $vgatype = $vgaconf->{type
};
2944 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
2951 return get_host_arch
() eq $arch;
2956 return $conf->{arch
} // get_host_arch
();
2959 my $default_machines = {
2964 sub get_installed_machine_version
{
2965 my ($kvmversion) = @_;
2966 $kvmversion = kvm_user_version
() if !defined($kvmversion);
2967 $kvmversion =~ m/^(\d+\.\d+)/;
2971 sub windows_get_pinned_machine_version
{
2972 my ($machine, $base_version, $kvmversion) = @_;
2974 my $pin_version = $base_version;
2975 if (!defined($base_version) ||
2976 !PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($base_version, $kvmversion)
2978 $pin_version = get_installed_machine_version
($kvmversion);
2980 if (!$machine || $machine eq 'pc') {
2981 $machine = "pc-i440fx-$pin_version";
2982 } elsif ($machine eq 'q35') {
2983 $machine = "pc-q35-$pin_version";
2984 } elsif ($machine eq 'virt') {
2985 $machine = "virt-$pin_version";
2987 warn "unknown machine type '$machine', not touching that!\n";
2993 sub get_vm_machine
{
2994 my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
2996 my $machine = $forcemachine || $conf->{machine
};
2998 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
2999 $kvmversion //= kvm_user_version
();
3000 # we must pin Windows VMs without a specific version to 5.1, as 5.2 fixed a bug in ACPI
3001 # layout which confuses windows quite a bit and may result in various regressions..
3002 # see: https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg08484.html
3003 if (windows_version
($conf->{ostype
})) {
3004 $machine = windows_get_pinned_machine_version
($machine, '5.1', $kvmversion);
3007 $machine ||= $default_machines->{$arch};
3008 if ($add_pve_version) {
3009 my $pvever = PVE
::QemuServer
::Machine
::get_pve_version
($kvmversion);
3010 $machine .= "+pve$pvever";
3014 if ($add_pve_version && $machine !~ m/\+pve\d+?(?:\.pxe)?$/) {
3015 my $is_pxe = $machine =~ m/^(.*?)\.pxe$/;
3016 $machine = $1 if $is_pxe;
3018 # for version-pinned machines that do not include a pve-version (e.g.
3019 # pc-q35-4.1), we assume 0 to keep them stable in case we bump
3020 $machine .= '+pve0';
3022 $machine .= '.pxe' if $is_pxe;
3028 sub get_ovmf_files
($) {
3031 my $ovmf = $OVMF->{$arch}
3032 or die "no OVMF images known for architecture '$arch'\n";
3038 aarch64
=> '/usr/bin/qemu-system-aarch64',
3039 x86_64
=> '/usr/bin/qemu-system-x86_64',
3041 sub get_command_for_arch
($) {
3043 return '/usr/bin/kvm' if is_native
($arch);
3045 my $cmd = $Arch2Qemu->{$arch}
3046 or die "don't know how to emulate architecture '$arch'\n";
3050 # To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
3051 # to use in a QEMU command line (-cpu element), first array_intersect the result
3052 # of query_supported_ with query_understood_. This is necessary because:
3054 # a) query_understood_ returns flags the host cannot use and
3055 # b) query_supported_ (rather the QMP call) doesn't actually return CPU
3056 # flags, but CPU settings - with most of them being flags. Those settings
3057 # (and some flags, curiously) cannot be specified as a "-cpu" argument.
3059 # query_supported_ needs to start up to 2 temporary VMs and is therefore rather
3060 # expensive. If you need the value returned from this, you can get it much
3061 # cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags-$accel') with
3062 # $accel being 'kvm' or 'tcg'.
3064 # pvestatd calls this function on startup and whenever the QEMU/KVM version
3065 # changes, automatically populating pmxcfs.
3067 # Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
3068 # since kvm and tcg machines support different flags
3070 sub query_supported_cpu_flags
{
3073 $arch //= get_host_arch
();
3074 my $default_machine = $default_machines->{$arch};
3078 # FIXME: Once this is merged, the code below should work for ARM as well:
3079 # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
3080 die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
3083 my $kvm_supported = defined(kvm_version
());
3084 my $qemu_cmd = get_command_for_arch
($arch);
3086 my $pidfile = PVE
::QemuServer
::Helpers
::pidfile_name
($fakevmid);
3088 # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
3089 my $query_supported_run_qemu = sub {
3095 '-machine', $default_machine,
3097 '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
3098 '-mon', 'chardev=qmp,mode=control',
3099 '-pidfile', $pidfile,
3104 push @$cmd, '-accel', 'tcg';
3107 my $rc = run_command
($cmd, noerr
=> 1, quiet
=> 0);
3108 die "QEMU flag querying VM exited with code " . $rc if $rc;
3111 my $cmd_result = mon_cmd
(
3113 'query-cpu-model-expansion',
3115 model
=> { name
=> 'host' }
3118 my $props = $cmd_result->{model
}->{props
};
3119 foreach my $prop (keys %$props) {
3120 next if $props->{$prop} ne '1';
3121 # QEMU returns some flags multiple times, with '_', '.' or '-'
3122 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
3123 # We only keep those with underscores, to match /proc/cpuinfo
3124 $prop =~ s/\.|-/_/g;
3125 $flags->{$prop} = 1;
3130 # force stop with 10 sec timeout and 'nocheck'
3131 # always stop, even if QMP failed
3132 vm_stop
(undef, $fakevmid, 1, 1, 10, 0, 1);
3136 return [ sort keys %$flags ];
3139 # We need to query QEMU twice, since KVM and TCG have different supported flags
3140 PVE
::QemuConfig-
>lock_config($fakevmid, sub {
3141 $flags->{tcg
} = eval { $query_supported_run_qemu->(0) };
3142 warn "warning: failed querying supported tcg flags: $@\n" if $@;
3144 if ($kvm_supported) {
3145 $flags->{kvm
} = eval { $query_supported_run_qemu->(1) };
3146 warn "warning: failed querying supported kvm flags: $@\n" if $@;
3153 # Understood CPU flags are written to a file at 'pve-qemu' compile time
3154 my $understood_cpu_flag_dir = "/usr/share/kvm";
3155 sub query_understood_cpu_flags
{
3156 my $arch = get_host_arch
();
3157 my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
3159 die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
3162 my $raw = file_get_contents
($filepath);
3163 $raw =~ s/^\s+|\s+$//g;
3164 my @flags = split(/\s+/, $raw);
3169 sub config_to_command
{
3170 my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
3174 my $globalFlags = [];
3175 my $machineFlags = [];
3180 my $ostype = $conf->{ostype
};
3181 my $winversion = windows_version
($ostype);
3182 my $kvm = $conf->{kvm
};
3183 my $nodename = nodename
();
3185 my $arch = get_vm_arch
($conf);
3186 my $kvm_binary = get_command_for_arch
($arch);
3187 my $kvmver = kvm_user_version
($kvm_binary);
3189 if (!$kvmver || $kvmver !~ m/^(\d+)\.(\d+)/ || $1 < 3) {
3190 $kvmver //= "undefined";
3191 die "Detected old QEMU binary ('$kvmver', at least 3.0 is required)\n";
3194 my $add_pve_version = min_version
($kvmver, 4, 1);
3196 my $machine_type = get_vm_machine
($conf, $forcemachine, $arch, $add_pve_version);
3197 my $machine_version = extract_version
($machine_type, $kvmver);
3198 $kvm //= 1 if is_native
($arch);
3200 $machine_version =~ m/(\d+)\.(\d+)/;
3201 my ($machine_major, $machine_minor) = ($1, $2);
3203 if ($kvmver =~ m/^\d+\.\d+\.(\d+)/ && $1 >= 90) {
3204 warn "warning: Installed QEMU version ($kvmver) is a release candidate, ignoring version checks\n";
3205 } elsif (!min_version
($kvmver, $machine_major, $machine_minor)) {
3206 die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type',"
3207 ." please upgrade node '$nodename'\n"
3208 } elsif (!PVE
::QemuServer
::Machine
::can_run_pve_machine_version
($machine_version, $kvmver)) {
3209 my $max_pve_version = PVE
::QemuServer
::Machine
::get_pve_version
($machine_version);
3210 die "Installed qemu-server (max feature level for $machine_major.$machine_minor is"
3211 ." pve$max_pve_version) is too old to run machine type '$machine_type', please upgrade"
3212 ." node '$nodename'\n";
3215 # if a specific +pve version is required for a feature, use $version_guard
3216 # instead of min_version to allow machines to be run with the minimum
3218 my $required_pve_version = 0;
3219 my $version_guard = sub {
3220 my ($major, $minor, $pve) = @_;
3221 return 0 if !min_version
($machine_version, $major, $minor, $pve);
3222 my $max_pve = PVE
::QemuServer
::Machine
::get_pve_version
("$major.$minor");
3223 return 1 if min_version
($machine_version, $major, $minor, $max_pve+1);
3224 $required_pve_version = $pve if $pve && $pve > $required_pve_version;
3228 if ($kvm && !defined kvm_version
()) {
3229 die "KVM virtualisation configured, but not available. Either disable in VM configuration"
3230 ." or enable in BIOS.\n";
3233 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3234 my $hotplug_features = parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
3235 my $use_old_bios_files = undef;
3236 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3238 my $cpuunits = defined($conf->{cpuunits
}) ?
3239 $conf->{cpuunits
} : $defaults->{cpuunits
};
3241 push @$cmd, $kvm_binary;
3243 push @$cmd, '-id', $vmid;
3245 my $vmname = $conf->{name
} || "vm$vmid";
3247 push @$cmd, '-name', $vmname;
3249 push @$cmd, '-no-shutdown';
3253 my $qmpsocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid);
3254 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server=on,wait=off";
3255 push @$cmd, '-mon', "chardev=qmp,mode=control";
3257 if (min_version
($machine_version, 2, 12)) {
3258 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3259 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3262 push @$cmd, '-pidfile' , PVE
::QemuServer
::Helpers
::pidfile_name
($vmid);
3264 push @$cmd, '-daemonize';
3266 if ($conf->{smbios1
}) {
3267 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
3268 if ($smbios_conf->{base64
}) {
3269 # Do not pass base64 flag to qemu
3270 delete $smbios_conf->{base64
};
3271 my $smbios_string = "";
3272 foreach my $key (keys %$smbios_conf) {
3274 if ($key eq "uuid") {
3275 $value = $smbios_conf->{uuid
}
3277 $value = decode_base64
($smbios_conf->{$key});
3279 # qemu accepts any binary data, only commas need escaping by double comma
3281 $smbios_string .= "," . $key . "=" . $value if $value;
3283 push @$cmd, '-smbios', "type=1" . $smbios_string;
3285 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3289 if ($conf->{bios
} && $conf->{bios
} eq 'ovmf') {
3290 my ($ovmf_code, $ovmf_vars) = get_ovmf_files
($arch);
3291 die "uefi base image '$ovmf_code' not found\n" if ! -f
$ovmf_code;
3293 my ($path, $format);
3294 if (my $efidisk = $conf->{efidisk0
}) {
3295 my $d = parse_drive
('efidisk0', $efidisk);
3296 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($d->{file
}, 1);
3297 $format = $d->{format
};
3299 $path = PVE
::Storage
::path
($storecfg, $d->{file
});
3300 if (!defined($format)) {
3301 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
3302 $format = qemu_img_format
($scfg, $volname);
3306 die "efidisk format must be specified\n"
3307 if !defined($format);
3310 warn "no efidisk configured! Using temporary efivars disk.\n";
3311 $path = "/tmp/$vmid-ovmf.fd";
3312 PVE
::Tools
::file_copy
($ovmf_vars, $path, -s
$ovmf_vars);
3318 if ($format eq 'raw' && $version_guard->(4, 1, 2)) {
3319 $size_str = ",size=" . (-s
$ovmf_vars);
3322 # on slower ceph clusters, booting without cache on efidisk can take a while, see #3329
3323 my $cache = $path =~ m/^rbd:/ ?
',cache=writeback' : '';
3325 push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code";
3326 push @$cmd, '-drive', "if=pflash,unit=1$cache,format=$format,id=drive-efidisk0$size_str,file=$path";
3331 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3332 if (min_version
($machine_version, 4, 0)) {
3333 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3335 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3339 if ($conf->{vmgenid
}) {
3340 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid
};
3343 # add usb controllers
3344 my @usbcontrollers = PVE
::QemuServer
::USB
::get_usb_controllers
(
3345 $conf, $bridges, $arch, $machine_type, $usbdesc->{format
}, $MAX_USB_DEVICES);
3346 push @$devices, @usbcontrollers if @usbcontrollers;
3347 my $vga = parse_vga
($conf->{vga
});
3349 my $qxlnum = vga_conf_has_spice
($conf->{vga
});
3350 $vga->{type
} = 'qxl' if $qxlnum;
3352 if (!$vga->{type
}) {
3353 if ($arch eq 'aarch64') {
3354 $vga->{type
} = 'virtio';
3355 } elsif (min_version
($machine_version, 2, 9)) {
3356 $vga->{type
} = (!$winversion || $winversion >= 6) ?
'std' : 'cirrus';
3358 $vga->{type
} = ($winversion >= 6) ?
'std' : 'cirrus';
3362 # enable absolute mouse coordinates (needed by vnc)
3364 if (defined($conf->{tablet
})) {
3365 $tablet = $conf->{tablet
};
3367 $tablet = $defaults->{tablet
};
3368 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3369 $tablet = 0 if $vga->{type
} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3373 push @$devices, '-device', print_tabletdevice_full
($conf, $arch) if $tablet;
3374 my $kbd = print_keyboarddevice_full
($conf, $arch);
3375 push @$devices, '-device', $kbd if defined($kbd);
3378 my $bootorder = device_bootorder
($conf);
3380 # host pci device passthrough
3381 my ($kvm_off, $gpu_passthrough, $legacy_igd) = PVE
::QemuServer
::PCI
::print_hostpci_devices
(
3382 $vmid, $conf, $devices, $vga, $winversion, $q35, $bridges, $arch, $machine_type, $bootorder);
3385 my $usb_dev_features = {};
3386 $usb_dev_features->{spice_usb3
} = 1 if min_version
($machine_version, 4, 0);
3388 my @usbdevices = PVE
::QemuServer
::USB
::get_usb_devices
(
3389 $conf, $usbdesc->{format
}, $MAX_USB_DEVICES, $usb_dev_features, $bootorder);
3390 push @$devices, @usbdevices if @usbdevices;
3393 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3394 if (my $path = $conf->{"serial$i"}) {
3395 if ($path eq 'socket') {
3396 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3397 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
3398 # On aarch64, serial0 is the UART device. Qemu only allows
3399 # connecting UART devices via the '-serial' command line, as
3400 # the device has a fixed slot on the hardware...
3401 if ($arch eq 'aarch64' && $i == 0) {
3402 push @$devices, '-serial', "chardev:serial$i";
3404 push @$devices, '-device', "isa-serial,chardev=serial$i";
3407 die "no such serial device\n" if ! -c
$path;
3408 push @$devices, '-chardev', "tty,id=serial$i,path=$path";
3409 push @$devices, '-device', "isa-serial,chardev=serial$i";
3415 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3416 if (my $path = $conf->{"parallel$i"}) {
3417 die "no such parallel device\n" if ! -c
$path;
3418 my $devtype = $path =~ m!^/dev/usb/lp! ?
'tty' : 'parport';
3419 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3420 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3424 if (min_version
($machine_version, 4, 0) && (my $audio = conf_has_audio
($conf))) {
3425 my $audiopciaddr = print_pci_addr
("audio0", $bridges, $arch, $machine_type);
3426 my $audio_devs = audio_devs
($audio, $audiopciaddr, $machine_version);
3427 push @$devices, @$audio_devs;
3431 $sockets = $conf->{smp
} if $conf->{smp
}; # old style - no longer iused
3432 $sockets = $conf->{sockets
} if $conf->{sockets
};
3434 my $cores = $conf->{cores
} || 1;
3436 my $maxcpus = $sockets * $cores;
3438 my $vcpus = $conf->{vcpus
} ?
$conf->{vcpus
} : $maxcpus;
3440 my $allowed_vcpus = $cpuinfo->{cpus
};
3442 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n"
3443 if ($allowed_vcpus < $maxcpus);
3445 if($hotplug_features->{cpu
} && min_version
($machine_version, 2, 7)) {
3447 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3448 for (my $i = 2; $i <= $vcpus; $i++) {
3449 my $cpustr = print_cpu_device
($conf,$i);
3450 push @$cmd, '-device', $cpustr;
3455 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3457 push @$cmd, '-nodefaults';
3459 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3461 push @$cmd, '-no-acpi' if defined($conf->{acpi
}) && $conf->{acpi
} == 0;
3463 push @$cmd, '-no-reboot' if defined($conf->{reboot
}) && $conf->{reboot
} == 0;
3465 if ($vga->{type
} && $vga->{type
} !~ m/^serial\d+$/ && $vga->{type
} ne 'none'){
3466 push @$devices, '-device', print_vga_device
(
3467 $conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
3468 my $socket = PVE
::QemuServer
::Helpers
::vnc_socket
($vmid);
3469 push @$cmd, '-vnc', "unix:$socket,password=on";
3471 push @$cmd, '-vga', 'none' if $vga->{type
} eq 'none';
3472 push @$cmd, '-nographic';
3476 my $tdf = defined($conf->{tdf
}) ?
$conf->{tdf
} : $defaults->{tdf
};
3477 my $useLocaltime = $conf->{localtime};
3479 if ($winversion >= 5) { # windows
3480 $useLocaltime = 1 if !defined($conf->{localtime});
3482 # use time drift fix when acpi is enabled
3483 if (!(defined($conf->{acpi
}) && $conf->{acpi
} == 0)) {
3484 $tdf = 1 if !defined($conf->{tdf
});
3488 if ($winversion >= 6) {
3489 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3490 push @$cmd, '-no-hpet';
3493 push @$rtcFlags, 'driftfix=slew' if $tdf;
3495 if ($conf->{startdate
} && $conf->{startdate
} ne 'now') {
3496 push @$rtcFlags, "base=$conf->{startdate}";
3497 } elsif ($useLocaltime) {
3498 push @$rtcFlags, 'base=localtime';
3502 push @$cmd, '-cpu', $forcecpu;
3504 push @$cmd, get_cpu_options
($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
3507 PVE
::QemuServer
::Memory
::config
($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
3509 push @$cmd, '-S' if $conf->{freeze
};
3511 push @$cmd, '-k', $conf->{keyboard
} if defined($conf->{keyboard
});
3513 my $guest_agent = parse_guest_agent
($conf);
3515 if ($guest_agent->{enabled
}) {
3516 my $qgasocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid, 1);
3517 push @$devices, '-chardev', "socket,path=$qgasocket,server=on,wait=off,id=qga0";
3519 if (!$guest_agent->{type
} || $guest_agent->{type
} eq 'virtio') {
3520 my $pciaddr = print_pci_addr
("qga0", $bridges, $arch, $machine_type);
3521 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3522 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3523 } elsif ($guest_agent->{type
} eq 'isa') {
3524 push @$devices, '-device', "isa-serial,chardev=qga0";
3528 my $rng = $conf->{rng0
} ? parse_rng
($conf->{rng0
}) : undef;
3529 if ($rng && $version_guard->(4, 1, 2)) {
3530 check_rng_source
($rng->{source
});
3532 my $max_bytes = $rng->{max_bytes
} // $rng_fmt->{max_bytes
}->{default};
3533 my $period = $rng->{period
} // $rng_fmt->{period
}->{default};
3534 my $limiter_str = "";
3536 $limiter_str = ",max-bytes=$max_bytes,period=$period";
3539 my $rng_addr = print_pci_addr
("rng0", $bridges, $arch, $machine_type);
3540 push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
3541 push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
3549 for (my $i = 1; $i < $qxlnum; $i++){
3550 push @$devices, '-device', print_vga_device
(
3551 $conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
3554 # assume other OS works like Linux
3555 my ($ram, $vram) = ("134217728", "67108864");
3556 if ($vga->{memory
}) {
3557 $ram = PVE
::Tools
::convert_size
($qxlnum*4*$vga->{memory
}, 'mb' => 'b');
3558 $vram = PVE
::Tools
::convert_size
($qxlnum*2*$vga->{memory
}, 'mb' => 'b');
3560 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3561 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3565 my $pciaddr = print_pci_addr
("spice", $bridges, $arch, $machine_type);
3567 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
3568 my @nodeaddrs = PVE
::Tools
::getaddrinfo_all
('localhost', family
=> $pfamily);
3569 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3571 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3572 push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
3573 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3575 my $localhost = PVE
::Network
::addr_to_ip
($nodeaddrs[0]->{addr
});
3576 $spice_port = PVE
::Tools
::next_spice_port
($pfamily, $localhost);
3578 my $spice_enhancement_str = $conf->{spice_enhancements
} // '';
3579 my $spice_enhancement = parse_property_string
($spice_enhancements_fmt, $spice_enhancement_str);
3580 if ($spice_enhancement->{foldersharing
}) {
3581 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3582 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3585 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3586 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}"
3587 if $spice_enhancement->{videostreaming
};
3589 push @$devices, '-spice', "$spice_opts";
3592 # enable balloon by default, unless explicitly disabled
3593 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
3594 $pciaddr = print_pci_addr
("balloon0", $bridges, $arch, $machine_type);
3595 push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr";
3598 if ($conf->{watchdog
}) {
3599 my $wdopts = parse_watchdog
($conf->{watchdog
});
3600 $pciaddr = print_pci_addr
("watchdog", $bridges, $arch, $machine_type);
3601 my $watchdog = $wdopts->{model
} || 'i6300esb';
3602 push @$devices, '-device', "$watchdog$pciaddr";
3603 push @$devices, '-watchdog-action', $wdopts->{action
} if $wdopts->{action
};
3607 my $scsicontroller = {};
3608 my $ahcicontroller = {};
3609 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : $defaults->{scsihw
};
3611 # Add iscsi initiator name if available
3612 if (my $initiator = get_initiator_name
()) {
3613 push @$devices, '-iscsi', "initiator-name=$initiator";
3616 PVE
::QemuConfig-
>foreach_volume($conf, sub {
3617 my ($ds, $drive) = @_;
3619 if (PVE
::Storage
::parse_volume_id
($drive->{file
}, 1)) {
3620 check_volume_storage_type
($storecfg, $drive->{file
});
3621 push @$vollist, $drive->{file
};
3624 # ignore efidisk here, already added in bios/fw handling code above
3625 return if $drive->{interface
} eq 'efidisk';
3627 $use_virtio = 1 if $ds =~ m/^virtio/;
3629 $drive->{bootindex
} = $bootorder->{$ds} if $bootorder->{$ds};
3631 if ($drive->{interface
} eq 'virtio'){
3632 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread
};
3635 if ($drive->{interface
} eq 'scsi') {
3637 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
3639 die "scsi$drive->{index}: machine version 4.1~pve2 or higher is required to use more than 14 SCSI disks\n"
3640 if $drive->{index} > 13 && !&$version_guard(4, 1, 2);
3642 $pciaddr = print_pci_addr
("$controller_prefix$controller", $bridges, $arch, $machine_type);
3643 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ?
"virtio-scsi-pci" : $scsihw;
3646 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{iothread
}){
3647 $iothread .= ",iothread=iothread-$controller_prefix$controller";
3648 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
3649 } elsif ($drive->{iothread
}) {
3650 warn "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n";
3654 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{queues
}){
3655 $queues = ",num_queues=$drive->{queues}";
3658 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues"
3659 if !$scsicontroller->{$controller};
3660 $scsicontroller->{$controller}=1;
3663 if ($drive->{interface
} eq 'sata') {
3664 my $controller = int($drive->{index} / $PVE::QemuServer
::Drive
::MAX_SATA_DISKS
);
3665 $pciaddr = print_pci_addr
("ahci$controller", $bridges, $arch, $machine_type);
3666 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr"
3667 if !$ahcicontroller->{$controller};
3668 $ahcicontroller->{$controller}=1;
3671 my $pbs_conf = $pbs_backing->{$ds};
3672 my $pbs_name = undef;
3674 $pbs_name = "drive-$ds-pbs";
3675 push @$devices, '-blockdev', print_pbs_blockdev
($pbs_conf, $pbs_name);
3678 my $drive_cmd = print_drive_commandline_full
(
3679 $storecfg, $vmid, $drive, $pbs_name, min_version
($kvmver, 6, 0));
3681 # extra protection for templates, but SATA and IDE don't support it..
3682 $drive_cmd .= ',readonly=on' if drive_is_read_only
($conf, $drive);
3684 push @$devices, '-drive',$drive_cmd;
3685 push @$devices, '-device', print_drivedevice_full
(
3686 $storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
3689 for (my $i = 0; $i < $MAX_NETS; $i++) {
3690 my $netname = "net$i";
3692 next if !$conf->{$netname};
3693 my $d = parse_net
($conf->{$netname});
3696 $use_virtio = 1 if $d->{model
} eq 'virtio';
3698 $d->{bootindex
} = $bootorder->{$netname} if $bootorder->{$netname};
3700 my $netdevfull = print_netdev_full
($vmid, $conf, $arch, $d, $netname);
3701 push @$devices, '-netdev', $netdevfull;
3703 my $netdevicefull = print_netdevice_full
(
3704 $vmid, $conf, $d, $netname, $bridges, $use_old_bios_files, $arch, $machine_type);
3706 push @$devices, '-device', $netdevicefull;
3709 if ($conf->{ivshmem
}) {
3710 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
3714 $bus = print_pcie_addr
("ivshmem");
3716 $bus = print_pci_addr
("ivshmem", $bridges, $arch, $machine_type);
3719 my $ivshmem_name = $ivshmem->{name
} // $vmid;
3720 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
3722 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
3723 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path"
3724 .",size=$ivshmem->{size}M";
3727 # pci.4 is nested in pci.1
3728 $bridges->{1} = 1 if $bridges->{4};
3732 if (min_version
($machine_version, 2, 3)) {
3737 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
3741 for my $k (sort {$b cmp $a} keys %$bridges) {
3742 next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
3745 if ($k == 2 && $legacy_igd) {
3748 $pciaddr = print_pci_addr
("pci.$k_name", undef, $arch, $machine_type);
3750 my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
3752 # add after -readconfig pve-q35.cfg
3753 splice @$devices, 2, 0, '-device', $devstr;
3755 unshift @$devices, '-device', $devstr if $k > 0;
3760 push @$machineFlags, 'accel=tcg';
3763 my $machine_type_min = $machine_type;
3764 if ($add_pve_version) {
3765 $machine_type_min =~ s/\+pve\d+$//;
3766 $machine_type_min .= "+pve$required_pve_version";
3768 push @$machineFlags, "type=${machine_type_min}";
3770 push @$cmd, @$devices;
3771 push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
3772 push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
3773 push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags);
3775 if (my $vmstate = $conf->{vmstate
}) {
3776 my $statepath = PVE
::Storage
::path
($storecfg, $vmstate);
3777 push @$vollist, $vmstate;
3778 push @$cmd, '-loadstate', $statepath;
3779 print "activating and using '$vmstate' as vmstate\n";
3783 if ($conf->{args
}) {
3784 my $aa = PVE
::Tools
::split_args
($conf->{args
});
3788 return wantarray ?
($cmd, $vollist, $spice_port) : $cmd;
3791 sub check_rng_source
{
3794 # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
3795 die "cannot create VirtIO RNG device: source file '$source' doesn't exist\n"
3798 my $rng_current = '/sys/devices/virtual/misc/hw_random/rng_current';
3799 if ($source eq '/dev/hwrng' && file_read_firstline
($rng_current) eq 'none') {
3800 # Needs to abort, otherwise QEMU crashes on first rng access. Note that rng_current cannot
3801 # be changed to 'none' manually, so once the VM is past this point, it's no longer an issue.
3802 die "Cannot start VM with passed-through RNG device: '/dev/hwrng' exists, but"
3803 ." '$rng_current' is set to 'none'. Ensure that a compatible hardware-RNG is attached"
3811 my $res = mon_cmd
($vmid, 'query-spice');
3813 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
3816 sub vm_devices_list
{
3819 my $res = mon_cmd
($vmid, 'query-pci');
3820 my $devices_to_check = [];
3822 foreach my $pcibus (@$res) {
3823 push @$devices_to_check, @{$pcibus->{devices
}},
3826 while (@$devices_to_check) {
3828 for my $d (@$devices_to_check) {
3829 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
3830 next if !$d->{'pci_bridge'};
3832 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices
}});
3833 push @$to_check, @{$d->{'pci_bridge'}->{devices
}};
3835 $devices_to_check = $to_check;
3838 my $resblock = mon_cmd
($vmid, 'query-block');
3839 foreach my $block (@$resblock) {
3840 if($block->{device
} =~ m/^drive-(\S+)/){
3845 my $resmice = mon_cmd
($vmid, 'query-mice');
3846 foreach my $mice (@$resmice) {
3847 if ($mice->{name
} eq 'QEMU HID Tablet') {
3848 $devices->{tablet
} = 1;
3853 # for usb devices there is no query-usb
3854 # but we can iterate over the entries in
3855 # qom-list path=/machine/peripheral
3856 my $resperipheral = mon_cmd
($vmid, 'qom-list', path
=> '/machine/peripheral');
3857 foreach my $per (@$resperipheral) {
3858 if ($per->{name
} =~ m/^usb\d+$/) {
3859 $devices->{$per->{name
}} = 1;
3867 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
3869 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3871 my $devices_list = vm_devices_list
($vmid);
3872 return 1 if defined($devices_list->{$deviceid});
3874 # add PCI bridge if we need it for the device
3875 qemu_add_pci_bridge
($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type);
3877 if ($deviceid eq 'tablet') {
3879 qemu_deviceadd
($vmid, print_tabletdevice_full
($conf, $arch));
3881 } elsif ($deviceid eq 'keyboard') {
3883 qemu_deviceadd
($vmid, print_keyboarddevice_full
($conf, $arch));
3885 } elsif ($deviceid =~ m/^usb(\d+)$/) {
3887 die "usb hotplug currently not reliable\n";
3888 # since we can't reliably hot unplug all added usb devices and usb
3889 # passthrough breaks live migration we disable usb hotplugging for now
3890 #qemu_deviceadd($vmid, PVE::QemuServer::USB::print_usbdevice_full($conf, $deviceid, $device));
3892 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
3894 qemu_iothread_add
($vmid, $deviceid, $device);
3896 qemu_driveadd
($storecfg, $vmid, $device);
3897 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
3899 qemu_deviceadd
($vmid, $devicefull);
3900 eval { qemu_deviceaddverify
($vmid, $deviceid); };
3902 eval { qemu_drivedel
($vmid, $deviceid); };
3907 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
3910 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : "lsi";
3911 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
3912 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ?
"virtio-scsi-pci" : $scsihw;
3914 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
3916 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread
}) {
3917 qemu_iothread_add
($vmid, $deviceid, $device);
3918 $devicefull .= ",iothread=iothread-$deviceid";
3921 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues
}) {
3922 $devicefull .= ",num_queues=$device->{queues}";
3925 qemu_deviceadd
($vmid, $devicefull);
3926 qemu_deviceaddverify
($vmid, $deviceid);
3928 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
3930 qemu_findorcreatescsihw
($storecfg,$conf, $vmid, $device, $arch, $machine_type);
3931 qemu_driveadd
($storecfg, $vmid, $device);
3933 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
3934 eval { qemu_deviceadd
($vmid, $devicefull); };
3936 eval { qemu_drivedel
($vmid, $deviceid); };
3941 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
3943 return if !qemu_netdevadd
($vmid, $conf, $arch, $device, $deviceid);
3945 my $machine_type = PVE
::QemuServer
::Machine
::qemu_machine_pxe
($vmid, $conf);
3946 my $use_old_bios_files = undef;
3947 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3949 my $netdevicefull = print_netdevice_full
(
3950 $vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type);
3951 qemu_deviceadd
($vmid, $netdevicefull);
3953 qemu_deviceaddverify
($vmid, $deviceid);
3954 qemu_set_link_status
($vmid, $deviceid, !$device->{link_down
});
3957 eval { qemu_netdevdel
($vmid, $deviceid); };
3962 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
3965 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
3966 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
3968 qemu_deviceadd
($vmid, $devicefull);
3969 qemu_deviceaddverify
($vmid, $deviceid);
3972 die "can't hotplug device '$deviceid'\n";
3978 # fixme: this should raise exceptions on error!
3979 sub vm_deviceunplug
{
3980 my ($vmid, $conf, $deviceid) = @_;
3982 my $devices_list = vm_devices_list
($vmid);
3983 return 1 if !defined($devices_list->{$deviceid});
3985 my $bootdisks = PVE
::QemuServer
::Drive
::get_bootdisks
($conf);
3986 die "can't unplug bootdisk '$deviceid'\n" if grep {$_ eq $deviceid} @$bootdisks;
3988 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard') {
3990 qemu_devicedel
($vmid, $deviceid);
3992 } elsif ($deviceid =~ m/^usb\d+$/) {
3994 die "usb hotplug currently not reliable\n";
3995 # when unplugging usb devices this way, there may be remaining usb
3996 # controllers/hubs so we disable it for now
3997 #qemu_devicedel($vmid, $deviceid);
3998 #qemu_devicedelverify($vmid, $deviceid);
4000 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4002 qemu_devicedel
($vmid, $deviceid);
4003 qemu_devicedelverify
($vmid, $deviceid);
4004 qemu_drivedel
($vmid, $deviceid);
4005 qemu_iothread_del
($conf, $vmid, $deviceid);
4007 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4009 qemu_devicedel
($vmid, $deviceid);
4010 qemu_devicedelverify
($vmid, $deviceid);
4011 qemu_iothread_del
($conf, $vmid, $deviceid);
4013 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4015 qemu_devicedel
($vmid, $deviceid);
4016 qemu_drivedel
($vmid, $deviceid);
4017 qemu_deletescsihw
($conf, $vmid, $deviceid);
4019 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4021 qemu_devicedel
($vmid, $deviceid);
4022 qemu_devicedelverify
($vmid, $deviceid);
4023 qemu_netdevdel
($vmid, $deviceid);
4026 die "can't unplug device '$deviceid'\n";
4032 sub qemu_deviceadd
{
4033 my ($vmid, $devicefull) = @_;
4035 $devicefull = "driver=".$devicefull;
4036 my %options = split(/[=,]/, $devicefull);
4038 mon_cmd
($vmid, "device_add" , %options);
4041 sub qemu_devicedel
{
4042 my ($vmid, $deviceid) = @_;
4044 my $ret = mon_cmd
($vmid, "device_del", id
=> $deviceid);
4047 sub qemu_iothread_add
{
4048 my($vmid, $deviceid, $device) = @_;
4050 if ($device->{iothread
}) {
4051 my $iothreads = vm_iothreads_list
($vmid);
4052 qemu_objectadd
($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4056 sub qemu_iothread_del
{
4057 my($conf, $vmid, $deviceid) = @_;
4059 my $confid = $deviceid;
4060 if ($deviceid =~ m/^(?:virtioscsi|scsihw)(\d+)$/) {
4061 $confid = 'scsi' . $1;
4063 my $device = parse_drive
($confid, $conf->{$confid});
4064 if ($device->{iothread
}) {
4065 my $iothreads = vm_iothreads_list
($vmid);
4066 qemu_objectdel
($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4070 sub qemu_objectadd
{
4071 my($vmid, $objectid, $qomtype) = @_;
4073 mon_cmd
($vmid, "object-add", id
=> $objectid, "qom-type" => $qomtype);
4078 sub qemu_objectdel
{
4079 my($vmid, $objectid) = @_;
4081 mon_cmd
($vmid, "object-del", id
=> $objectid);
4087 my ($storecfg, $vmid, $device) = @_;
4089 my $kvmver = get_running_qemu_version
($vmid);
4090 my $io_uring = min_version
($kvmver, 6, 0);
4091 my $drive = print_drive_commandline_full
($storecfg, $vmid, $device, undef, $io_uring);
4092 $drive =~ s/\\/\\\\/g;
4093 my $ret = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "drive_add auto \"$drive\"");
4095 # If the command succeeds qemu prints: "OK
"
4096 return 1 if $ret =~ m/OK/s;
4098 die "adding drive failed
: $ret\n";
4102 my($vmid, $deviceid) = @_;
4104 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-
$deviceid");
4107 return 1 if $ret eq "";
4109 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4110 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4112 die "deleting drive
$deviceid failed
: $ret\n";
4115 sub qemu_deviceaddverify {
4116 my ($vmid, $deviceid) = @_;
4118 for (my $i = 0; $i <= 5; $i++) {
4119 my $devices_list = vm_devices_list($vmid);
4120 return 1 if defined($devices_list->{$deviceid});
4124 die "error on hotplug device
'$deviceid'\n";
4128 sub qemu_devicedelverify {
4129 my ($vmid, $deviceid) = @_;
4131 # need to verify that the device is correctly removed as device_del
4132 # is async and empty return is not reliable
4134 for (my $i = 0; $i <= 5; $i++) {
4135 my $devices_list = vm_devices_list($vmid);
4136 return 1 if !defined($devices_list->{$deviceid});
4140 die "error on hot-unplugging device
'$deviceid'\n";
4143 sub qemu_findorcreatescsihw {
4144 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4146 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4148 my $scsihwid="$controller_prefix$controller";
4149 my $devices_list = vm_devices_list($vmid);
4151 if(!defined($devices_list->{$scsihwid})) {
4152 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4158 sub qemu_deletescsihw {
4159 my ($conf, $vmid, $opt) = @_;
4161 my $device = parse_drive($opt, $conf->{$opt});
4163 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4164 vm_deviceunplug($vmid, $conf, "virtioscsi
$device->{index}");
4168 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4170 my $devices_list = vm_devices_list($vmid);
4171 foreach my $opt (keys %{$devices_list}) {
4172 if (is_valid_drivename($opt)) {
4173 my $drive = parse_drive($opt, $conf->{$opt});
4174 if($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4180 my $scsihwid="scsihw
$controller";
4182 vm_deviceunplug($vmid, $conf, $scsihwid);
4187 sub qemu_add_pci_bridge {
4188 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4194 print_pci_addr($device, $bridges, $arch, $machine_type);
4196 while (my ($k, $v) = each %$bridges) {
4199 return 1 if !defined($bridgeid) || $bridgeid < 1;
4201 my $bridge = "pci
.$bridgeid";
4202 my $devices_list = vm_devices_list($vmid);
4204 if (!defined($devices_list->{$bridge})) {
4205 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4211 sub qemu_set_link_status {
4212 my ($vmid, $device, $up) = @_;
4214 mon_cmd($vmid, "set_link
", name => $device,
4215 up => $up ? JSON::true : JSON::false);
4218 sub qemu_netdevadd {
4219 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4221 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4222 my %options = split(/[=,]/, $netdev);
4224 if (defined(my $vhost = $options{vhost})) {
4225 $options{vhost} = JSON::boolean(PVE::JSONSchema::parse_boolean($vhost));
4228 if (defined(my $queues = $options{queues})) {
4229 $options{queues} = $queues + 0;
4232 mon_cmd($vmid, "netdev_add
", %options);
4236 sub qemu_netdevdel {
4237 my ($vmid, $deviceid) = @_;
4239 mon_cmd($vmid, "netdev_del
", id => $deviceid);
4242 sub qemu_usb_hotplug {
4243 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4247 # remove the old one first
4248 vm_deviceunplug($vmid, $conf, $deviceid);
4250 # check if xhci controller is necessary and available
4251 if ($device->{usb3}) {
4253 my $devicelist = vm_devices_list($vmid);
4255 if (!$devicelist->{xhci}) {
4256 my $pciaddr = print_pci_addr("xhci
", undef, $arch, $machine_type);
4257 qemu_deviceadd($vmid, "nec-usb-xhci
,id
=xhci
$pciaddr");
4260 my $d = parse_usb_device($device->{host});
4261 $d->{usb3} = $device->{usb3};
4264 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d, $arch, $machine_type);
4267 sub qemu_cpu_hotplug {
4268 my ($vmid, $conf, $vcpus) = @_;
4270 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
4273 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4274 $sockets = $conf->{sockets} if $conf->{sockets};
4275 my $cores = $conf->{cores} || 1;
4276 my $maxcpus = $sockets * $cores;
4278 $vcpus = $maxcpus if !$vcpus;
4280 die "you can
't add more vcpus than maxcpus\n"
4281 if $vcpus > $maxcpus;
4283 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4285 if ($vcpus < $currentvcpus) {
4287 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4289 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4290 qemu_devicedel($vmid, "cpu$i");
4292 my $currentrunningvcpus = undef;
4294 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4295 last if scalar(@{$currentrunningvcpus}) == $i-1;
4296 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4300 #update conf after each succesfull cpu unplug
4301 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4302 PVE::QemuConfig->write_config($vmid, $conf);
4305 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4311 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4312 die "vcpus in running vm does not match its configuration\n"
4313 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4315 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4317 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4318 my $cpustr = print_cpu_device($conf, $i);
4319 qemu_deviceadd($vmid, $cpustr);
4322 my $currentrunningvcpus = undef;
4324 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4325 last if scalar(@{$currentrunningvcpus}) == $i;
4326 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4330 #update conf after each succesfull cpu hotplug
4331 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4332 PVE::QemuConfig->write_config($vmid, $conf);
4336 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4337 mon_cmd($vmid, "cpu-add", id => int($i));
4342 sub qemu_block_set_io_throttle {
4343 my ($vmid, $deviceid,
4344 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4345 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4346 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4347 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4349 return if !check_running($vmid) ;
4351 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4353 bps_rd => int($bps_rd),
4354 bps_wr => int($bps_wr),
4356 iops_rd => int($iops_rd),
4357 iops_wr => int($iops_wr),
4358 bps_max => int($bps_max),
4359 bps_rd_max => int($bps_rd_max),
4360 bps_wr_max => int($bps_wr_max),
4361 iops_max => int($iops_max),
4362 iops_rd_max => int($iops_rd_max),
4363 iops_wr_max => int($iops_wr_max),
4364 bps_max_length => int($bps_max_length),
4365 bps_rd_max_length => int($bps_rd_max_length),
4366 bps_wr_max_length => int($bps_wr_max_length),
4367 iops_max_length => int($iops_max_length),
4368 iops_rd_max_length => int($iops_rd_max_length),
4369 iops_wr_max_length => int($iops_wr_max_length),
4374 sub qemu_block_resize {
4375 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4377 my $running = check_running($vmid);
4379 $size = 0 if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4381 return if !$running;
4383 my $padding = (1024 - $size % 1024) % 1024;
4384 $size = $size + $padding;
4389 device => $deviceid,
4395 sub qemu_volume_snapshot {
4396 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4398 my $running = check_running($vmid);
4400 if ($running && do_snapshots_with_qemu($storecfg, $volid)){
4401 mon_cmd($vmid, 'blockdev-snapshot-internal-sync
', device => $deviceid, name => $snap);
4403 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4407 sub qemu_volume_snapshot_delete {
4408 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4410 my $running = check_running($vmid);
4415 my $conf = PVE::QemuConfig->load_config($vmid);
4416 PVE::QemuConfig->foreach_volume($conf, sub {
4417 my ($ds, $drive) = @_;
4418 $running = 1 if $drive->{file} eq $volid;
4422 if ($running && do_snapshots_with_qemu($storecfg, $volid)){
4423 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync
', device => $deviceid, name => $snap);
4425 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4429 sub set_migration_caps {
4430 my ($vmid, $savevm) = @_;
4432 my $qemu_support = eval { mon_cmd($vmid, "query-proxmox-support") };
4434 my $bitmap_prop = $savevm ? 'pbs-dirty-bitmap-savevm
' : 'pbs-dirty-bitmap-migration
';
4435 my $dirty_bitmaps = $qemu_support->{$bitmap_prop} ? 1 : 0;
4440 "auto-converge" => 1,
4442 "x-rdma-pin-all" => 0,
4445 "dirty-bitmaps" => $dirty_bitmaps,
4448 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4450 for my $supported_capability (@$supported_capabilities) {
4452 capability => $supported_capability->{capability},
4453 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4457 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4461 my ($conf, $func, @param) = @_;
4465 my $test_volid = sub {
4466 my ($key, $drive, $snapname) = @_;
4468 my $volid = $drive->{file};
4471 $volhash->{$volid}->{cdrom} //= 1;
4472 $volhash->{$volid}->{cdrom} = 0 if !drive_is_cdrom($drive);
4474 my $replicate = $drive->{replicate} // 1;
4475 $volhash->{$volid}->{replicate} //= 0;
4476 $volhash->{$volid}->{replicate} = 1 if $replicate;
4478 $volhash->{$volid}->{shared} //= 0;
4479 $volhash->{$volid}->{shared} = 1 if $drive->{shared};
4481 $volhash->{$volid}->{referenced_in_config} //= 0;
4482 $volhash->{$volid}->{referenced_in_config} = 1 if !defined($snapname);
4484 $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
4485 if defined($snapname);
4487 my $size = $drive->{size};
4488 $volhash->{$volid}->{size} //= $size if $size;
4490 $volhash->{$volid}->{is_vmstate} //= 0;
4491 $volhash->{$volid}->{is_vmstate} = 1 if $key eq 'vmstate
';
4493 $volhash->{$volid}->{is_unused} //= 0;
4494 $volhash->{$volid}->{is_unused} = 1 if $key =~ /^unused\d+$/;
4496 $volhash->{$volid}->{drivename} = $key if is_valid_drivename($key);
4499 my $include_opts = {
4500 extra_keys => ['vmstate
'],
4501 include_unused => 1,
4504 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $test_volid);
4505 foreach my $snapname (keys %{$conf->{snapshots}}) {
4506 my $snap = $conf->{snapshots}->{$snapname};
4507 PVE::QemuConfig->foreach_volume_full($snap, $include_opts, $test_volid, $snapname);
4510 foreach my $volid (keys %$volhash) {
4511 &$func($volid, $volhash->{$volid}, @param);
4515 my $fast_plug_option = {
4523 'vmstatestorage
' => 1,
4528 # hotplug changes in [PENDING]
4529 # $selection hash can be used to only apply specified options, for
4530 # example: { cores => 1 } (only apply changed 'cores
')
4531 # $errors ref is used to return error messages
4532 sub vmconfig_hotplug_pending {
4533 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4535 my $defaults = load_defaults();
4536 my $arch = get_vm_arch($conf);
4537 my $machine_type = get_vm_machine($conf, undef, $arch);
4539 # commit values which do not have any impact on running VM first
4540 # Note: those option cannot raise errors, we we do not care about
4541 # $selection and always apply them.
4543 my $add_error = sub {
4544 my ($opt, $msg) = @_;
4545 $errors->{$opt} = "hotplug problem - $msg";
4549 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4550 if ($fast_plug_option->{$opt}) {
4551 $conf->{$opt} = $conf->{pending}->{$opt};
4552 delete $conf->{pending}->{$opt};
4558 PVE::QemuConfig->write_config($vmid, $conf);
4561 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
4563 my $cgroup = PVE::QemuServer::CGroup->new($vmid);
4564 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4565 foreach my $opt (sort keys %$pending_delete_hash) {
4566 next if $selection && !$selection->{$opt};
4567 my $force = $pending_delete_hash->{$opt}->{force};
4569 if ($opt eq 'hotplug
') {
4570 die "skip\n" if ($conf->{hotplug} =~ /memory/);
4571 } elsif ($opt eq 'tablet
') {
4572 die "skip\n" if !$hotplug_features->{usb};
4573 if ($defaults->{tablet}) {
4574 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4575 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4576 if $arch eq 'aarch64
';
4578 vm_deviceunplug($vmid, $conf, 'tablet
');
4579 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4581 } elsif ($opt =~ m/^usb\d+/) {
4583 # since we cannot reliably hot unplug usb devices we are disabling it
4584 #die "skip\n" if !$hotplug_features->{usb} || $conf->{$opt} =~ m/spice/i;
4585 #vm_deviceunplug($vmid, $conf, $opt);
4586 } elsif ($opt eq 'vcpus
') {
4587 die "skip\n" if !$hotplug_features->{cpu};
4588 qemu_cpu_hotplug($vmid, $conf, undef);
4589 } elsif ($opt eq 'balloon
') {
4590 # enable balloon device is not hotpluggable
4591 die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
4592 # here we reset the ballooning value to memory
4593 my $balloon = $conf->{memory} || $defaults->{memory};
4594 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4595 } elsif ($fast_plug_option->{$opt}) {
4597 } elsif ($opt =~ m/^net(\d+)$/) {
4598 die "skip\n" if !$hotplug_features->{network};
4599 vm_deviceunplug($vmid, $conf, $opt);
4600 } elsif (is_valid_drivename($opt)) {
4601 die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
4602 vm_deviceunplug($vmid, $conf, $opt);
4603 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4604 } elsif ($opt =~ m/^memory$/) {
4605 die "skip\n" if !$hotplug_features->{memory};
4606 PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
4607 } elsif ($opt eq 'cpuunits
') {
4608 $cgroup->change_cpu_shares(undef, $defaults->{cpuunits});
4609 } elsif ($opt eq 'cpulimit
') {
4610 $cgroup->change_cpu_quota(-1, 100000);
4616 &$add_error($opt, $err) if $err ne "skip\n";
4618 delete $conf->{$opt};
4619 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4623 my ($apply_pending_cloudinit, $apply_pending_cloudinit_done);
4624 $apply_pending_cloudinit = sub {
4625 return if $apply_pending_cloudinit_done; # once is enough
4626 $apply_pending_cloudinit_done = 1; # once is enough
4628 my ($key, $value) = @_;
4630 my @cloudinit_opts = keys %$confdesc_cloudinit;
4631 foreach my $opt (keys %{$conf->{pending}}) {
4632 next if !grep { $_ eq $opt } @cloudinit_opts;
4633 $conf->{$opt} = delete $conf->{pending}->{$opt};
4636 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4637 foreach my $opt (sort keys %$pending_delete_hash) {
4638 next if !grep { $_ eq $opt } @cloudinit_opts;
4639 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4640 delete $conf->{$opt};
4643 my $new_conf = { %$conf };
4644 $new_conf->{$key} = $value;
4645 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($new_conf, $vmid);
4648 foreach my $opt (keys %{$conf->{pending}}) {
4649 next if $selection && !$selection->{$opt};
4650 my $value = $conf->{pending}->{$opt};
4652 if ($opt eq 'hotplug
') {
4653 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/);
4654 } elsif ($opt eq 'tablet
') {
4655 die "skip\n" if !$hotplug_features->{usb};
4657 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4658 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4659 if $arch eq 'aarch64
';
4660 } elsif ($value == 0) {
4661 vm_deviceunplug($vmid, $conf, 'tablet
');
4662 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4664 } elsif ($opt =~ m/^usb\d+$/) {
4666 # since we cannot reliably hot unplug usb devices we disable it for now
4667 #die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
4668 #my $d = eval { parse_property_string($usbdesc->{format}, $value) };
4669 #die "skip\n" if !$d;
4670 #qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d, $arch, $machine_type);
4671 } elsif ($opt eq 'vcpus
') {
4672 die "skip\n" if !$hotplug_features->{cpu};
4673 qemu_cpu_hotplug($vmid, $conf, $value);
4674 } elsif ($opt eq 'balloon
') {
4675 # enable/disable balloning device is not hotpluggable
4676 my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon});
4677 my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon});
4678 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
4680 # allow manual ballooning if shares is set to zero
4681 if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
4682 my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
4683 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4685 } elsif ($opt =~ m/^net(\d+)$/) {
4686 # some changes can be done without hotplug
4687 vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
4688 $vmid, $opt, $value, $arch, $machine_type);
4689 } elsif (is_valid_drivename($opt)) {
4690 die "skip\n" if $opt eq 'efidisk0
';
4691 # some changes can be done without hotplug
4692 my $drive = parse_drive($opt, $value);
4693 if (drive_is_cloudinit($drive)) {
4694 &$apply_pending_cloudinit($opt, $value);
4696 vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
4697 $vmid, $opt, $value, $arch, $machine_type);
4698 } elsif ($opt =~ m/^memory$/) { #dimms
4699 die "skip\n" if !$hotplug_features->{memory};
4700 $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
4701 } elsif ($opt eq 'cpuunits
') {
4702 $cgroup->change_cpu_shares($conf->{pending}->{$opt}, $defaults->{cpuunits});
4703 } elsif ($opt eq 'cpulimit
') {
4704 my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
4705 $cgroup->change_cpu_quota($cpulimit, 100000);
4707 die "skip\n"; # skip non-hot-pluggable options
4711 &$add_error($opt, $err) if $err ne "skip\n";
4713 $conf->{$opt} = $value;
4714 delete $conf->{pending}->{$opt};
4718 PVE::QemuConfig->write_config($vmid, $conf);
4721 sub try_deallocate_drive {
4722 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
4724 if (($force || $key =~ /^unused/) && !drive_is_cdrom($drive, 1)) {
4725 my $volid = $drive->{file};
4726 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
4727 my $sid = PVE::Storage::parse_volume_id($volid);
4728 $rpcenv->check($authuser, "/storage/$sid", ['Datastore
.AllocateSpace
']);
4730 # check if the disk is really unused
4731 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
4732 if PVE::QemuServer::Drive::is_volume_in_use($storecfg, $conf, $key, $volid);
4733 PVE::Storage::vdisk_free($storecfg, $volid);
4736 # If vm is not owner of this disk remove from config
4744 sub vmconfig_delete_or_detach_drive {
4745 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
4747 my $drive = parse_drive($opt, $conf->{$opt});
4749 my $rpcenv = PVE::RPCEnvironment::get();
4750 my $authuser = $rpcenv->get_user();
4753 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM
.Config
.Disk
']);
4754 try_deallocate_drive($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
4756 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $drive);
4762 sub vmconfig_apply_pending {
4763 my ($vmid, $conf, $storecfg, $errors) = @_;
4765 my $add_apply_error = sub {
4766 my ($opt, $msg) = @_;
4767 my $err_msg = "unable to apply pending change $opt : $msg";
4768 $errors->{$opt} = $err_msg;
4774 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4775 foreach my $opt (sort keys %$pending_delete_hash) {
4776 my $force = $pending_delete_hash->{$opt}->{force};
4778 if ($opt =~ m/^unused/) {
4779 die "internal error";
4780 } elsif (defined($conf->{$opt}) && is_valid_drivename($opt)) {
4781 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4785 $add_apply_error->($opt, $err);
4787 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4788 delete $conf->{$opt};
4792 PVE::QemuConfig->cleanup_pending($conf);
4794 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4795 next if $opt eq 'delete'; # just to be sure
4797 if (defined($conf->{$opt}) && is_valid_drivename($opt)) {
4798 vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
4802 $add_apply_error->($opt, $err);
4804 $conf->{$opt} = delete $conf->{pending}->{$opt};
4808 # write all changes at once to avoid unnecessary i/o
4809 PVE::QemuConfig->write_config($vmid, $conf);
4812 sub vmconfig_update_net {
4813 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
4815 my $newnet = parse_net($value);
4817 if ($conf->{$opt}) {
4818 my $oldnet = parse_net($conf->{$opt});
4820 if (safe_string_ne($oldnet->{model}, $newnet->{model}) ||
4821 safe_string_ne($oldnet->{macaddr}, $newnet->{macaddr}) ||
4822 safe_num_ne($oldnet->{queues}, $newnet->{queues}) ||
4823 !($newnet->{bridge} && $oldnet->{bridge})) { # bridge/nat mode change
4825 # for non online change, we try to hot-unplug
4826 die "skip\n" if !$hotplug;
4827 vm_deviceunplug($vmid, $conf, $opt);
4830 die "internal error" if $opt !~ m/net(\d+)/;
4831 my $iface = "tap${vmid}i$1";
4833 if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
4834 safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
4835 safe_string_ne($oldnet->{trunks}, $newnet->{trunks}) ||
4836 safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
4837 PVE::Network::tap_unplug($iface);
4840 PVE::Network::SDN::Zones::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
4842 PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
4844 } elsif (safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
4845 # Rate can be applied on its own but any change above needs to
4846 # include the rate in tap_plug since OVS resets everything.
4847 PVE::Network::tap_rate_limit($iface, $newnet->{rate});
4850 if (safe_string_ne($oldnet->{link_down}, $newnet->{link_down})) {
4851 qemu_set_link_status($vmid, $opt, !$newnet->{link_down});
4859 vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
4865 sub vmconfig_update_disk {
4866 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
4868 my $drive = parse_drive($opt, $value);
4870 if ($conf->{$opt} && (my $old_drive = parse_drive($opt, $conf->{$opt}))) {
4871 my $media = $drive->{media} || 'disk
';
4872 my $oldmedia = $old_drive->{media} || 'disk
';
4873 die "unable to change media type\n" if $media ne $oldmedia;
4875 if (!drive_is_cdrom($old_drive)) {
4877 if ($drive->{file} ne $old_drive->{file}) {
4879 die "skip\n" if !$hotplug;
4881 # unplug and register as unused
4882 vm_deviceunplug($vmid, $conf, $opt);
4883 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive)
4886 # update existing disk
4888 # skip non hotpluggable value
4889 if (safe_string_ne($drive->{discard}, $old_drive->{discard}) ||
4890 safe_string_ne($drive->{iothread}, $old_drive->{iothread}) ||
4891 safe_string_ne($drive->{queues}, $old_drive->{queues}) ||
4892 safe_string_ne($drive->{cache}, $old_drive->{cache}) ||
4893 safe_string_ne($drive->{ssd}, $old_drive->{ssd})) {
4898 if (safe_num_ne($drive->{mbps}, $old_drive->{mbps}) ||
4899 safe_num_ne($drive->{mbps_rd}, $old_drive->{mbps_rd}) ||
4900 safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) ||
4901 safe_num_ne($drive->{iops}, $old_drive->{iops}) ||
4902 safe_num_ne($drive->{iops_rd}, $old_drive->{iops_rd}) ||
4903 safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr}) ||
4904 safe_num_ne($drive->{mbps_max}, $old_drive->{mbps_max}) ||
4905 safe_num_ne($drive->{mbps_rd_max}, $old_drive->{mbps_rd_max}) ||
4906 safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) ||
4907 safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) ||
4908 safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) ||
4909 safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max}) ||
4910 safe_num_ne($drive->{bps_max_length}, $old_drive->{bps_max_length}) ||
4911 safe_num_ne($drive->{bps_rd_max_length}, $old_drive->{bps_rd_max_length}) ||
4912 safe_num_ne($drive->{bps_wr_max_length}, $old_drive->{bps_wr_max_length}) ||
4913 safe_num_ne($drive->{iops_max_length}, $old_drive->{iops_max_length}) ||
4914 safe_num_ne($drive->{iops_rd_max_length}, $old_drive->{iops_rd_max_length}) ||
4915 safe_num_ne($drive->{iops_wr_max_length}, $old_drive->{iops_wr_max_length})) {
4917 qemu_block_set_io_throttle(
4919 ($drive->{mbps} || 0)*1024*1024,
4920 ($drive->{mbps_rd} || 0)*1024*1024,
4921 ($drive->{mbps_wr} || 0)*1024*1024,
4922 $drive->{iops} || 0,
4923 $drive->{iops_rd} || 0,
4924 $drive->{iops_wr} || 0,
4925 ($drive->{mbps_max} || 0)*1024*1024,
4926 ($drive->{mbps_rd_max} || 0)*1024*1024,
4927 ($drive->{mbps_wr_max} || 0)*1024*1024,
4928 $drive->{iops_max} || 0,
4929 $drive->{iops_rd_max} || 0,
4930 $drive->{iops_wr_max} || 0,
4931 $drive->{bps_max_length} || 1,
4932 $drive->{bps_rd_max_length} || 1,
4933 $drive->{bps_wr_max_length} || 1,
4934 $drive->{iops_max_length} || 1,
4935 $drive->{iops_rd_max_length} || 1,
4936 $drive->{iops_wr_max_length} || 1,
4946 if ($drive->{file} eq 'none
') {
4947 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
4948 if (drive_is_cloudinit($old_drive)) {
4949 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive);
4952 my $path = get_iso_path($storecfg, $vmid, $drive->{file});
4954 # force eject if locked
4955 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
4958 mon_cmd($vmid, "blockdev-change-medium",
4959 id => "$opt", filename => "$path");
4967 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
4969 PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
4970 vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
4973 # called in locked context by incoming migration
4974 sub vm_migrate_get_nbd_disks {
4975 my ($storecfg, $conf, $replicated_volumes) = @_;
4977 my $local_volumes = {};
4978 PVE::QemuConfig->foreach_volume($conf, sub {
4979 my ($ds, $drive) = @_;
4981 return if drive_is_cdrom($drive);
4983 my $volid = $drive->{file};
4987 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
4989 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
4990 return if $scfg->{shared};
4992 # replicated disks re-use existing state via bitmap
4993 my $use_existing = $replicated_volumes->{$volid} ? 1 : 0;
4994 $local_volumes->{$ds} = [$volid, $storeid, $volname, $drive, $use_existing];
4996 return $local_volumes;
4999 # called in locked context by incoming migration
5000 sub vm_migrate_alloc_nbd_disks {
5001 my ($storecfg, $vmid, $source_volumes, $storagemap) = @_;
5006 foreach my $opt (sort keys %$source_volumes) {
5007 my ($volid, $storeid, $volname, $drive, $use_existing) = @{$source_volumes->{$opt}};
5009 if ($use_existing) {
5010 $nbd->{$opt}->{drivestr} = print_drive($drive);
5011 $nbd->{$opt}->{volid} = $volid;
5012 $nbd->{$opt}->{replicated} = 1;
5016 # If a remote storage is specified and the format of the original
5017 # volume is not available there, fall back to the default format.
5018 # Otherwise use the same format as the original.
5019 if (!$storagemap->{identity}) {
5020 $storeid = map_storage($storagemap, $storeid);
5021 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
5022 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5023 my $fileFormat = qemu_img_format($scfg, $volname);
5024 $format = (grep {$fileFormat eq $_} @{$validFormats}) ? $fileFormat : $defFormat;
5026 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5027 $format = qemu_img_format($scfg, $volname);
5030 my $size = $drive->{size} / 1024;
5031 my $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $format, undef, $size);
5032 my $newdrive = $drive;
5033 $newdrive->{format} = $format;
5034 $newdrive->{file} = $newvolid;
5035 my $drivestr = print_drive($newdrive);
5036 $nbd->{$opt}->{drivestr} = $drivestr;
5037 $nbd->{$opt}->{volid} = $newvolid;
5043 # see vm_start_nolock for parameters, additionally:
5045 # storagemap = parsed storage map for allocating NBD disks
5047 my ($storecfg, $vmid, $params, $migrate_opts) = @_;
5049 return PVE::QemuConfig->lock_config($vmid, sub {
5050 my $conf = PVE::QemuConfig->load_config($vmid, $migrate_opts->{migratedfrom});
5052 die "you can't start a vm
if it
's a template\n"
5053 if !$params->{skiptemplate} && PVE::QemuConfig->is_template($conf);
5055 my $has_suspended_lock = PVE::QemuConfig->has_lock($conf, 'suspended
');
5056 my $has_backup_lock = PVE::QemuConfig->has_lock($conf, 'backup
');
5058 my $running = check_running($vmid, undef, $migrate_opts->{migratedfrom});
5060 if ($has_backup_lock && $running) {
5061 # a backup is currently running, attempt to start the guest in the
5062 # existing QEMU instance
5063 return vm_resume($vmid);
5066 PVE::QemuConfig->check_lock($conf)
5067 if !($params->{skiplock} || $has_suspended_lock);
5069 $params->{resume} = $has_suspended_lock || defined($conf->{vmstate});
5071 die "VM $vmid already running\n" if $running;
5073 if (my $storagemap = $migrate_opts->{storagemap}) {
5074 my $replicated = $migrate_opts->{replicated_volumes};
5075 my $disks = vm_migrate_get_nbd_disks($storecfg, $conf, $replicated);
5076 $migrate_opts->{nbd} = vm_migrate_alloc_nbd_disks($storecfg, $vmid, $disks, $storagemap);
5078 foreach my $opt (keys %{$migrate_opts->{nbd}}) {
5079 $conf->{$opt} = $migrate_opts->{nbd}->{$opt}->{drivestr};
5083 return vm_start_nolock($storecfg, $vmid, $conf, $params, $migrate_opts);
5089 # statefile => 'tcp
', 'unix
' for migration or path/volid for RAM state
5090 # skiplock => 0/1, skip checking for config lock
5091 # skiptemplate => 0/1, skip checking whether VM is template
5092 # forcemachine => to force Qemu machine (rollback/migration)
5093 # forcecpu => a QEMU '-cpu
' argument string to override get_cpu_options
5094 # timeout => in seconds
5095 # paused => start VM in paused state (backup)
5096 # resume => resume from hibernation
5107 # nbd => volumes for NBD exports (vm_migrate_alloc_nbd_disks)
5108 # migratedfrom => source node
5109 # spice_ticket => used for spice migration, passed via tunnel/stdin
5110 # network => CIDR of migration network
5111 # type => secure/insecure - tunnel over encrypted connection or plain-text
5112 # nbd_proto_version => int, 0 for TCP, 1 for UNIX
5113 # replicated_volumes = which volids should be re-used with bitmaps for nbd migration
5114 sub vm_start_nolock {
5115 my ($storecfg, $vmid, $conf, $params, $migrate_opts) = @_;
5117 my $statefile = $params->{statefile};
5118 my $resume = $params->{resume};
5120 my $migratedfrom = $migrate_opts->{migratedfrom};
5121 my $migration_type = $migrate_opts->{type};
5125 # clean up leftover reboot request files
5126 eval { clear_reboot_request($vmid); };
5129 if (!$statefile && scalar(keys %{$conf->{pending}})) {
5130 vmconfig_apply_pending($vmid, $conf, $storecfg);
5131 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
5134 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
5136 my $defaults = load_defaults();
5138 # set environment variable useful inside network script
5139 $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
5141 PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-start
', 1);
5143 my $forcemachine = $params->{forcemachine};
5144 my $forcecpu = $params->{forcecpu};
5146 # enforce machine and CPU type on suspended vm to ensure HW compatibility
5147 $forcemachine = $conf->{runningmachine};
5148 $forcecpu = $conf->{runningcpu};
5149 print "Resuming suspended VM\n";
5152 my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid,
5153 $conf, $defaults, $forcemachine, $forcecpu, $params->{'pbs-backing
'});
5156 my $get_migration_ip = sub {
5157 my ($nodename) = @_;
5159 return $migration_ip if defined($migration_ip);
5161 my $cidr = $migrate_opts->{network};
5163 if (!defined($cidr)) {
5164 my $dc_conf = PVE::Cluster::cfs_read_file('datacenter
.cfg
');
5165 $cidr = $dc_conf->{migration}->{network};
5168 if (defined($cidr)) {
5169 my $ips = PVE::Network::get_local_ip_from_cidr($cidr);
5171 die "could not get IP: no address configured on local " .
5172 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5174 die "could not get IP: multiple addresses configured on local " .
5175 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5177 $migration_ip = @$ips[0];
5180 $migration_ip = PVE::Cluster::remote_node_ip($nodename, 1)
5181 if !defined($migration_ip);
5183 return $migration_ip;
5188 if ($statefile eq 'tcp
') {
5189 my $localip = "localhost";
5190 my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter
.cfg
');
5191 my $nodename = nodename();
5193 if (!defined($migration_type)) {
5194 if (defined($datacenterconf->{migration}->{type})) {
5195 $migration_type = $datacenterconf->{migration}->{type};
5197 $migration_type = 'secure
';
5201 if ($migration_type eq 'insecure
') {
5202 $localip = $get_migration_ip->($nodename);
5203 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5206 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5207 my $migrate_port = PVE::Tools::next_migrate_port($pfamily);
5208 $migrate_uri = "tcp:${localip}:${migrate_port}";
5209 push @$cmd, '-incoming
', $migrate_uri;
5212 } elsif ($statefile eq 'unix
') {
5213 # should be default for secure migrations as a ssh TCP forward
5214 # tunnel is not deterministic reliable ready and fails regurarly
5215 # to set up in time, so use UNIX socket forwards
5216 my $socket_addr = "/run/qemu-server/$vmid.migrate";
5217 unlink $socket_addr;
5219 $migrate_uri = "unix:$socket_addr";
5221 push @$cmd, '-incoming
', $migrate_uri;
5224 } elsif (-e $statefile) {
5225 push @$cmd, '-loadstate
', $statefile;
5227 my $statepath = PVE::Storage::path($storecfg, $statefile);
5228 push @$vollist, $statefile;
5229 push @$cmd, '-loadstate
', $statepath;
5231 } elsif ($params->{paused}) {
5236 for (my $i = 0; $i < $PVE::QemuServer::PCI::MAX_HOSTPCI_DEVICES; $i++) {
5237 my $d = parse_hostpci($conf->{"hostpci$i"});
5239 my $pcidevices = $d->{pciid};
5240 foreach my $pcidevice (@$pcidevices) {
5241 my $pciid = $pcidevice->{id};
5243 my $info = PVE::SysFSTools::pci_device_info("$pciid");
5244 die "IOMMU not present\n" if !PVE::SysFSTools::check_iommu_support();
5245 die "no pci device info for device '$pciid'\n" if !$info;
5248 my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
5249 PVE::SysFSTools::pci_create_mdev_device($pciid, $uuid, $d->{mdev});
5251 die "can't unbind
/bind PCI group to VFIO
'$pciid'\n"
5252 if !PVE::SysFSTools::pci_dev_group_bind_to_vfio($pciid);
5253 die "can
't reset PCI device '$pciid'\n"
5254 if $info->{has_fl_reset} && !PVE::SysFSTools::pci_dev_reset($info);
5259 PVE::Storage::activate_volumes($storecfg, $vollist);
5262 run_command(['/bin/systemctl
', 'stop
', "$vmid.scope"],
5263 outfunc => sub {}, errfunc => sub {});
5265 # Issues with the above 'stop
' not being fully completed are extremely rare, a very low
5266 # timeout should be more than enough here...
5267 PVE::Systemd::wait_for_unit_removed("$vmid.scope", 5);
5269 my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
5270 : $defaults->{cpuunits};
5272 my $start_timeout = $params->{timeout} // config_aware_timeout($conf, $resume);
5274 timeout => $statefile ? undef : $start_timeout,
5279 # when migrating, prefix QEMU output so other side can pick up any
5280 # errors that might occur and show the user
5281 if ($migratedfrom) {
5282 $run_params{quiet} = 1;
5283 $run_params{logfunc} = sub { print "QEMU: $_[0]\n" };
5287 Slice => 'qemu
.slice
',
5288 KillMode => 'process
',
5290 TimeoutStopUSec => ULONG_MAX, # infinity
5293 if (PVE::CGroup::cgroup_mode() == 2) {
5294 $properties{CPUWeight} = $cpuunits;
5296 $properties{CPUShares} = $cpuunits;
5299 if (my $cpulimit = $conf->{cpulimit}) {
5300 $properties{CPUQuota} = int($cpulimit * 100);
5302 $properties{timeout} = 10 if $statefile; # setting up the scope shoul be quick
5304 my $run_qemu = sub {
5305 PVE::Tools::run_fork sub {
5306 PVE::Systemd::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %properties);
5308 my $exitcode = run_command($cmd, %run_params);
5309 die "QEMU exited with code $exitcode\n" if $exitcode;
5313 if ($conf->{hugepages}) {
5316 my $hugepages_topology = PVE::QemuServer::Memory::hugepages_topology($conf);
5317 my $hugepages_host_topology = PVE::QemuServer::Memory::hugepages_host_topology();
5319 PVE::QemuServer::Memory::hugepages_mount();
5320 PVE::QemuServer::Memory::hugepages_allocate($hugepages_topology, $hugepages_host_topology);
5322 eval { $run_qemu->() };
5324 PVE::QemuServer::Memory::hugepages_reset($hugepages_host_topology)
5325 if !$conf->{keephugepages};
5329 PVE::QemuServer::Memory::hugepages_pre_deallocate($hugepages_topology)
5330 if !$conf->{keephugepages};
5332 eval { PVE::QemuServer::Memory::hugepages_update_locked($code); };
5335 eval { $run_qemu->() };
5339 # deactivate volumes if start fails
5340 eval { PVE::Storage::deactivate_volumes($storecfg, $vollist); };
5341 die "start failed: $err";
5344 print "migration listens on $migrate_uri\n" if $migrate_uri;
5345 $res->{migrate_uri} = $migrate_uri;
5347 if ($statefile && $statefile ne 'tcp
' && $statefile ne 'unix
') {
5348 eval { mon_cmd($vmid, "cont"); };
5352 #start nbd server for storage migration
5353 if (my $nbd = $migrate_opts->{nbd}) {
5354 my $nbd_protocol_version = $migrate_opts->{nbd_proto_version} // 0;
5356 my $migrate_storage_uri;
5357 # nbd_protocol_version > 0 for unix socket support
5358 if ($nbd_protocol_version > 0 && $migration_type eq 'secure
') {
5359 my $socket_path = "/run/qemu-server/$vmid\_nbd.migrate";
5360 mon_cmd($vmid, "nbd-server-start", addr => { type => 'unix
', data => { path => $socket_path } } );
5361 $migrate_storage_uri = "nbd:unix:$socket_path";
5363 my $nodename = nodename();
5364 my $localip = $get_migration_ip->($nodename);
5365 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5366 my $storage_migrate_port = PVE::Tools::next_migrate_port($pfamily);
5368 mon_cmd($vmid, "nbd-server-start", addr => {
5371 host => "${localip}",
5372 port => "${storage_migrate_port}",
5375 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5376 $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}";
5379 $res->{migrate_storage_uri} = $migrate_storage_uri;
5381 foreach my $opt (sort keys %$nbd) {
5382 my $drivestr = $nbd->{$opt}->{drivestr};
5383 my $volid = $nbd->{$opt}->{volid};
5384 mon_cmd($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
5385 my $nbd_uri = "$migrate_storage_uri:exportname=drive-$opt";
5386 print "storage migration listens on $nbd_uri volume:$drivestr\n";
5387 print "re-using replicated volume: $opt - $volid\n"
5388 if $nbd->{$opt}->{replicated};
5390 $res->{drives}->{$opt} = $nbd->{$opt};
5391 $res->{drives}->{$opt}->{nbd_uri} = $nbd_uri;
5395 if ($migratedfrom) {
5397 set_migration_caps($vmid);
5402 print "spice listens on port $spice_port\n";
5403 $res->{spice_port} = $spice_port;
5404 if ($migrate_opts->{spice_ticket}) {
5405 mon_cmd($vmid, "set_password", protocol => 'spice
', password =>
5406 $migrate_opts->{spice_ticket});
5407 mon_cmd($vmid, "expire_password", protocol => 'spice
', time => "+30");
5412 mon_cmd($vmid, "balloon", value => $conf->{balloon}*1024*1024)
5413 if !$statefile && $conf->{balloon};
5415 foreach my $opt (keys %$conf) {
5416 next if $opt !~ m/^net\d+$/;
5417 my $nicconf = parse_net($conf->{$opt});
5418 qemu_set_link_status($vmid, $opt, 0) if $nicconf->{link_down};
5422 mon_cmd($vmid, 'qom-set
',
5423 path => "machine/peripheral/balloon0",
5424 property => "guest-stats-polling-interval",
5425 value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
5428 print "Resumed VM, removing state\n";
5429 if (my $vmstate = $conf->{vmstate}) {
5430 PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
5431 PVE::Storage::vdisk_free($storecfg, $vmstate);
5433 delete $conf->@{qw(lock vmstate runningmachine runningcpu)};
5434 PVE
::QemuConfig-
>write_config($vmid, $conf);
5437 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'post-start');
5442 sub vm_commandline
{
5443 my ($storecfg, $vmid, $snapname) = @_;
5445 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5450 my $snapshot = $conf->{snapshots
}->{$snapname};
5451 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
5453 # check for machine or CPU overrides in snapshot
5454 $forcemachine = $snapshot->{runningmachine
};
5455 $forcecpu = $snapshot->{runningcpu
};
5457 $snapshot->{digest
} = $conf->{digest
}; # keep file digest for API
5462 my $defaults = load_defaults
();
5464 my $cmd = config_to_command
($storecfg, $vmid, $conf, $defaults,
5465 $forcemachine, $forcecpu);
5467 return PVE
::Tools
::cmd2string
($cmd);
5471 my ($vmid, $skiplock) = @_;
5473 PVE
::QemuConfig-
>lock_config($vmid, sub {
5475 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5477 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5479 mon_cmd
($vmid, "system_reset");
5483 sub get_vm_volumes
{
5487 foreach_volid
($conf, sub {
5488 my ($volid, $attr) = @_;
5490 return if $volid =~ m
|^/|;
5492 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
5495 push @$vollist, $volid;
5501 sub vm_stop_cleanup
{
5502 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
5507 my $vollist = get_vm_volumes
($conf);
5508 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
5511 foreach my $ext (qw(mon qmp pid vnc qga)) {
5512 unlink "/var/run/qemu-server/${vmid}.$ext";
5515 if ($conf->{ivshmem
}) {
5516 my $ivshmem = parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
5517 # just delete it for now, VMs which have this already open do not
5518 # are affected, but new VMs will get a separated one. If this
5519 # becomes an issue we either add some sort of ref-counting or just
5520 # add a "don't delete on stop" flag to the ivshmem format.
5521 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name
} // $vmid);
5524 foreach my $key (keys %$conf) {
5525 next if $key !~ m/^hostpci(\d+)$/;
5526 my $hostpciindex = $1;
5527 my $d = parse_hostpci
($conf->{$key});
5528 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $hostpciindex);
5530 foreach my $pci (@{$d->{pciid
}}) {
5531 my $pciid = $pci->{id
};
5532 PVE
::SysFSTools
::pci_cleanup_mdev_device
($pciid, $uuid);
5536 vmconfig_apply_pending
($vmid, $conf, $storecfg) if $apply_pending_changes;
5538 warn $@ if $@; # avoid errors - just warn
5541 # call only in locked context
5543 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
5545 my $pid = check_running
($vmid, $nocheck);
5550 $conf = PVE
::QemuConfig-
>load_config($vmid);
5551 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5552 if (!defined($timeout) && $shutdown && $conf->{startup
}) {
5553 my $opts = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
5554 $timeout = $opts->{down
} if $opts->{down
};
5556 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-stop');
5561 if (defined($conf) && get_qga_key
($conf, 'enabled')) {
5562 mon_cmd
($vmid, "guest-shutdown", timeout
=> $timeout);
5564 mon_cmd
($vmid, "system_powerdown");
5567 mon_cmd
($vmid, "quit");
5573 $timeout = 60 if !defined($timeout);
5576 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
5581 if ($count >= $timeout) {
5583 warn "VM still running - terminating now with SIGTERM\n";
5586 die "VM quit/powerdown failed - got timeout\n";
5589 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5593 if (!check_running
($vmid, $nocheck)) {
5594 warn "Unexpected: VM shutdown command failed, but VM not running anymore..\n";
5598 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
5601 die "VM quit/powerdown failed\n";
5609 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
5614 if ($count >= $timeout) {
5615 warn "VM still running - terminating now with SIGKILL\n";
5620 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5623 # Note: use $nocheck to skip tests if VM configuration file exists.
5624 # We need that when migration VMs to other nodes (files already moved)
5625 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
5627 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
5629 $force = 1 if !defined($force) && !$shutdown;
5632 my $pid = check_running
($vmid, $nocheck, $migratedfrom);
5633 kill 15, $pid if $pid;
5634 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migratedfrom);
5635 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 0);
5639 PVE
::QemuConfig-
>lock_config($vmid, sub {
5640 _do_vm_stop
($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
5645 my ($vmid, $timeout) = @_;
5647 PVE
::QemuConfig-
>lock_config($vmid, sub {
5650 # only reboot if running, as qmeventd starts it again on a stop event
5651 return if !check_running
($vmid);
5653 create_reboot_request
($vmid);
5655 my $storecfg = PVE
::Storage
::config
();
5656 _do_vm_stop
($storecfg, $vmid, undef, undef, $timeout, 1);
5660 # avoid that the next normal shutdown will be confused for a reboot
5661 clear_reboot_request
($vmid);
5667 # note: if using the statestorage parameter, the caller has to check privileges
5669 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
5676 PVE
::QemuConfig-
>lock_config($vmid, sub {
5678 $conf = PVE
::QemuConfig-
>load_config($vmid);
5680 my $is_backing_up = PVE
::QemuConfig-
>has_lock($conf, 'backup');
5681 PVE
::QemuConfig-
>check_lock($conf)
5682 if !($skiplock || $is_backing_up);
5684 die "cannot suspend to disk during backup\n"
5685 if $is_backing_up && $includestate;
5687 if ($includestate) {
5688 $conf->{lock} = 'suspending';
5689 my $date = strftime
("%Y-%m-%d", localtime(time()));
5690 $storecfg = PVE
::Storage
::config
();
5691 if (!$statestorage) {
5692 $statestorage = find_vmstate_storage
($conf, $storecfg);
5693 # check permissions for the storage
5694 my $rpcenv = PVE
::RPCEnvironment
::get
();
5695 if ($rpcenv->{type
} ne 'cli') {
5696 my $authuser = $rpcenv->get_user();
5697 $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
5702 $vmstate = PVE
::QemuConfig-
>__snapshot_save_vmstate(
5703 $vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
5704 $path = PVE
::Storage
::path
($storecfg, $vmstate);
5705 PVE
::QemuConfig-
>write_config($vmid, $conf);
5707 mon_cmd
($vmid, "stop");
5711 if ($includestate) {
5713 PVE
::Storage
::activate_volumes
($storecfg, [$vmstate]);
5716 set_migration_caps
($vmid, 1);
5717 mon_cmd
($vmid, "savevm-start", statefile
=> $path);
5719 my $state = mon_cmd
($vmid, "query-savevm");
5720 if (!$state->{status
}) {
5721 die "savevm not active\n";
5722 } elsif ($state->{status
} eq 'active') {
5725 } elsif ($state->{status
} eq 'completed') {
5726 print "State saved, quitting\n";
5728 } elsif ($state->{status
} eq 'failed' && $state->{error
}) {
5729 die "query-savevm failed with error '$state->{error}'\n"
5731 die "query-savevm returned status '$state->{status}'\n";
5737 PVE
::QemuConfig-
>lock_config($vmid, sub {
5738 $conf = PVE
::QemuConfig-
>load_config($vmid);
5740 # cleanup, but leave suspending lock, to indicate something went wrong
5742 mon_cmd
($vmid, "savevm-end");
5743 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
5744 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
5745 delete $conf->@{qw(vmstate runningmachine runningcpu)};
5746 PVE
::QemuConfig-
>write_config($vmid, $conf);
5752 die "lock changed unexpectedly\n"
5753 if !PVE
::QemuConfig-
>has_lock($conf, 'suspending');
5755 mon_cmd
($vmid, "quit");
5756 $conf->{lock} = 'suspended';
5757 PVE
::QemuConfig-
>write_config($vmid, $conf);
5763 my ($vmid, $skiplock, $nocheck) = @_;
5765 PVE
::QemuConfig-
>lock_config($vmid, sub {
5766 my $res = mon_cmd
($vmid, 'query-status');
5767 my $resume_cmd = 'cont';
5770 if ($res->{status
}) {
5771 return if $res->{status
} eq 'running'; # job done, go home
5772 $resume_cmd = 'system_wakeup' if $res->{status
} eq 'suspended';
5773 $reset = 1 if $res->{status
} eq 'shutdown';
5778 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5780 PVE
::QemuConfig-
>check_lock($conf)
5781 if !($skiplock || PVE
::QemuConfig-
>has_lock($conf, 'backup'));
5785 # required if a VM shuts down during a backup and we get a resume
5786 # request before the backup finishes for example
5787 mon_cmd
($vmid, "system_reset");
5789 mon_cmd
($vmid, $resume_cmd);
5794 my ($vmid, $skiplock, $key) = @_;
5796 PVE
::QemuConfig-
>lock_config($vmid, sub {
5798 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5800 # there is no qmp command, so we use the human monitor command
5801 my $res = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "sendkey $key");
5802 die $res if $res ne '';
5806 # vzdump restore implementaion
5808 sub tar_archive_read_firstfile
{
5809 my $archive = shift;
5811 die "ERROR: file '$archive' does not exist\n" if ! -f
$archive;
5813 # try to detect archive type first
5814 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
5815 die "unable to open file '$archive'\n";
5816 my $firstfile = <$fh>;
5820 die "ERROR: archive contaions no data\n" if !$firstfile;
5826 sub tar_restore_cleanup
{
5827 my ($storecfg, $statfile) = @_;
5829 print STDERR
"starting cleanup\n";
5831 if (my $fd = IO
::File-
>new($statfile, "r")) {
5832 while (defined(my $line = <$fd>)) {
5833 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
5836 if ($volid =~ m
|^/|) {
5837 unlink $volid || die 'unlink failed\n';
5839 PVE
::Storage
::vdisk_free
($storecfg, $volid);
5841 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
5843 print STDERR
"unable to cleanup '$volid' - $@" if $@;
5845 print STDERR
"unable to parse line in statfile - $line";
5852 sub restore_file_archive
{
5853 my ($archive, $vmid, $user, $opts) = @_;
5855 return restore_vma_archive
($archive, $vmid, $user, $opts)
5858 my $info = PVE
::Storage
::archive_info
($archive);
5859 my $format = $opts->{format
} // $info->{format
};
5860 my $comp = $info->{compression
};
5862 # try to detect archive format
5863 if ($format eq 'tar') {
5864 return restore_tar_archive
($archive, $vmid, $user, $opts);
5866 return restore_vma_archive
($archive, $vmid, $user, $opts, $comp);
5870 # hepler to remove disks that will not be used after restore
5871 my $restore_cleanup_oldconf = sub {
5872 my ($storecfg, $vmid, $oldconf, $virtdev_hash) = @_;
5874 PVE
::QemuConfig-
>foreach_volume($oldconf, sub {
5875 my ($ds, $drive) = @_;
5877 return if drive_is_cdrom
($drive, 1);
5879 my $volid = $drive->{file
};
5880 return if !$volid || $volid =~ m
|^/|;
5882 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
5883 return if !$path || !$owner || ($owner != $vmid);
5885 # Note: only delete disk we want to restore
5886 # other volumes will become unused
5887 if ($virtdev_hash->{$ds}) {
5888 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid); };
5895 # delete vmstate files, after the restore we have no snapshots anymore
5896 foreach my $snapname (keys %{$oldconf->{snapshots
}}) {
5897 my $snap = $oldconf->{snapshots
}->{$snapname};
5898 if ($snap->{vmstate
}) {
5899 eval { PVE
::Storage
::vdisk_free
($storecfg, $snap->{vmstate
}); };
5907 # Helper to parse vzdump backup device hints
5909 # $rpcenv: Environment, used to ckeck storage permissions
5910 # $user: User ID, to check storage permissions
5911 # $storecfg: Storage configuration
5912 # $fh: the file handle for reading the configuration
5913 # $devinfo: should contain device sizes for all backu-up'ed devices
5914 # $options: backup options (pool, default storage)
5916 # Return: $virtdev_hash, updates $devinfo (add devname, virtdev, format, storeid)
5917 my $parse_backup_hints = sub {
5918 my ($rpcenv, $user, $storecfg, $fh, $devinfo, $options) = @_;
5920 my $virtdev_hash = {};
5922 while (defined(my $line = <$fh>)) {
5923 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
5924 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
5925 die "archive does not contain data for drive '$virtdev'\n"
5926 if !$devinfo->{$devname};
5928 if (defined($options->{storage
})) {
5929 $storeid = $options->{storage
} || 'local';
5930 } elsif (!$storeid) {
5933 $format = 'raw' if !$format;
5934 $devinfo->{$devname}->{devname
} = $devname;
5935 $devinfo->{$devname}->{virtdev
} = $virtdev;
5936 $devinfo->{$devname}->{format
} = $format;
5937 $devinfo->{$devname}->{storeid
} = $storeid;
5939 # check permission on storage
5940 my $pool = $options->{pool
}; # todo: do we need that?
5941 if ($user ne 'root@pam') {
5942 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
5945 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
5946 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
5948 my $drive = parse_drive
($virtdev, $2);
5949 if (drive_is_cloudinit
($drive)) {
5950 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
5951 $storeid = $options->{storage
} if defined ($options->{storage
});
5952 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
5953 my $format = qemu_img_format
($scfg, $volname); # has 'raw' fallback
5955 $virtdev_hash->{$virtdev} = {
5957 storeid
=> $storeid,
5958 size
=> PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
,
5965 return $virtdev_hash;
5968 # Helper to allocate and activate all volumes required for a restore
5970 # $storecfg: Storage configuration
5971 # $virtdev_hash: as returned by parse_backup_hints()
5973 # Returns: { $virtdev => $volid }
5974 my $restore_allocate_devices = sub {
5975 my ($storecfg, $virtdev_hash, $vmid) = @_;
5978 foreach my $virtdev (sort keys %$virtdev_hash) {
5979 my $d = $virtdev_hash->{$virtdev};
5980 my $alloc_size = int(($d->{size
} + 1024 - 1)/1024);
5981 my $storeid = $d->{storeid
};
5982 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
5984 # test if requested format is supported
5985 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
5986 my $supported = grep { $_ eq $d->{format
} } @$validFormats;
5987 $d->{format
} = $defFormat if !$supported;
5990 if ($d->{is_cloudinit
}) {
5991 $name = "vm-$vmid-cloudinit";
5992 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
5993 if ($scfg->{path
}) {
5994 $name .= ".$d->{format}";
5998 my $volid = PVE
::Storage
::vdisk_alloc
(
5999 $storecfg, $storeid, $vmid, $d->{format
}, $name, $alloc_size);
6001 print STDERR
"new volume ID is '$volid'\n";
6002 $d->{volid
} = $volid;
6004 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
6006 $map->{$virtdev} = $volid;
6012 sub restore_update_config_line
{
6013 my ($cookie, $map, $line, $unique) = @_;
6015 return '' if $line =~ m/^\#qmdump\#/;
6016 return '' if $line =~ m/^\#vzdump\#/;
6017 return '' if $line =~ m/^lock:/;
6018 return '' if $line =~ m/^unused\d+:/;
6019 return '' if $line =~ m/^parent:/;
6023 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
6024 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
6025 # try to convert old 1.X settings
6026 my ($id, $ind, $ethcfg) = ($1, $2, $3);
6027 foreach my $devconfig (PVE
::Tools
::split_list
($ethcfg)) {
6028 my ($model, $macaddr) = split(/\=/, $devconfig);
6029 $macaddr = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if !$macaddr || $unique;
6032 bridge
=> "vmbr$ind",
6033 macaddr
=> $macaddr,
6035 my $netstr = print_net
($net);
6037 $res .= "net$cookie->{netcount}: $netstr\n";
6038 $cookie->{netcount
}++;
6040 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
6041 my ($id, $netstr) = ($1, $2);
6042 my $net = parse_net
($netstr);
6043 $net->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if $net->{macaddr
};
6044 $netstr = print_net
($net);
6045 $res .= "$id: $netstr\n";
6046 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk)\d+):\s*(\S+)\s*$/) {
6049 my $di = parse_drive
($virtdev, $value);
6050 if (defined($di->{backup
}) && !$di->{backup
}) {
6052 } elsif ($map->{$virtdev}) {
6053 delete $di->{format
}; # format can change on restore
6054 $di->{file
} = $map->{$virtdev};
6055 $value = print_drive
($di);
6056 $res .= "$virtdev: $value\n";
6060 } elsif (($line =~ m/^vmgenid: (.*)/)) {
6062 if ($vmgenid ne '0') {
6063 # always generate a new vmgenid if there was a valid one setup
6064 $vmgenid = generate_uuid
();
6066 $res .= "vmgenid: $vmgenid\n";
6067 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
6068 my ($uuid, $uuid_str);
6069 UUID
::generate
($uuid);
6070 UUID
::unparse
($uuid, $uuid_str);
6071 my $smbios1 = parse_smbios1
($2);
6072 $smbios1->{uuid
} = $uuid_str;
6073 $res .= $1.print_smbios1
($smbios1)."\n";
6081 my $restore_deactivate_volumes = sub {
6082 my ($storecfg, $devinfo) = @_;
6085 foreach my $devname (keys %$devinfo) {
6086 my $volid = $devinfo->{$devname}->{volid
};
6087 push @$vollist, $volid if $volid;
6090 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
6093 my $restore_destroy_volumes = sub {
6094 my ($storecfg, $devinfo) = @_;
6096 foreach my $devname (keys %$devinfo) {
6097 my $volid = $devinfo->{$devname}->{volid
};
6100 if ($volid =~ m
|^/|) {
6101 unlink $volid || die 'unlink failed\n';
6103 PVE
::Storage
::vdisk_free
($storecfg, $volid);
6105 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6107 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6112 my ($cfg, $vmid) = @_;
6114 my $info = PVE
::Storage
::vdisk_list
($cfg, undef, $vmid, undef, 'images');
6116 my $volid_hash = {};
6117 foreach my $storeid (keys %$info) {
6118 foreach my $item (@{$info->{$storeid}}) {
6119 next if !($item->{volid
} && $item->{size
});
6120 $item->{path
} = PVE
::Storage
::path
($cfg, $item->{volid
});
6121 $volid_hash->{$item->{volid
}} = $item;
6128 sub update_disk_config
{
6129 my ($vmid, $conf, $volid_hash) = @_;
6132 my $prefix = "VM $vmid";
6134 # used and unused disks
6135 my $referenced = {};
6137 # Note: it is allowed to define multiple storages with same path (alias), so
6138 # we need to check both 'volid' and real 'path' (two different volid can point
6139 # to the same path).
6141 my $referencedpath = {};
6144 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6145 my ($opt, $drive) = @_;
6147 my $volid = $drive->{file
};
6149 my $volume = $volid_hash->{$volid};
6151 # mark volid as "in-use" for next step
6152 $referenced->{$volid} = 1;
6153 if ($volume && (my $path = $volume->{path
})) {
6154 $referencedpath->{$path} = 1;
6157 return if drive_is_cdrom
($drive);
6160 my ($updated, $msg) = PVE
::QemuServer
::Drive
::update_disksize
($drive, $volume->{size
});
6161 if (defined($updated)) {
6163 $conf->{$opt} = print_drive
($updated);
6164 print "$prefix ($opt): $msg\n";
6168 # remove 'unusedX' entry if volume is used
6169 PVE
::QemuConfig-
>foreach_unused_volume($conf, sub {
6170 my ($opt, $drive) = @_;
6172 my $volid = $drive->{file
};
6176 $path = $volid_hash->{$volid}->{path
} if $volid_hash->{$volid};
6177 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6178 print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
6180 delete $conf->{$opt};
6183 $referenced->{$volid} = 1;
6184 $referencedpath->{$path} = 1 if $path;
6187 foreach my $volid (sort keys %$volid_hash) {
6188 next if $volid =~ m/vm-$vmid-state-/;
6189 next if $referenced->{$volid};
6190 my $path = $volid_hash->{$volid}->{path
};
6191 next if !$path; # just to be sure
6192 next if $referencedpath->{$path};
6194 my $key = PVE
::QemuConfig-
>add_unused_volume($conf, $volid);
6195 print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
6196 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6203 my ($vmid, $nolock, $dryrun) = @_;
6205 my $cfg = PVE
::Storage
::config
();
6207 print "rescan volumes...\n";
6208 my $volid_hash = scan_volids
($cfg, $vmid);
6210 my $updatefn = sub {
6213 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6215 PVE
::QemuConfig-
>check_lock($conf);
6218 foreach my $volid (keys %$volid_hash) {
6219 my $info = $volid_hash->{$volid};
6220 $vm_volids->{$volid} = $info if $info->{vmid
} && $info->{vmid
} == $vmid;
6223 my $changes = update_disk_config
($vmid, $conf, $vm_volids);
6225 PVE
::QemuConfig-
>write_config($vmid, $conf) if $changes && !$dryrun;
6228 if (defined($vmid)) {
6232 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6235 my $vmlist = config_list
();
6236 foreach my $vmid (keys %$vmlist) {
6240 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6246 sub restore_proxmox_backup_archive
{
6247 my ($archive, $vmid, $user, $options) = @_;
6249 my $storecfg = PVE
::Storage
::config
();
6251 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($archive);
6252 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
6254 my $fingerprint = $scfg->{fingerprint
};
6255 my $keyfile = PVE
::Storage
::PBSPlugin
::pbs_encryption_key_file_name
($storecfg, $storeid);
6257 my $repo = PVE
::PBSClient
::get_repository
($scfg);
6259 # This is only used for `pbs-restore` and the QEMU PBS driver (live-restore)
6260 my $password = PVE
::Storage
::PBSPlugin
::pbs_get_password
($scfg, $storeid);
6261 local $ENV{PBS_PASSWORD
} = $password;
6262 local $ENV{PBS_FINGERPRINT
} = $fingerprint if defined($fingerprint);
6264 my ($vtype, $pbs_backup_name, undef, undef, undef, undef, $format) =
6265 PVE
::Storage
::parse_volname
($storecfg, $archive);
6267 die "got unexpected vtype '$vtype'\n" if $vtype ne 'backup';
6269 die "got unexpected backup format '$format'\n" if $format ne 'pbs-vm';
6271 my $tmpdir = "/var/tmp/vzdumptmp$$";
6275 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6276 # disable interrupts (always do cleanups)
6280 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
6282 # Note: $oldconf is undef if VM does not exists
6283 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
6284 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
6285 my $new_conf_raw = '';
6287 my $rpcenv = PVE
::RPCEnvironment
::get
();
6296 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6298 my $cfgfn = "$tmpdir/qemu-server.conf";
6299 my $firewall_config_fn = "$tmpdir/fw.conf";
6300 my $index_fn = "$tmpdir/index.json";
6302 my $cmd = "restore";
6304 my $param = [$pbs_backup_name, "index.json", $index_fn];
6305 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6306 my $index = PVE
::Tools
::file_get_contents
($index_fn);
6307 $index = decode_json
($index);
6309 # print Dumper($index);
6310 foreach my $info (@{$index->{files
}}) {
6311 if ($info->{filename
} =~ m/^(drive-\S+).img.fidx$/) {
6313 if ($info->{size
} =~ m/^(\d+)$/) { # untaint size
6314 $devinfo->{$devname}->{size
} = $1;
6316 die "unable to parse file size in 'index.json' - got '$info->{size}'\n";
6321 my $is_qemu_server_backup = scalar(
6322 grep { $_->{filename
} eq 'qemu-server.conf.blob' } @{$index->{files
}}
6324 if (!$is_qemu_server_backup) {
6325 die "backup does not look like a qemu-server backup (missing 'qemu-server.conf' file)\n";
6327 my $has_firewall_config = scalar(grep { $_->{filename
} eq 'fw.conf.blob' } @{$index->{files
}});
6329 $param = [$pbs_backup_name, "qemu-server.conf", $cfgfn];
6330 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6332 if ($has_firewall_config) {
6333 $param = [$pbs_backup_name, "fw.conf", $firewall_config_fn];
6334 PVE
::Storage
::PBSPlugin
::run_raw_client_cmd
($scfg, $storeid, $cmd, $param);
6336 my $pve_firewall_dir = '/etc/pve/firewall';
6337 mkdir $pve_firewall_dir; # make sure the dir exists
6338 PVE
::Tools
::file_copy
($firewall_config_fn, "${pve_firewall_dir}/$vmid.fw");
6341 my $fh = IO
::File-
>new($cfgfn, "r") ||
6342 die "unable to read qemu-server.conf - $!\n";
6344 my $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $storecfg, $fh, $devinfo, $options);
6346 # fixme: rate limit?
6348 # create empty/temp config
6349 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\nlock: create");
6351 $restore_cleanup_oldconf->($storecfg, $vmid, $oldconf, $virtdev_hash) if $oldconf;
6354 my $map = $restore_allocate_devices->($storecfg, $virtdev_hash, $vmid);
6356 if (!$options->{live
}) {
6357 foreach my $virtdev (sort keys %$virtdev_hash) {
6358 my $d = $virtdev_hash->{$virtdev};
6359 next if $d->{is_cloudinit
}; # no need to restore cloudinit
6361 my $volid = $d->{volid
};
6363 my $path = PVE
::Storage
::path
($storecfg, $volid);
6365 my $pbs_restore_cmd = [
6366 '/usr/bin/pbs-restore',
6367 '--repository', $repo,
6369 "$d->{devname}.img.fidx",
6374 push @$pbs_restore_cmd, '--format', $d->{format
} if $d->{format
};
6375 push @$pbs_restore_cmd, '--keyfile', $keyfile if -e
$keyfile;
6377 if (PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $volid)) {
6378 push @$pbs_restore_cmd, '--skip-zero';
6381 my $dbg_cmdstring = PVE
::Tools
::cmd2string
($pbs_restore_cmd);
6382 print "restore proxmox backup image: $dbg_cmdstring\n";
6383 run_command
($pbs_restore_cmd);
6387 $fh->seek(0, 0) || die "seek failed - $!\n";
6389 my $cookie = { netcount
=> 0 };
6390 while (defined(my $line = <$fh>)) {
6391 $new_conf_raw .= restore_update_config_line
(
6403 if ($err || !$options->{live
}) {
6404 $restore_deactivate_volumes->($storecfg, $devinfo);
6410 $restore_destroy_volumes->($storecfg, $devinfo);
6414 if ($options->{live
}) {
6415 # keep lock during live-restore
6416 $new_conf_raw .= "\nlock: create";
6419 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
6421 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6423 eval { rescan
($vmid, 1); };
6426 PVE
::AccessControl
::add_vm_to_pool
($vmid, $options->{pool
}) if $options->{pool
};
6428 if ($options->{live
}) {
6434 local $SIG{PIPE
} = sub { die "got signal ($!) - abort\n"; };
6436 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6437 die "cannot do live-restore for template\n" if PVE
::QemuConfig-
>is_template($conf);
6439 pbs_live_restore
($vmid, $conf, $storecfg, $devinfo, $repo, $keyfile, $pbs_backup_name);
6441 PVE
::QemuConfig-
>remove_lock($vmid, "create");
6445 sub pbs_live_restore
{
6446 my ($vmid, $conf, $storecfg, $restored_disks, $repo, $keyfile, $snap) = @_;
6448 print "starting VM for live-restore\n";
6449 print "repository: '$repo', snapshot: '$snap'\n";
6451 my $pbs_backing = {};
6452 for my $ds (keys %$restored_disks) {
6453 $ds =~ m/^drive-(.*)$/;
6455 $pbs_backing->{$confname} = {
6456 repository
=> $repo,
6458 archive
=> "$ds.img.fidx",
6460 $pbs_backing->{$confname}->{keyfile
} = $keyfile if -e
$keyfile;
6462 my $drive = parse_drive
($confname, $conf->{$confname});
6463 print "restoring '$ds' to '$drive->{file}'\n";
6466 my $drives_streamed = 0;
6468 # make sure HA doesn't interrupt our restore by stopping the VM
6469 if (PVE
::HA
::Config
::vm_is_ha_managed
($vmid)) {
6470 run_command
(['ha-manager', 'set', "vm:$vmid", '--state', 'started']);
6473 # start VM with backing chain pointing to PBS backup, environment vars for PBS driver
6474 # in QEMU (PBS_PASSWORD and PBS_FINGERPRINT) are already set by our caller
6475 vm_start_nolock
($storecfg, $vmid, $conf, {paused
=> 1, 'pbs-backing' => $pbs_backing}, {});
6477 my $qmeventd_fd = register_qmeventd_handle
($vmid);
6479 # begin streaming, i.e. data copy from PBS to target disk for every vol,
6480 # this will effectively collapse the backing image chain consisting of
6481 # [target <- alloc-track -> PBS snapshot] to just [target] (alloc-track
6482 # removes itself once all backing images vanish with 'auto-remove=on')
6484 for my $ds (sort keys %$restored_disks) {
6485 my $job_id = "restore-$ds";
6486 mon_cmd
($vmid, 'block-stream',
6487 'job-id' => $job_id,
6490 $jobs->{$job_id} = {};
6493 mon_cmd
($vmid, 'cont');
6494 qemu_drive_mirror_monitor
($vmid, undef, $jobs, 'auto', 0, 'stream');
6496 print "restore-drive jobs finished successfully, removing all tracking block devices"
6497 ." to disconnect from Proxmox Backup Server\n";
6499 for my $ds (sort keys %$restored_disks) {
6500 mon_cmd
($vmid, 'blockdev-del', 'node-name' => "$ds-pbs");
6503 close($qmeventd_fd);
6509 warn "An error occured during live-restore: $err\n";
6510 _do_vm_stop
($storecfg, $vmid, 1, 1, 10, 0, 1);
6511 die "live-restore failed\n";
6515 sub restore_vma_archive
{
6516 my ($archive, $vmid, $user, $opts, $comp) = @_;
6518 my $readfrom = $archive;
6520 my $cfg = PVE
::Storage
::config
();
6522 my $bwlimit = $opts->{bwlimit
};
6524 my $dbg_cmdstring = '';
6525 my $add_pipe = sub {
6527 push @$commands, $cmd;
6528 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
6529 $dbg_cmdstring .= PVE
::Tools
::cmd2string
($cmd);
6534 if ($archive eq '-') {
6537 # If we use a backup from a PVE defined storage we also consider that
6538 # storage's rate limit:
6539 my (undef, $volid) = PVE
::Storage
::path_to_volume_id
($cfg, $archive);
6540 if (defined($volid)) {
6541 my ($sid, undef) = PVE
::Storage
::parse_volume_id
($volid);
6542 my $readlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [$sid], $bwlimit);
6544 print STDERR
"applying read rate limit: $readlimit\n";
6545 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
6546 $add_pipe->($cstream);
6552 my $info = PVE
::Storage
::decompressor_info
('vma', $comp);
6553 my $cmd = $info->{decompressor
};
6554 push @$cmd, $readfrom;
6558 my $tmpdir = "/var/tmp/vzdumptmp$$";
6561 # disable interrupts (always do cleanups)
6565 local $SIG{HUP
} = sub { warn "got interrupt - ignored\n"; };
6567 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
6568 POSIX
::mkfifo
($mapfifo, 0600);
6570 my $openfifo = sub { open($fifofh, '>', $mapfifo) or die $! };
6572 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
6579 my $rpcenv = PVE
::RPCEnvironment
::get
();
6581 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6583 # Note: $oldconf is undef if VM does not exist
6584 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
6585 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
6586 my $new_conf_raw = '';
6590 my $print_devmap = sub {
6591 my $cfgfn = "$tmpdir/qemu-server.conf";
6593 # we can read the config - that is already extracted
6594 my $fh = IO
::File-
>new($cfgfn, "r") ||
6595 die "unable to read qemu-server.conf - $!\n";
6597 my $fwcfgfn = "$tmpdir/qemu-server.fw";
6599 my $pve_firewall_dir = '/etc/pve/firewall';
6600 mkdir $pve_firewall_dir; # make sure the dir exists
6601 PVE
::Tools
::file_copy
($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
6604 my $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $cfg, $fh, $devinfo, $opts);
6606 foreach my $info (values %{$virtdev_hash}) {
6607 my $storeid = $info->{storeid
};
6608 next if defined($storage_limits{$storeid});
6610 my $limit = PVE
::Storage
::get_bandwidth_limit
('restore', [$storeid], $bwlimit) // 0;
6611 print STDERR
"rate limit for storage $storeid: $limit KiB/s\n" if $limit;
6612 $storage_limits{$storeid} = $limit * 1024;
6615 foreach my $devname (keys %$devinfo) {
6616 die "found no device mapping information for device '$devname'\n"
6617 if !$devinfo->{$devname}->{virtdev
};
6620 # create empty/temp config
6622 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\n");
6623 $restore_cleanup_oldconf->($cfg, $vmid, $oldconf, $virtdev_hash);
6627 my $map = $restore_allocate_devices->($cfg, $virtdev_hash, $vmid);
6629 # print restore information to $fifofh
6630 foreach my $virtdev (sort keys %$virtdev_hash) {
6631 my $d = $virtdev_hash->{$virtdev};
6632 next if $d->{is_cloudinit
}; # no need to restore cloudinit
6634 my $storeid = $d->{storeid
};
6635 my $volid = $d->{volid
};
6638 if (my $limit = $storage_limits{$storeid}) {
6639 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
6642 my $write_zeros = 1;
6643 if (PVE
::Storage
::volume_has_feature
($cfg, 'sparseinit', $volid)) {
6647 my $path = PVE
::Storage
::path
($cfg, $volid);
6649 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
6651 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
6654 $fh->seek(0, 0) || die "seek failed - $!\n";
6656 my $cookie = { netcount
=> 0 };
6657 while (defined(my $line = <$fh>)) {
6658 $new_conf_raw .= restore_update_config_line
(
6675 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6676 local $SIG{ALRM
} = sub { die "got timeout\n"; };
6678 $oldtimeout = alarm($timeout);
6685 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
6686 my ($dev_id, $size, $devname) = ($1, $2, $3);
6687 $devinfo->{$devname} = { size
=> $size, dev_id
=> $dev_id };
6688 } elsif ($line =~ m/^CTIME: /) {
6689 # we correctly received the vma config, so we can disable
6690 # the timeout now for disk allocation (set to 10 minutes, so
6691 # that we always timeout if something goes wrong)
6694 print $fifofh "done\n";
6695 my $tmp = $oldtimeout || 0;
6696 $oldtimeout = undef;
6703 print "restore vma archive: $dbg_cmdstring\n";
6704 run_command
($commands, input
=> $input, outfunc
=> $parser, afterfork
=> $openfifo);
6708 alarm($oldtimeout) if $oldtimeout;
6710 $restore_deactivate_volumes->($cfg, $devinfo);
6712 close($fifofh) if $fifofh;
6717 $restore_destroy_volumes->($cfg, $devinfo);
6721 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
6723 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6725 eval { rescan
($vmid, 1); };
6728 PVE
::AccessControl
::add_vm_to_pool
($vmid, $opts->{pool
}) if $opts->{pool
};
6731 sub restore_tar_archive
{
6732 my ($archive, $vmid, $user, $opts) = @_;
6734 if ($archive ne '-') {
6735 my $firstfile = tar_archive_read_firstfile
($archive);
6736 die "ERROR: file '$archive' does not look like a QemuServer vzdump backup\n"
6737 if $firstfile ne 'qemu-server.conf';
6740 my $storecfg = PVE
::Storage
::config
();
6742 # avoid zombie disks when restoring over an existing VM -> cleanup first
6743 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
6744 # skiplock=1 because qmrestore has set the 'create' lock itself already
6745 my $vmcfgfn = PVE
::QemuConfig-
>config_file($vmid);
6746 destroy_vm
($storecfg, $vmid, 1, { lock => 'restore' }) if -f
$vmcfgfn;
6748 my $tocmd = "/usr/lib/qemu-server/qmextract";
6750 $tocmd .= " --storage " . PVE
::Tools
::shellquote
($opts->{storage
}) if $opts->{storage
};
6751 $tocmd .= " --pool " . PVE
::Tools
::shellquote
($opts->{pool
}) if $opts->{pool
};
6752 $tocmd .= ' --prealloc' if $opts->{prealloc
};
6753 $tocmd .= ' --info' if $opts->{info
};
6755 # tar option "xf" does not autodetect compression when read from STDIN,
6756 # so we pipe to zcat
6757 my $cmd = "zcat -f|tar xf " . PVE
::Tools
::shellquote
($archive) . " " .
6758 PVE
::Tools
::shellquote
("--to-command=$tocmd");
6760 my $tmpdir = "/var/tmp/vzdumptmp$$";
6763 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
6764 local $ENV{VZDUMP_VMID
} = $vmid;
6765 local $ENV{VZDUMP_USER
} = $user;
6767 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6768 my $new_conf_raw = '';
6770 # disable interrupts (always do cleanups)
6774 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
6782 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6784 if ($archive eq '-') {
6785 print "extracting archive from STDIN\n";
6786 run_command
($cmd, input
=> "<&STDIN");
6788 print "extracting archive '$archive'\n";
6792 return if $opts->{info
};
6796 my $statfile = "$tmpdir/qmrestore.stat";
6797 if (my $fd = IO
::File-
>new($statfile, "r")) {
6798 while (defined (my $line = <$fd>)) {
6799 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6800 $map->{$1} = $2 if $1;
6802 print STDERR
"unable to parse line in statfile - $line\n";
6808 my $confsrc = "$tmpdir/qemu-server.conf";
6810 my $srcfd = IO
::File-
>new($confsrc, "r") || die "unable to open file '$confsrc'\n";
6812 my $cookie = { netcount
=> 0 };
6813 while (defined (my $line = <$srcfd>)) {
6814 $new_conf_raw .= restore_update_config_line
(
6825 tar_restore_cleanup
($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info
};
6831 PVE
::Tools
::file_set_contents
($conffile, $new_conf_raw);
6833 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6835 eval { rescan
($vmid, 1); };
6839 sub foreach_storage_used_by_vm
{
6840 my ($conf, $func) = @_;
6844 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6845 my ($ds, $drive) = @_;
6846 return if drive_is_cdrom
($drive);
6848 my $volid = $drive->{file
};
6850 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
6851 $sidhash->{$sid} = $sid if $sid;
6854 foreach my $sid (sort keys %$sidhash) {
6859 my $qemu_snap_storage = {
6862 sub do_snapshots_with_qemu
{
6863 my ($storecfg, $volid) = @_;
6865 my $storage_name = PVE
::Storage
::parse_volume_id
($volid);
6866 my $scfg = $storecfg->{ids
}->{$storage_name};
6867 die "could not find storage '$storage_name'\n" if !defined($scfg);
6869 if ($qemu_snap_storage->{$scfg->{type
}} && !$scfg->{krbd
}){
6873 if ($volid =~ m/\.(qcow2|qed)$/){
6880 sub qga_check_running
{
6881 my ($vmid, $nowarn) = @_;
6883 eval { mon_cmd
($vmid, "guest-ping", timeout
=> 3); };
6885 warn "Qemu Guest Agent is not running - $@" if !$nowarn;
6891 sub template_create
{
6892 my ($vmid, $conf, $disk) = @_;
6894 my $storecfg = PVE
::Storage
::config
();
6896 PVE
::QemuConfig-
>foreach_volume($conf, sub {
6897 my ($ds, $drive) = @_;
6899 return if drive_is_cdrom
($drive);
6900 return if $disk && $ds ne $disk;
6902 my $volid = $drive->{file
};
6903 return if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
6905 my $voliddst = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
6906 $drive->{file
} = $voliddst;
6907 $conf->{$ds} = print_drive
($drive);
6908 PVE
::QemuConfig-
>write_config($vmid, $conf);
6912 sub convert_iscsi_path
{
6915 if ($path =~ m
|^iscsi
://([^/]+)/([^/]+)/(.+)$|) {
6920 my $initiator_name = get_initiator_name
();
6922 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
6923 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
6926 die "cannot convert iscsi path '$path', unkown format\n";
6929 sub qemu_img_convert
{
6930 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
6932 my $storecfg = PVE
::Storage
::config
();
6933 my ($src_storeid, $src_volname) = PVE
::Storage
::parse_volume_id
($src_volid, 1);
6934 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid, 1);
6936 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
6940 my $src_is_iscsi = 0;
6944 PVE
::Storage
::activate_volumes
($storecfg, [$src_volid], $snapname);
6945 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
6946 $src_format = qemu_img_format
($src_scfg, $src_volname);
6947 $src_path = PVE
::Storage
::path
($storecfg, $src_volid, $snapname);
6948 $src_is_iscsi = ($src_path =~ m
|^iscsi
://|);
6949 $cachemode = 'none' if $src_scfg->{type
} eq 'zfspool';
6950 } elsif (-f
$src_volid) {
6951 $src_path = $src_volid;
6952 if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
6957 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
6959 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
6960 my $dst_format = qemu_img_format
($dst_scfg, $dst_volname);
6961 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
6962 my $dst_is_iscsi = ($dst_path =~ m
|^iscsi
://|);
6965 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
6966 push @$cmd, '-l', "snapshot.name=$snapname"
6967 if $snapname && $src_format && $src_format eq "qcow2";
6968 push @$cmd, '-t', 'none' if $dst_scfg->{type
} eq 'zfspool';
6969 push @$cmd, '-T', $cachemode if defined($cachemode);
6971 if ($src_is_iscsi) {
6972 push @$cmd, '--image-opts';
6973 $src_path = convert_iscsi_path
($src_path);
6974 } elsif ($src_format) {
6975 push @$cmd, '-f', $src_format;
6978 if ($dst_is_iscsi) {
6979 push @$cmd, '--target-image-opts';
6980 $dst_path = convert_iscsi_path
($dst_path);
6982 push @$cmd, '-O', $dst_format;
6985 push @$cmd, $src_path;
6987 if (!$dst_is_iscsi && $is_zero_initialized) {
6988 push @$cmd, "zeroinit:$dst_path";
6990 push @$cmd, $dst_path;
6995 if($line =~ m/\((\S+)\/100\
%\)/){
6997 my $transferred = int($size * $percent / 100);
6998 my $total_h = render_bytes
($size, 1);
6999 my $transferred_h = render_bytes
($transferred, 1);
7001 print "transferred $transferred_h of $total_h ($percent%)\n";
7006 eval { run_command
($cmd, timeout
=> undef, outfunc
=> $parser); };
7008 die "copy failed: $err" if $err;
7011 sub qemu_img_format
{
7012 my ($scfg, $volname) = @_;
7014 if ($scfg->{path
} && $volname =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
7021 sub qemu_drive_mirror
{
7022 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
7024 $jobs = {} if !$jobs;
7028 $jobs->{"drive-$drive"} = {};
7030 if ($dst_volid =~ /^nbd:/) {
7031 $qemu_target = $dst_volid;
7034 my $storecfg = PVE
::Storage
::config
();
7035 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid);
7037 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
7039 $format = qemu_img_format
($dst_scfg, $dst_volname);
7041 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
7043 $qemu_target = $is_zero_initialized ?
"zeroinit:$dst_path" : $dst_path;
7046 my $opts = { timeout
=> 10, device
=> "drive-$drive", mode
=> "existing", sync
=> "full", target
=> $qemu_target };
7047 $opts->{format
} = $format if $format;
7049 if (defined($src_bitmap)) {
7050 $opts->{sync
} = 'incremental';
7051 $opts->{bitmap
} = $src_bitmap;
7052 print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
7055 if (defined($bwlimit)) {
7056 $opts->{speed
} = $bwlimit * 1024;
7057 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
7059 print "drive mirror is starting for drive-$drive\n";
7062 # if a job already runs for this device we get an error, catch it for cleanup
7063 eval { mon_cmd
($vmid, "drive-mirror", %$opts); };
7065 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7067 die "mirroring error: $err\n";
7070 qemu_drive_mirror_monitor
($vmid, $vmiddst, $jobs, $completion, $qga);
7073 # $completion can be either
7074 # 'complete': wait until all jobs are ready, block-job-complete them (default)
7075 # 'cancel': wait until all jobs are ready, block-job-cancel them
7076 # 'skip': wait until all jobs are ready, return with block jobs in ready state
7077 # 'auto': wait until all jobs disappear, only use for jobs which complete automatically
7078 sub qemu_drive_mirror_monitor
{
7079 my ($vmid, $vmiddst, $jobs, $completion, $qga, $op) = @_;
7081 $completion //= 'complete';
7085 my $err_complete = 0;
7087 my $starttime = time ();
7089 die "block job ('$op') timed out\n" if $err_complete > 300;
7091 my $stats = mon_cmd
($vmid, "query-block-jobs");
7094 my $running_jobs = {};
7095 for my $stat (@$stats) {
7096 next if $stat->{type
} ne $op;
7097 $running_jobs->{$stat->{device
}} = $stat;
7100 my $readycounter = 0;
7102 for my $job_id (sort keys %$jobs) {
7103 my $job = $running_jobs->{$job_id};
7105 my $vanished = !defined($job);
7106 my $complete = defined($jobs->{$job_id}->{complete
}) && $vanished;
7107 if($complete || ($vanished && $completion eq 'auto')) {
7108 print "$job_id: $op-job finished\n";
7109 delete $jobs->{$job_id};
7113 die "$job_id: '$op' has been cancelled\n" if !defined($job);
7115 my $busy = $job->{busy
};
7116 my $ready = $job->{ready
};
7117 if (my $total = $job->{len
}) {
7118 my $transferred = $job->{offset
} || 0;
7119 my $remaining = $total - $transferred;
7120 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
7122 my $duration = $ctime - $starttime;
7123 my $total_h = render_bytes
($total, 1);
7124 my $transferred_h = render_bytes
($transferred, 1);
7126 my $status = sprintf(
7127 "transferred $transferred_h of $total_h ($percent%%) in %s",
7128 render_duration
($duration),
7133 $status .= ", still busy"; # shouldn't even happen? but mirror is weird
7135 $status .= ", ready";
7138 print "$job_id: $status\n" if !$jobs->{$job_id}->{ready
};
7139 $jobs->{$job_id}->{ready
} = $ready;
7142 $readycounter++ if $job->{ready
};
7145 last if scalar(keys %$jobs) == 0;
7147 if ($readycounter == scalar(keys %$jobs)) {
7148 print "all '$op' jobs are ready\n";
7150 # do the complete later (or has already been done)
7151 last if $completion eq 'skip' || $completion eq 'auto';
7153 if ($vmiddst && $vmiddst != $vmid) {
7154 my $agent_running = $qga && qga_check_running
($vmid);
7155 if ($agent_running) {
7156 print "freeze filesystem\n";
7157 eval { mon_cmd
($vmid, "guest-fsfreeze-freeze"); };
7159 print "suspend vm\n";
7160 eval { PVE
::QemuServer
::vm_suspend
($vmid, 1); };
7163 # if we clone a disk for a new target vm, we don't switch the disk
7164 PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs);
7166 if ($agent_running) {
7167 print "unfreeze filesystem\n";
7168 eval { mon_cmd
($vmid, "guest-fsfreeze-thaw"); };
7170 print "resume vm\n";
7171 eval { PVE
::QemuServer
::vm_resume
($vmid, 1, 1); };
7177 for my $job_id (sort keys %$jobs) {
7178 # try to switch the disk if source and destination are on the same guest
7179 print "$job_id: Completing block job_id...\n";
7182 if ($completion eq 'complete') {
7183 $op = 'block-job-complete';
7184 } elsif ($completion eq 'cancel') {
7185 $op = 'block-job-cancel';
7187 die "invalid completion value: $completion\n";
7189 eval { mon_cmd
($vmid, $op, device
=> $job_id) };
7190 if ($@ =~ m/cannot be completed/) {
7191 print "$job_id: block job cannot be completed, trying again.\n";
7194 print "$job_id: Completed successfully.\n";
7195 $jobs->{$job_id}->{complete
} = 1;
7206 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
7207 die "block job ($op) error: $err";
7211 sub qemu_blockjobs_cancel
{
7212 my ($vmid, $jobs) = @_;
7214 foreach my $job (keys %$jobs) {
7215 print "$job: Cancelling block job\n";
7216 eval { mon_cmd
($vmid, "block-job-cancel", device
=> $job); };
7217 $jobs->{$job}->{cancel
} = 1;
7221 my $stats = mon_cmd
($vmid, "query-block-jobs");
7223 my $running_jobs = {};
7224 foreach my $stat (@$stats) {
7225 $running_jobs->{$stat->{device
}} = $stat;
7228 foreach my $job (keys %$jobs) {
7230 if (defined($jobs->{$job}->{cancel
}) && !defined($running_jobs->{$job})) {
7231 print "$job: Done.\n";
7232 delete $jobs->{$job};
7236 last if scalar(keys %$jobs) == 0;
7243 my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
7244 $newvmid, $storage, $format, $full, $newvollist, $jobs, $completion, $qga, $bwlimit, $conf) = @_;
7249 print "create linked clone of drive $drivename ($drive->{file})\n";
7250 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $drive->{file
}, $newvmid, $snapname);
7251 push @$newvollist, $newvolid;
7254 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
7255 $storeid = $storage if $storage;
7257 my $dst_format = resolve_dst_disk_format
($storecfg, $storeid, $volname, $format);
7259 print "create full clone of drive $drivename ($drive->{file})\n";
7262 if (drive_is_cloudinit
($drive)) {
7263 $name = "vm-$newvmid-cloudinit";
7264 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7265 if ($scfg->{path
}) {
7266 $name .= ".$dst_format";
7269 $size = PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
;
7270 } elsif ($drivename eq 'efidisk0') {
7271 $size = get_efivars_size
($conf);
7273 ($size) = PVE
::Storage
::volume_size_info
($storecfg, $drive->{file
}, 10);
7275 $newvolid = PVE
::Storage
::vdisk_alloc
(
7276 $storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024)
7278 push @$newvollist, $newvolid;
7280 PVE
::Storage
::activate_volumes
($storecfg, [$newvolid]);
7282 if (drive_is_cloudinit
($drive)) {
7283 # when cloning multiple disks (e.g. during clone_vm) it might be the last disk
7284 # if this is the case, we have to complete any block-jobs still there from
7285 # previous drive-mirrors
7286 if (($completion eq 'complete') && (scalar(keys %$jobs) > 0)) {
7287 qemu_drive_mirror_monitor
($vmid, $newvmid, $jobs, $completion, $qga);
7292 my $sparseinit = PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $newvolid);
7293 if (!$running || $snapname) {
7294 # TODO: handle bwlimits
7295 if ($drivename eq 'efidisk0') {
7296 # the relevant data on the efidisk may be smaller than the source
7297 # e.g. on RBD/ZFS, so we use dd to copy only the amount
7298 # that is given by the OVMF_VARS.fd
7299 my $src_path = PVE
::Storage
::path
($storecfg, $drive->{file
});
7300 my $dst_path = PVE
::Storage
::path
($storecfg, $newvolid);
7302 # better for Ceph if block size is not too small, see bug #3324
7305 run_command
(['qemu-img', 'dd', '-n', '-O', $dst_format, "bs=$bs", "osize=$size",
7306 "if=$src_path", "of=$dst_path"]);
7308 qemu_img_convert
($drive->{file
}, $newvolid, $size, $snapname, $sparseinit);
7312 my $kvmver = get_running_qemu_version
($vmid);
7313 if (!min_version
($kvmver, 2, 7)) {
7314 die "drive-mirror with iothread requires qemu version 2.7 or higher\n"
7315 if $drive->{iothread
};
7318 qemu_drive_mirror
($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs,
7319 $completion, $qga, $bwlimit);
7324 my ($size) = eval { PVE
::Storage
::volume_size_info
($storecfg, $newvolid, 10) };
7327 $disk->{format
} = undef;
7328 $disk->{file
} = $newvolid;
7329 $disk->{size
} = $size if defined($size);
7334 sub get_running_qemu_version
{
7336 my $res = mon_cmd
($vmid, "query-version");
7337 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
7340 sub qemu_use_old_bios_files
{
7341 my ($machine_type) = @_;
7343 return if !$machine_type;
7345 my $use_old_bios_files = undef;
7347 if ($machine_type =~ m/^(\S+)\.pxe$/) {
7349 $use_old_bios_files = 1;
7351 my $version = extract_version
($machine_type, kvm_user_version
());
7352 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
7353 # load new efi bios files on migration. So this hack is required to allow
7354 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
7355 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
7356 $use_old_bios_files = !min_version
($version, 2, 4);
7359 return ($use_old_bios_files, $machine_type);
7362 sub get_efivars_size
{
7364 my $arch = get_vm_arch
($conf);
7365 my (undef, $ovmf_vars) = get_ovmf_files
($arch);
7366 die "uefi vars image '$ovmf_vars' not found\n" if ! -f
$ovmf_vars;
7367 return -s
$ovmf_vars;
7370 sub update_efidisk_size
{
7373 return if !defined($conf->{efidisk0
});
7375 my $disk = PVE
::QemuServer
::parse_drive
('efidisk0', $conf->{efidisk0
});
7376 $disk->{size
} = get_efivars_size
($conf);
7377 $conf->{efidisk0
} = print_drive
($disk);
7382 sub create_efidisk
($$$$$) {
7383 my ($storecfg, $storeid, $vmid, $fmt, $arch) = @_;
7385 my (undef, $ovmf_vars) = get_ovmf_files
($arch);
7386 die "EFI vars default image not found\n" if ! -f
$ovmf_vars;
7388 my $vars_size_b = -s
$ovmf_vars;
7389 my $vars_size = PVE
::Tools
::convert_size
($vars_size_b, 'b' => 'kb');
7390 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
7391 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
7393 qemu_img_convert
($ovmf_vars, $volid, $vars_size_b, undef, 0);
7394 my ($size) = PVE
::Storage
::volume_size_info
($storecfg, $volid, 3);
7396 return ($volid, $size/1024);
7399 sub vm_iothreads_list
{
7402 my $res = mon_cmd
($vmid, 'query-iothreads');
7405 foreach my $iothread (@$res) {
7406 $iothreads->{ $iothread->{id
} } = $iothread->{"thread-id"};
7413 my ($conf, $drive) = @_;
7417 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)) {
7419 } elsif ($conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single')) {
7425 my $controller = int($drive->{index} / $maxdev);
7426 my $controller_prefix = ($conf->{scsihw
} && $conf->{scsihw
} eq 'virtio-scsi-single')
7430 return ($maxdev, $controller, $controller_prefix);
7433 sub windows_version
{
7436 return 0 if !$ostype;
7440 if($ostype eq 'wxp' || $ostype eq 'w2k3' || $ostype eq 'w2k') {
7442 } elsif($ostype eq 'w2k8' || $ostype eq 'wvista') {
7444 } elsif ($ostype =~ m/^win(\d+)$/) {
7451 sub resolve_dst_disk_format
{
7452 my ($storecfg, $storeid, $src_volname, $format) = @_;
7453 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
7456 # if no target format is specified, use the source disk format as hint
7458 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7459 $format = qemu_img_format
($scfg, $src_volname);
7465 # test if requested format is supported - else use default
7466 my $supported = grep { $_ eq $format } @$validFormats;
7467 $format = $defFormat if !$supported;
7471 # NOTE: if this logic changes, please update docs & possibly gui logic
7472 sub find_vmstate_storage
{
7473 my ($conf, $storecfg) = @_;
7475 # first, return storage from conf if set
7476 return $conf->{vmstatestorage
} if $conf->{vmstatestorage
};
7478 my ($target, $shared, $local);
7480 foreach_storage_used_by_vm
($conf, sub {
7482 my $scfg = PVE
::Storage
::storage_config
($storecfg, $sid);
7483 my $dst = $scfg->{shared
} ? \
$shared : \
$local;
7484 $$dst = $sid if !$$dst || $scfg->{path
}; # prefer file based storage
7487 # second, use shared storage where VM has at least one disk
7488 # third, use local storage where VM has at least one disk
7489 # fall back to local storage
7490 $target = $shared // $local // 'local';
7496 my ($uuid, $uuid_str);
7497 UUID
::generate
($uuid);
7498 UUID
::unparse
($uuid, $uuid_str);
7502 sub generate_smbios1_uuid
{
7503 return "uuid=".generate_uuid
();
7509 mon_cmd
($vmid, 'nbd-server-stop');
7512 sub create_reboot_request
{
7514 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
7515 or die "failed to create reboot trigger file: $!\n";
7519 sub clear_reboot_request
{
7521 my $path = "/run/qemu-server/$vmid.reboot";
7524 $res = unlink($path);
7525 die "could not remove reboot request for $vmid: $!"
7526 if !$res && $! != POSIX
::ENOENT
;
7531 sub bootorder_from_legacy
{
7532 my ($conf, $bootcfg) = @_;
7534 my $boot = $bootcfg->{legacy
} || $boot_fmt->{legacy
}->{default};
7535 my $bootindex_hash = {};
7537 foreach my $o (split(//, $boot)) {
7538 $bootindex_hash->{$o} = $i*100;
7544 PVE
::QemuConfig-
>foreach_volume($conf, sub {
7545 my ($ds, $drive) = @_;
7547 if (drive_is_cdrom
($drive, 1)) {
7548 if ($bootindex_hash->{d
}) {
7549 $bootorder->{$ds} = $bootindex_hash->{d
};
7550 $bootindex_hash->{d
} += 1;
7552 } elsif ($bootindex_hash->{c
}) {
7553 $bootorder->{$ds} = $bootindex_hash->{c
}
7554 if $conf->{bootdisk
} && $conf->{bootdisk
} eq $ds;
7555 $bootindex_hash->{c
} += 1;
7559 if ($bootindex_hash->{n
}) {
7560 for (my $i = 0; $i < $MAX_NETS; $i++) {
7561 my $netname = "net$i";
7562 next if !$conf->{$netname};
7563 $bootorder->{$netname} = $bootindex_hash->{n
};
7564 $bootindex_hash->{n
} += 1;
7571 # Generate default device list for 'boot: order=' property. Matches legacy
7572 # default boot order, but with explicit device names. This is important, since
7573 # the fallback for when neither 'order' nor the old format is specified relies
7574 # on 'bootorder_from_legacy' above, and it would be confusing if this diverges.
7575 sub get_default_bootdevices
{
7581 my $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 0);
7582 push @ret, $first if $first;
7585 $first = PVE
::QemuServer
::Drive
::resolve_first_disk
($conf, 1);
7586 push @ret, $first if $first;
7589 for (my $i = 0; $i < $MAX_NETS; $i++) {
7590 my $netname = "net$i";
7591 next if !$conf->{$netname};
7592 push @ret, $netname;
7599 sub device_bootorder
{
7602 return bootorder_from_legacy
($conf) if !defined($conf->{boot
});
7604 my $boot = parse_property_string
($boot_fmt, $conf->{boot
});
7607 if (!defined($boot) || $boot->{legacy
}) {
7608 $bootorder = bootorder_from_legacy
($conf, $boot);
7609 } elsif ($boot->{order
}) {
7610 my $i = 100; # start at 100 to allow user to insert devices before us with -args
7611 for my $dev (PVE
::Tools
::split_list
($boot->{order
})) {
7612 $bootorder->{$dev} = $i++;
7619 sub register_qmeventd_handle
{
7623 my $peer = "/var/run/qmeventd.sock";
7628 $fh = IO
::Socket
::UNIX-
>new(Peer
=> $peer, Blocking
=> 0, Timeout
=> 1);
7630 if ($! != EINTR
&& $! != EAGAIN
) {
7631 die "unable to connect to qmeventd socket (vmid: $vmid) - $!\n";
7634 die "unable to connect to qmeventd socket (vmid: $vmid) - timeout "
7635 . "after $count retries\n";
7640 # send handshake to mark VM as backing up
7641 print $fh to_json
({vzdump
=> {vmid
=> "$vmid"}});
7643 # return handle to be closed later when inhibit is no longer required
7647 # bash completion helper
7649 sub complete_backup_archives
{
7650 my ($cmdname, $pname, $cvalue) = @_;
7652 my $cfg = PVE
::Storage
::config
();
7656 if ($cvalue =~ m/^([^:]+):/) {
7660 my $data = PVE
::Storage
::template_list
($cfg, $storeid, 'backup');
7663 foreach my $id (keys %$data) {
7664 foreach my $item (@{$data->{$id}}) {
7665 next if $item->{format
} !~ m/^vma\.(${\PVE::Storage::Plugin::COMPRESSOR_RE})$/;
7666 push @$res, $item->{volid
} if defined($item->{volid
});
7673 my $complete_vmid_full = sub {
7676 my $idlist = vmstatus
();
7680 foreach my $id (keys %$idlist) {
7681 my $d = $idlist->{$id};
7682 if (defined($running)) {
7683 next if $d->{template
};
7684 next if $running && $d->{status
} ne 'running';
7685 next if !$running && $d->{status
} eq 'running';
7694 return &$complete_vmid_full();
7697 sub complete_vmid_stopped
{
7698 return &$complete_vmid_full(0);
7701 sub complete_vmid_running
{
7702 return &$complete_vmid_full(1);
7705 sub complete_storage
{
7707 my $cfg = PVE
::Storage
::config
();
7708 my $ids = $cfg->{ids
};
7711 foreach my $sid (keys %$ids) {
7712 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, undef, 1);
7713 next if !$ids->{$sid}->{content
}->{images
};
7720 sub complete_migration_storage
{
7721 my ($cmd, $param, $current_value, $all_args) = @_;
7723 my $targetnode = @$all_args[1];
7725 my $cfg = PVE
::Storage
::config
();
7726 my $ids = $cfg->{ids
};
7729 foreach my $sid (keys %$ids) {
7730 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, $targetnode, 1);
7731 next if !$ids->{$sid}->{content
}->{images
};
7740 my $qmpstatus = eval {
7741 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid);
7742 mon_cmd
($vmid, "query-status");
7745 return $qmpstatus && $qmpstatus->{status
} eq "paused";
7748 sub check_volume_storage_type
{
7749 my ($storecfg, $vol) = @_;
7751 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($vol);
7752 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7753 my ($vtype) = PVE
::Storage
::parse_volname
($storecfg, $vol);
7755 die "storage '$storeid' does not support content-type '$vtype'\n"
7756 if !$scfg->{content
}->{$vtype};