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 cfs_lock_file);
30 use PVE
::DataCenterConfig
;
31 use PVE
::Exception
qw(raise raise_param_exc);
32 use PVE
::GuestHelpers
;
34 use PVE
::JSONSchema
qw(get_standard_option);
36 use PVE
::RPCEnvironment
;
40 use PVE
::Tools
qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach get_host_arch $IPV6RE);
44 use PVE
::QemuServer
::Helpers
;
45 use PVE
::QemuServer
::Cloudinit
;
46 use PVE
::QemuServer
::Memory
;
47 use PVE
::QemuServer
::Monitor
qw(mon_cmd);
48 use PVE
::QemuServer
::PCI
qw(print_pci_addr print_pcie_addr print_pcie_root_port);
49 use PVE
::QemuServer
::USB
qw(parse_usb_device);
51 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
54 "$EDK2_FW_BASE/OVMF_CODE.fd",
55 "$EDK2_FW_BASE/OVMF_VARS.fd"
58 "$EDK2_FW_BASE/AAVMF_CODE.fd",
59 "$EDK2_FW_BASE/AAVMF_VARS.fd"
63 my $qemu_snap_storage = { rbd
=> 1 };
65 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
67 my $QEMU_FORMAT_RE = qr/raw|cow|qcow|qcow2|qed|vmdk|cloop/;
69 # Note about locking: we use flock on the config file protect
70 # against concurent actions.
71 # Aditionaly, we have a 'lock' setting in the config file. This
72 # can be set to 'migrate', 'backup', 'snapshot' or 'rollback'. Most actions are not
73 # allowed when such lock is set. But you can ignore this kind of
74 # lock with the --skiplock flag.
76 cfs_register_file
('/qemu-server/',
80 PVE
::JSONSchema
::register_standard_option
('pve-qm-stateuri', {
81 description
=> "Some command save/restore state from this location.",
87 PVE
::JSONSchema
::register_standard_option
('pve-qm-image-format', {
89 enum
=> [qw(raw cow qcow qed qcow2 vmdk cloop)],
90 description
=> "The drive's backing file's data format.",
94 PVE
::JSONSchema
::register_standard_option
('pve-qemu-machine', {
95 description
=> "Specifies the Qemu machine type.",
97 pattern
=> '(pc|pc(-i440fx)?-\d+(\.\d+)+(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\.pxe)?|virt(?:-\d+(\.\d+)+)?)',
102 #no warnings 'redefine';
105 my ($controller, $vmid, $option, $value) = @_;
107 my $path = "/sys/fs/cgroup/$controller/qemu.slice/$vmid.scope/$option";
108 PVE
::ProcFSTools
::write_proc_entry
($path, $value);
112 my $nodename = PVE
::INotify
::nodename
();
114 my $cpu_vendor_list = {
116 486 => 'GenuineIntel',
117 pentium
=> 'GenuineIntel',
118 pentium2
=> 'GenuineIntel',
119 pentium3
=> 'GenuineIntel',
120 coreduo
=> 'GenuineIntel',
121 core2duo
=> 'GenuineIntel',
122 Conroe
=> 'GenuineIntel',
123 Penryn
=> 'GenuineIntel',
124 Nehalem
=> 'GenuineIntel',
125 'Nehalem-IBRS' => 'GenuineIntel',
126 Westmere
=> 'GenuineIntel',
127 'Westmere-IBRS' => 'GenuineIntel',
128 SandyBridge
=> 'GenuineIntel',
129 'SandyBridge-IBRS' => 'GenuineIntel',
130 IvyBridge
=> 'GenuineIntel',
131 'IvyBridge-IBRS' => 'GenuineIntel',
132 Haswell
=> 'GenuineIntel',
133 'Haswell-IBRS' => 'GenuineIntel',
134 'Haswell-noTSX' => 'GenuineIntel',
135 'Haswell-noTSX-IBRS' => 'GenuineIntel',
136 Broadwell
=> 'GenuineIntel',
137 'Broadwell-IBRS' => 'GenuineIntel',
138 'Broadwell-noTSX' => 'GenuineIntel',
139 'Broadwell-noTSX-IBRS' => 'GenuineIntel',
140 'Skylake-Client' => 'GenuineIntel',
141 'Skylake-Client-IBRS' => 'GenuineIntel',
142 'Skylake-Server' => 'GenuineIntel',
143 'Skylake-Server-IBRS' => 'GenuineIntel',
144 'Cascadelake-Server' => 'GenuineIntel',
145 KnightsMill
=> 'GenuineIntel',
149 athlon
=> 'AuthenticAMD',
150 phenom
=> 'AuthenticAMD',
151 Opteron_G1
=> 'AuthenticAMD',
152 Opteron_G2
=> 'AuthenticAMD',
153 Opteron_G3
=> 'AuthenticAMD',
154 Opteron_G4
=> 'AuthenticAMD',
155 Opteron_G5
=> 'AuthenticAMD',
156 EPYC
=> 'AuthenticAMD',
157 'EPYC-IBPB' => 'AuthenticAMD',
159 # generic types, use vendor from host node
168 my @supported_cpu_flags = (
182 my $cpu_flag = qr/[+-](@{[join('|', @supported_cpu_flags)]})/;
186 description
=> "Emulated CPU type.",
188 enum
=> [ sort { "\L$a" cmp "\L$b" } keys %$cpu_vendor_list ],
193 description
=> "Do not identify as a KVM virtual machine.",
200 pattern
=> qr/[a-zA-Z0-9]{1,12}/,
201 format_description
=> 'vendor-id',
202 description
=> 'The Hyper-V vendor ID. Some drivers or programs inside Windows guests need a specific ID.',
206 description
=> "List of additional CPU flags separated by ';'."
207 . " Use '+FLAG' to enable, '-FLAG' to disable a flag."
208 . " Currently supported flags: @{[join(', ', @supported_cpu_flags)]}.",
209 format_description
=> '+FLAG[;-FLAG...]',
211 pattern
=> qr/$cpu_flag(;$cpu_flag)*/,
220 enum
=> [qw(i6300esb ib700)],
221 description
=> "Watchdog type to emulate.",
222 default => 'i6300esb',
227 enum
=> [qw(reset shutdown poweroff pause debug none)],
228 description
=> "The action to perform if after activation the guest fails to poll the watchdog in time.",
232 PVE
::JSONSchema
::register_format
('pve-qm-watchdog', $watchdog_fmt);
236 description
=> "Enable/disable Qemu GuestAgent.",
241 fstrim_cloned_disks
=> {
242 description
=> "Run fstrim after cloning/moving a disk.",
248 description
=> "Select the agent type",
252 enum
=> [qw(virtio isa)],
258 description
=> "Select the VGA type.",
263 enum
=> [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio vmware)],
266 description
=> "Sets the VGA memory (in MiB). Has no effect with serial display.",
278 description
=> "The size of the file in MB.",
282 pattern
=> '[a-zA-Z0-9\-]+',
284 format_description
=> 'string',
285 description
=> "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
292 enum
=> [qw(ich9-intel-hda intel-hda AC97)],
293 description
=> "Configure an audio device."
300 description
=> "Driver backend for the audio device."
304 my $spice_enhancements_fmt = {
309 description
=> "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
313 enum
=> ['off', 'all', 'filter'],
316 description
=> "Enable video streaming. Uses compression for detected video streams."
324 description
=> "Specifies whether a VM will be started during system bootup.",
330 description
=> "Automatic restart after crash (currently ignored).",
335 type
=> 'string', format
=> 'pve-hotplug-features',
336 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'.",
337 default => 'network,disk,usb',
342 description
=> "Allow reboot. If set to '0' the VM exit on reboot.",
348 description
=> "Lock/unlock the VM.",
349 enum
=> [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
354 description
=> "Limit of CPU usage.",
355 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.",
363 description
=> "CPU weight for a VM.",
364 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.",
372 description
=> "Amount of RAM for the VM in MB. This is the maximum available memory when you use the balloon device.",
379 description
=> "Amount of target RAM for the VM in MB. Using zero disables the ballon driver.",
385 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.",
393 description
=> "Keybord layout for vnc server. Default is read from the '/etc/pve/datacenter.cfg' configuration file.".
394 "It should not be necessary to set it.",
395 enum
=> PVE
::Tools
::kvmkeymaplist
(),
400 type
=> 'string', format
=> 'dns-name',
401 description
=> "Set a name for the VM. Only used on the configuration web interface.",
406 description
=> "SCSI controller model",
407 enum
=> [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
413 description
=> "Description for the VM. Only used on the configuration web interface. This is saved as comment inside the configuration file.",
418 enum
=> [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 l24 l26 solaris)],
419 description
=> "Specify guest operating system.",
420 verbose_description
=> <<EODESC,
421 Specify guest operating system. This is used to enable special
422 optimization/features for specific operating systems:
425 other;; unspecified OS
426 wxp;; Microsoft Windows XP
427 w2k;; Microsoft Windows 2000
428 w2k3;; Microsoft Windows 2003
429 w2k8;; Microsoft Windows 2008
430 wvista;; Microsoft Windows Vista
431 win7;; Microsoft Windows 7
432 win8;; Microsoft Windows 8/2012/2012r2
433 win10;; Microsoft Windows 10/2016
434 l24;; Linux 2.4 Kernel
435 l26;; Linux 2.6 - 5.X Kernel
436 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
442 description
=> "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n).",
443 pattern
=> '[acdn]{1,4}',
448 type
=> 'string', format
=> 'pve-qm-bootdisk',
449 description
=> "Enable booting from specified disk.",
450 pattern
=> '(ide|sata|scsi|virtio)\d+',
455 description
=> "The number of CPUs. Please use option -sockets instead.",
462 description
=> "The number of CPU sockets.",
469 description
=> "The number of cores per socket.",
476 description
=> "Enable/disable NUMA.",
482 description
=> "Enable/disable hugepages memory.",
483 enum
=> [qw(any 2 1024)],
488 description
=> "Number of hotplugged vcpus.",
495 description
=> "Enable/disable ACPI.",
500 description
=> "Enable/disable Qemu GuestAgent and its properties.",
502 format
=> $agent_fmt,
507 description
=> "Enable/disable KVM hardware virtualization.",
513 description
=> "Enable/disable time drift fix.",
519 description
=> "Set the real time clock to local time. This is enabled by default if ostype indicates a Microsoft OS.",
524 description
=> "Freeze CPU at startup (use 'c' monitor command to start execution).",
528 type
=> 'string', format
=> $vga_fmt,
529 description
=> "Configure the VGA hardware.",
530 verbose_description
=> "Configure the VGA Hardware. If you want to use ".
531 "high resolution modes (>= 1280x1024x16) you may need to increase " .
532 "the vga memory option. Since QEMU 2.9 the default VGA display type " .
533 "is 'std' for all OS types besides some Windows versions (XP and " .
534 "older) which use 'cirrus'. The 'qxl' option enables the SPICE " .
535 "display server. For win* OS you can select how many independent " .
536 "displays you want, Linux guests can add displays them self.\n".
537 "You can also run without any graphic card, using a serial device as terminal.",
541 type
=> 'string', format
=> 'pve-qm-watchdog',
542 description
=> "Create a virtual hardware watchdog device.",
543 verbose_description
=> "Create a virtual hardware watchdog device. Once enabled" .
544 " (by a guest action), the watchdog must be periodically polled " .
545 "by an agent inside the guest or else the watchdog will reset " .
546 "the guest (or execute the respective action specified)",
551 typetext
=> "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
552 description
=> "Set the initial date of the real time clock. Valid format for date are: 'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
553 pattern
=> '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
556 startup
=> get_standard_option
('pve-startup-order'),
560 description
=> "Enable/disable Template.",
566 description
=> "Arbitrary arguments passed to kvm.",
567 verbose_description
=> <<EODESCR,
568 Arbitrary arguments passed to kvm, for example:
570 args: -no-reboot -no-hpet
572 NOTE: this option is for experts only.
579 description
=> "Enable/disable the USB tablet device.",
580 verbose_description
=> "Enable/disable the USB tablet device. This device is " .
581 "usually needed to allow absolute mouse positioning with VNC. " .
582 "Else the mouse runs out of sync with normal VNC clients. " .
583 "If you're running lots of console-only guests on one host, " .
584 "you may consider disabling this to save some context switches. " .
585 "This is turned off by default if you use spice (-vga=qxl).",
590 description
=> "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
594 migrate_downtime
=> {
597 description
=> "Set maximum tolerated downtime (in seconds) for migrations.",
603 type
=> 'string', format
=> 'pve-qm-ide',
604 typetext
=> '<volume>',
605 description
=> "This is an alias for option -ide2",
609 description
=> "Emulated CPU type.",
613 parent
=> get_standard_option
('pve-snapshot-name', {
615 description
=> "Parent snapshot name. This is used internally, and should not be modified.",
619 description
=> "Timestamp for snapshots.",
625 type
=> 'string', format
=> 'pve-volume-id',
626 description
=> "Reference to a volume which stores the VM state. This is used internally for snapshots.",
628 vmstatestorage
=> get_standard_option
('pve-storage-id', {
629 description
=> "Default storage for VM state volumes/files.",
632 runningmachine
=> get_standard_option
('pve-qemu-machine', {
633 description
=> "Specifies the Qemu machine type of the running vm. This is used internally for snapshots.",
635 machine
=> get_standard_option
('pve-qemu-machine'),
637 description
=> "Virtual processor architecture. Defaults to the host.",
640 enum
=> [qw(x86_64 aarch64)],
643 description
=> "Specify SMBIOS type 1 fields.",
644 type
=> 'string', format
=> 'pve-qm-smbios1',
651 description
=> "Sets the protection flag of the VM. This will disable the remove VM and remove disk operations.",
657 enum
=> [ qw(seabios ovmf) ],
658 description
=> "Select BIOS implementation.",
659 default => 'seabios',
663 pattern
=> '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
664 format_description
=> 'UUID',
665 description
=> "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0' to disable explicitly.",
666 verbose_description
=> "The VM generation ID (vmgenid) device exposes a".
667 " 128-bit integer value identifier to the guest OS. This allows to".
668 " notify the guest operating system when the virtual machine is".
669 " executed with a different configuration (e.g. snapshot execution".
670 " or creation from a template). The guest operating system notices".
671 " the change, and is then able to react as appropriate by marking".
672 " its copies of distributed databases as dirty, re-initializing its".
673 " random number generator, etc.\n".
674 "Note that auto-creation only works when done throug API/CLI create".
675 " or update methods, but not when manually editing the config file.",
676 default => "1 (autogenerated)",
681 format
=> 'pve-volume-id',
683 description
=> "Script that will be executed during various steps in the vms lifetime.",
687 format
=> $ivshmem_fmt,
688 description
=> "Inter-VM shared memory. Useful for direct communication between VMs, or to the host.",
693 format
=> $audio_fmt,
694 description
=> "Configure a audio device, useful in combination with QXL/Spice.",
697 spice_enhancements
=> {
699 format
=> $spice_enhancements_fmt,
700 description
=> "Configure additional enhancements for SPICE.",
709 description
=> 'Specify a custom file containing all meta data passed to the VM via cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
710 format
=> 'pve-volume-id',
711 format_description
=> 'volume',
716 description
=> 'Specify a custom file containing all network data passed to the VM via cloud-init.',
717 format
=> 'pve-volume-id',
718 format_description
=> 'volume',
723 description
=> 'Specify a custom file containing all user data passed to the VM via cloud-init.',
724 format
=> 'pve-volume-id',
725 format_description
=> 'volume',
728 PVE
::JSONSchema
::register_format
('pve-qm-cicustom', $cicustom_fmt);
730 my $confdesc_cloudinit = {
734 description
=> 'Specifies the cloud-init configuration format. The default depends on the configured operating system type (`ostype`. We use the `nocloud` format for Linux, and `configdrive2` for windows.',
735 enum
=> ['configdrive2', 'nocloud'],
740 description
=> "cloud-init: User name to change ssh keys and password for instead of the image's configured default user.",
745 description
=> 'cloud-init: Password to assign the user. Using this is generally not recommended. Use ssh keys instead. Also note that older cloud-init versions do not support hashed passwords.',
750 description
=> 'cloud-init: Specify custom files to replace the automatically generated ones at start.',
751 format
=> 'pve-qm-cicustom',
756 description
=> "cloud-init: Sets DNS search domains for a container. Create will automatically use the setting from the host if neither searchdomain nor nameserver are set.",
760 type
=> 'string', format
=> 'address-list',
761 description
=> "cloud-init: Sets DNS server IP address for a container. Create will automatically use the setting from the host if neither searchdomain nor nameserver are set.",
766 format
=> 'urlencoded',
767 description
=> "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
771 # what about other qemu settings ?
773 #machine => 'string',
786 ##soundhw => 'string',
788 while (my ($k, $v) = each %$confdesc) {
789 PVE
::JSONSchema
::register_standard_option
("pve-qm-$k", $v);
792 my $MAX_IDE_DISKS = 4;
793 my $MAX_SCSI_DISKS = 14;
794 my $MAX_VIRTIO_DISKS = 16;
795 my $MAX_SATA_DISKS = 6;
796 my $MAX_USB_DEVICES = 5;
798 my $MAX_UNUSED_DISKS = 256;
799 my $MAX_HOSTPCI_DEVICES = 16;
800 my $MAX_SERIAL_PORTS = 4;
801 my $MAX_PARALLEL_PORTS = 3;
807 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
808 description
=> "CPUs accessing this NUMA node.",
809 format_description
=> "id[-id];...",
813 description
=> "Amount of memory this NUMA node provides.",
818 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
819 description
=> "Host NUMA nodes to use.",
820 format_description
=> "id[-id];...",
825 enum
=> [qw(preferred bind interleave)],
826 description
=> "NUMA allocation policy.",
830 PVE
::JSONSchema
::register_format
('pve-qm-numanode', $numa_fmt);
833 type
=> 'string', format
=> $numa_fmt,
834 description
=> "NUMA topology.",
836 PVE
::JSONSchema
::register_standard_option
("pve-qm-numanode", $numadesc);
838 for (my $i = 0; $i < $MAX_NUMA; $i++) {
839 $confdesc->{"numa$i"} = $numadesc;
842 my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000', 'pcnet', 'virtio',
843 'ne2k_isa', 'i82551', 'i82557b', 'i82559er', 'vmxnet3',
844 'e1000-82540em', 'e1000-82544gc', 'e1000-82545em'];
845 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
847 my $net_fmt_bridge_descr = <<__EOD__;
848 Bridge to attach the network device to. The Proxmox VE standard bridge
851 If you do not specify a bridge, we create a kvm user (NATed) network
852 device, which provides DHCP and DNS services. The following addresses
859 The DHCP server assign addresses to the guest starting from 10.0.2.15.
863 macaddr
=> get_standard_option
('mac-addr', {
864 description
=> "MAC address. That address must be unique withing your network. This is automatically generated if not specified.",
868 description
=> "Network Card Model. The 'virtio' model provides the best performance with very low CPU overhead. If your guest does not support this driver, it is usually best to use 'e1000'.",
869 enum
=> $nic_model_list,
872 (map { $_ => { keyAlias
=> 'model', alias
=> 'macaddr' }} @$nic_model_list),
875 description
=> $net_fmt_bridge_descr,
876 format_description
=> 'bridge',
881 minimum
=> 0, maximum
=> 16,
882 description
=> 'Number of packet queues to be used on the device.',
888 description
=> "Rate limit in mbps (megabytes per second) as floating point number.",
893 minimum
=> 1, maximum
=> 4094,
894 description
=> 'VLAN tag to apply to packets on this interface.',
899 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
900 description
=> 'VLAN trunks to pass through this interface.',
901 format_description
=> 'vlanid[;vlanid...]',
906 description
=> 'Whether this interface should be protected by the firewall.',
911 description
=> 'Whether this interface should be disconnected (like pulling the plug).',
918 type
=> 'string', format
=> $net_fmt,
919 description
=> "Specify network devices.",
922 PVE
::JSONSchema
::register_standard_option
("pve-qm-net", $netdesc);
927 format
=> 'pve-ipv4-config',
928 format_description
=> 'IPv4Format/CIDR',
929 description
=> 'IPv4 address in CIDR format.',
936 format_description
=> 'GatewayIPv4',
937 description
=> 'Default gateway for IPv4 traffic.',
943 format
=> 'pve-ipv6-config',
944 format_description
=> 'IPv6Format/CIDR',
945 description
=> 'IPv6 address in CIDR format.',
952 format_description
=> 'GatewayIPv6',
953 description
=> 'Default gateway for IPv6 traffic.',
958 PVE
::JSONSchema
::register_format
('pve-qm-ipconfig', $ipconfig_fmt);
961 type
=> 'string', format
=> 'pve-qm-ipconfig',
962 description
=> <<'EODESCR',
963 cloud-init: Specify IP addresses and gateways for the corresponding interface.
965 IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
967 The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit gateway should be provided.
968 For IPv6 the special string 'auto' can be used to use stateless autoconfiguration.
970 If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using dhcp on IPv4.
973 PVE
::JSONSchema
::register_standard_option
("pve-qm-ipconfig", $netdesc);
975 for (my $i = 0; $i < $MAX_NETS; $i++) {
976 $confdesc->{"net$i"} = $netdesc;
977 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
980 foreach my $key (keys %$confdesc_cloudinit) {
981 $confdesc->{$key} = $confdesc_cloudinit->{$key};
984 PVE
::JSONSchema
::register_format
('pve-volume-id-or-qm-path', \
&verify_volume_id_or_qm_path
);
985 sub verify_volume_id_or_qm_path
{
986 my ($volid, $noerr) = @_;
988 if ($volid eq 'none' || $volid eq 'cdrom' || $volid =~ m
|^/|) {
992 # if its neither 'none' nor 'cdrom' nor a path, check if its a volume-id
993 $volid = eval { PVE
::JSONSchema
::check_format
('pve-volume-id', $volid, '') };
995 return undef if $noerr;
1003 my %drivedesc_base = (
1004 volume
=> { alias
=> 'file' },
1007 format
=> 'pve-volume-id-or-qm-path',
1009 format_description
=> 'volume',
1010 description
=> "The drive's backing volume.",
1014 enum
=> [qw(cdrom disk)],
1015 description
=> "The drive's media type.",
1021 description
=> "Force the drive's physical geometry to have a specific cylinder count.",
1026 description
=> "Force the drive's physical geometry to have a specific head count.",
1031 description
=> "Force the drive's physical geometry to have a specific sector count.",
1036 enum
=> [qw(none lba auto)],
1037 description
=> "Force disk geometry bios translation mode.",
1042 description
=> "Controls qemu's snapshot mode feature."
1043 . " If activated, changes made to the disk are temporary and will"
1044 . " be discarded when the VM is shutdown.",
1049 enum
=> [qw(none writethrough writeback unsafe directsync)],
1050 description
=> "The drive's cache mode",
1053 format
=> get_standard_option
('pve-qm-image-format'),
1056 format
=> 'disk-size',
1057 format_description
=> 'DiskSize',
1058 description
=> "Disk size. This is purely informational and has no effect.",
1063 description
=> "Whether the drive should be included when making backups.",
1068 description
=> 'Whether the drive should considered for replication jobs.',
1074 enum
=> [qw(ignore report stop)],
1075 description
=> 'Read error action.',
1080 enum
=> [qw(enospc ignore report stop)],
1081 description
=> 'Write error action.',
1086 enum
=> [qw(native threads)],
1087 description
=> 'AIO type to use.',
1092 enum
=> [qw(ignore on)],
1093 description
=> 'Controls whether to pass discard/trim requests to the underlying storage.',
1098 description
=> 'Controls whether to detect and try to optimize writes of zeroes.',
1103 format
=> 'urlencoded',
1104 format_description
=> 'serial',
1105 maxLength
=> 20*3, # *3 since it's %xx url enoded
1106 description
=> "The drive's reported serial number, url-encoded, up to 20 bytes long.",
1111 description
=> 'Mark this locally-managed volume as available on all nodes',
1112 verbose_description
=> "Mark this locally-managed volume as available on all nodes.\n\nWARNING: This option does not share the volume automatically, it assumes it is shared already!",
1118 my %iothread_fmt = ( iothread
=> {
1120 description
=> "Whether to use iothreads for this drive",
1127 format
=> 'urlencoded',
1128 format_description
=> 'model',
1129 maxLength
=> 40*3, # *3 since it's %xx url enoded
1130 description
=> "The drive's reported model name, url-encoded, up to 40 bytes long.",
1138 description
=> "Number of queues.",
1144 my %scsiblock_fmt = (
1147 description
=> "whether to use scsi-block for full passthrough of host block device\n\nWARNING: can lead to I/O errors in combination with low memory or high memory fragmentation on host",
1156 description
=> "Whether to expose this drive as an SSD, rather than a rotational hard disk.",
1164 pattern
=> qr/^(0x)[0-9a-fA-F]{16}/,
1165 format_description
=> 'wwn',
1166 description
=> "The drive's worldwide name, encoded as 16 bytes hex string, prefixed by '0x'.",
1171 my $add_throttle_desc = sub {
1172 my ($key, $type, $what, $unit, $longunit, $minimum) = @_;
1175 format_description
=> $unit,
1176 description
=> "Maximum $what in $longunit.",
1179 $d->{minimum
} = $minimum if defined($minimum);
1180 $drivedesc_base{$key} = $d;
1182 # throughput: (leaky bucket)
1183 $add_throttle_desc->('bps', 'integer', 'r/w speed', 'bps', 'bytes per second');
1184 $add_throttle_desc->('bps_rd', 'integer', 'read speed', 'bps', 'bytes per second');
1185 $add_throttle_desc->('bps_wr', 'integer', 'write speed', 'bps', 'bytes per second');
1186 $add_throttle_desc->('mbps', 'number', 'r/w speed', 'mbps', 'megabytes per second');
1187 $add_throttle_desc->('mbps_rd', 'number', 'read speed', 'mbps', 'megabytes per second');
1188 $add_throttle_desc->('mbps_wr', 'number', 'write speed', 'mbps', 'megabytes per second');
1189 $add_throttle_desc->('iops', 'integer', 'r/w I/O', 'iops', 'operations per second');
1190 $add_throttle_desc->('iops_rd', 'integer', 'read I/O', 'iops', 'operations per second');
1191 $add_throttle_desc->('iops_wr', 'integer', 'write I/O', 'iops', 'operations per second');
1193 # pools: (pool of IO before throttling starts taking effect)
1194 $add_throttle_desc->('mbps_max', 'number', 'unthrottled r/w pool', 'mbps', 'megabytes per second');
1195 $add_throttle_desc->('mbps_rd_max', 'number', 'unthrottled read pool', 'mbps', 'megabytes per second');
1196 $add_throttle_desc->('mbps_wr_max', 'number', 'unthrottled write pool', 'mbps', 'megabytes per second');
1197 $add_throttle_desc->('iops_max', 'integer', 'unthrottled r/w I/O pool', 'iops', 'operations per second');
1198 $add_throttle_desc->('iops_rd_max', 'integer', 'unthrottled read I/O pool', 'iops', 'operations per second');
1199 $add_throttle_desc->('iops_wr_max', 'integer', 'unthrottled write I/O pool', 'iops', 'operations per second');
1202 $add_throttle_desc->('bps_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
1203 $add_throttle_desc->('bps_rd_max_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
1204 $add_throttle_desc->('bps_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
1205 $add_throttle_desc->('iops_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
1206 $add_throttle_desc->('iops_rd_max_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
1207 $add_throttle_desc->('iops_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
1210 $drivedesc_base{'bps_rd_length'} = { alias
=> 'bps_rd_max_length' };
1211 $drivedesc_base{'bps_wr_length'} = { alias
=> 'bps_wr_max_length' };
1212 $drivedesc_base{'iops_rd_length'} = { alias
=> 'iops_rd_max_length' };
1213 $drivedesc_base{'iops_wr_length'} = { alias
=> 'iops_wr_max_length' };
1221 PVE
::JSONSchema
::register_format
("pve-qm-ide", $ide_fmt);
1225 type
=> 'string', format
=> $ide_fmt,
1226 description
=> "Use volume as IDE hard disk or CD-ROM (n is 0 to " .($MAX_IDE_DISKS -1) . ").",
1228 PVE
::JSONSchema
::register_standard_option
("pve-qm-ide", $idedesc);
1240 type
=> 'string', format
=> $scsi_fmt,
1241 description
=> "Use volume as SCSI hard disk or CD-ROM (n is 0 to " . ($MAX_SCSI_DISKS - 1) . ").",
1243 PVE
::JSONSchema
::register_standard_option
("pve-qm-scsi", $scsidesc);
1252 type
=> 'string', format
=> $sata_fmt,
1253 description
=> "Use volume as SATA hard disk or CD-ROM (n is 0 to " . ($MAX_SATA_DISKS - 1). ").",
1255 PVE
::JSONSchema
::register_standard_option
("pve-qm-sata", $satadesc);
1263 type
=> 'string', format
=> $virtio_fmt,
1264 description
=> "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . ").",
1266 PVE
::JSONSchema
::register_standard_option
("pve-qm-virtio", $virtiodesc);
1268 my $alldrive_fmt = {
1279 volume
=> { alias
=> 'file' },
1282 format
=> 'pve-volume-id-or-qm-path',
1284 format_description
=> 'volume',
1285 description
=> "The drive's backing volume.",
1287 format
=> get_standard_option
('pve-qm-image-format'),
1290 format
=> 'disk-size',
1291 format_description
=> 'DiskSize',
1292 description
=> "Disk size. This is purely informational and has no effect.",
1297 my $efidisk_desc = {
1299 type
=> 'string', format
=> $efidisk_fmt,
1300 description
=> "Configure a Disk for storing EFI vars",
1303 PVE
::JSONSchema
::register_standard_option
("pve-qm-efidisk", $efidisk_desc);
1308 type
=> 'string', format
=> 'pve-qm-usb-device',
1309 format_description
=> 'HOSTUSBDEVICE|spice',
1310 description
=> <<EODESCR,
1311 The Host USB device or port or the value 'spice'. HOSTUSBDEVICE syntax is:
1313 'bus-port(.port)*' (decimal numbers) or
1314 'vendor_id:product_id' (hexadeciaml numbers) or
1317 You can use the 'lsusb -t' command to list existing usb devices.
1319 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
1321 The value 'spice' can be used to add a usb redirection devices for spice.
1327 description
=> "Specifies whether if given host option is a USB3 device or port.",
1334 type
=> 'string', format
=> $usb_fmt,
1335 description
=> "Configure an USB device (n is 0 to 4).",
1337 PVE
::JSONSchema
::register_standard_option
("pve-qm-usb", $usbdesc);
1339 my $PCIRE = qr/[a-f0-9]{2}:[a-f0-9]{2}(?:\.[a-f0-9])?/;
1344 pattern
=> qr/$PCIRE(;$PCIRE)*/,
1345 format_description
=> 'HOSTPCIID[;HOSTPCIID2...]',
1346 description
=> <<EODESCR,
1347 Host PCI device pass through. The PCI ID of a host's PCI device or a list
1348 of PCI virtual functions of the host. HOSTPCIID syntax is:
1350 'bus:dev.func' (hexadecimal numbers)
1352 You can us the 'lspci' command to list existing PCI devices.
1357 description
=> "Specify whether or not the device's ROM will be visible in the guest's memory map.",
1363 pattern
=> '[^,;]+',
1364 format_description
=> 'string',
1365 description
=> "Custom pci device rom filename (must be located in /usr/share/kvm/).",
1370 description
=> "Choose the PCI-express bus (needs the 'q35' machine model).",
1376 description
=> "Enable vfio-vga device support.",
1382 format_description
=> 'string',
1383 pattern
=> '[^/\.:]+',
1385 description
=> <<EODESCR
1386 The type of mediated device to use.
1387 An instance of this type will be created on startup of the VM and
1388 will be cleaned up when the VM stops.
1392 PVE
::JSONSchema
::register_format
('pve-qm-hostpci', $hostpci_fmt);
1396 type
=> 'string', format
=> 'pve-qm-hostpci',
1397 description
=> "Map host PCI devices into guest.",
1398 verbose_description
=> <<EODESCR,
1399 Map host PCI devices into guest.
1401 NOTE: This option allows direct access to host hardware. So it is no longer
1402 possible to migrate such machines - use with special care.
1404 CAUTION: Experimental! User reported problems with this option.
1407 PVE
::JSONSchema
::register_standard_option
("pve-qm-hostpci", $hostpcidesc);
1412 pattern
=> '(/dev/.+|socket)',
1413 description
=> "Create a serial device inside the VM (n is 0 to 3)",
1414 verbose_description
=> <<EODESCR,
1415 Create a serial device inside the VM (n is 0 to 3), and pass through a
1416 host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1417 host side (use 'qm terminal' to open a terminal connection).
1419 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines - use with special care.
1421 CAUTION: Experimental! User reported problems with this option.
1428 pattern
=> '/dev/parport\d+|/dev/usb/lp\d+',
1429 description
=> "Map host parallel devices (n is 0 to 2).",
1430 verbose_description
=> <<EODESCR,
1431 Map host parallel devices (n is 0 to 2).
1433 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
1435 CAUTION: Experimental! User reported problems with this option.
1439 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1440 $confdesc->{"parallel$i"} = $paralleldesc;
1443 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1444 $confdesc->{"serial$i"} = $serialdesc;
1447 for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
1448 $confdesc->{"hostpci$i"} = $hostpcidesc;
1451 for (my $i = 0; $i < $MAX_IDE_DISKS; $i++) {
1452 $drivename_hash->{"ide$i"} = 1;
1453 $confdesc->{"ide$i"} = $idedesc;
1456 for (my $i = 0; $i < $MAX_SATA_DISKS; $i++) {
1457 $drivename_hash->{"sata$i"} = 1;
1458 $confdesc->{"sata$i"} = $satadesc;
1461 for (my $i = 0; $i < $MAX_SCSI_DISKS; $i++) {
1462 $drivename_hash->{"scsi$i"} = 1;
1463 $confdesc->{"scsi$i"} = $scsidesc ;
1466 for (my $i = 0; $i < $MAX_VIRTIO_DISKS; $i++) {
1467 $drivename_hash->{"virtio$i"} = 1;
1468 $confdesc->{"virtio$i"} = $virtiodesc;
1471 $drivename_hash->{efidisk0
} = 1;
1472 $confdesc->{efidisk0
} = $efidisk_desc;
1474 for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
1475 $confdesc->{"usb$i"} = $usbdesc;
1480 type
=> 'string', format
=> 'pve-volume-id',
1481 description
=> "Reference to unused volumes. This is used internally, and should not be modified manually.",
1484 for (my $i = 0; $i < $MAX_UNUSED_DISKS; $i++) {
1485 $confdesc->{"unused$i"} = $unuseddesc;
1488 my $kvm_api_version = 0;
1491 return $kvm_api_version if $kvm_api_version;
1493 open my $fh, '<', '/dev/kvm'
1496 # 0xae00 => KVM_GET_API_VERSION
1497 $kvm_api_version = ioctl($fh, 0xae00, 0);
1499 return $kvm_api_version;
1502 my $kvm_user_version = {};
1505 sub kvm_user_version
{
1508 $binary //= get_command_for_arch
(get_host_arch
()); # get the native arch by default
1509 my $st = stat($binary);
1511 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1512 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1513 $cachedmtime == $st->mtime;
1515 $kvm_user_version->{$binary} = 'unknown';
1516 $kvm_mtime->{$binary} = $st->mtime;
1520 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1521 $kvm_user_version->{$binary} = $2;
1525 eval { run_command
([$binary, '--version'], outfunc
=> $code); };
1528 return $kvm_user_version->{$binary};
1532 sub kernel_has_vhost_net
{
1533 return -c
'/dev/vhost-net';
1536 sub valid_drive_names
{
1537 # order is important - used to autoselect boot disk
1538 return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))),
1539 (map { "scsi$_" } (0 .. ($MAX_SCSI_DISKS - 1))),
1540 (map { "virtio$_" } (0 .. ($MAX_VIRTIO_DISKS - 1))),
1541 (map { "sata$_" } (0 .. ($MAX_SATA_DISKS - 1))),
1545 sub is_valid_drivename
{
1548 return defined($drivename_hash->{$dev});
1553 return defined($confdesc->{$key});
1557 sub get_cdrom_path
{
1559 return $cdrom_path if $cdrom_path;
1561 return $cdrom_path = "/dev/cdrom" if -l
"/dev/cdrom";
1562 return $cdrom_path = "/dev/cdrom1" if -l
"/dev/cdrom1";
1563 return $cdrom_path = "/dev/cdrom2" if -l
"/dev/cdrom2";
1567 my ($storecfg, $vmid, $cdrom) = @_;
1569 if ($cdrom eq 'cdrom') {
1570 return get_cdrom_path
();
1571 } elsif ($cdrom eq 'none') {
1573 } elsif ($cdrom =~ m
|^/|) {
1576 return PVE
::Storage
::path
($storecfg, $cdrom);
1580 # try to convert old style file names to volume IDs
1581 sub filename_to_volume_id
{
1582 my ($vmid, $file, $media) = @_;
1584 if (!($file eq 'none' || $file eq 'cdrom' ||
1585 $file =~ m
|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1587 return undef if $file =~ m
|/|;
1589 if ($media && $media eq 'cdrom') {
1590 $file = "local:iso/$file";
1592 $file = "local:$vmid/$file";
1599 sub verify_media_type
{
1600 my ($opt, $vtype, $media) = @_;
1605 if ($media eq 'disk') {
1607 } elsif ($media eq 'cdrom') {
1610 die "internal error";
1613 return if ($vtype eq $etype);
1615 raise_param_exc
({ $opt => "unexpected media type ($vtype != $etype)" });
1618 sub cleanup_drive_path
{
1619 my ($opt, $storecfg, $drive) = @_;
1621 # try to convert filesystem paths to volume IDs
1623 if (($drive->{file
} !~ m/^(cdrom|none)$/) &&
1624 ($drive->{file
} !~ m
|^/dev/.+|) &&
1625 ($drive->{file
} !~ m/^([^:]+):(.+)$/) &&
1626 ($drive->{file
} !~ m/^\d+$/)) {
1627 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($storecfg, $drive->{file
});
1628 raise_param_exc
({ $opt => "unable to associate path '$drive->{file}' to any storage"}) if !$vtype;
1629 $drive->{media
} = 'cdrom' if !$drive->{media
} && $vtype eq 'iso';
1630 verify_media_type
($opt, $vtype, $drive->{media
});
1631 $drive->{file
} = $volid;
1634 $drive->{media
} = 'cdrom' if !$drive->{media
} && $drive->{file
} =~ m/^(cdrom|none)$/;
1637 sub parse_hotplug_features
{
1642 return $res if $data eq '0';
1644 $data = $confdesc->{hotplug
}->{default} if $data eq '1';
1646 foreach my $feature (PVE
::Tools
::split_list
($data)) {
1647 if ($feature =~ m/^(network|disk|cpu|memory|usb)$/) {
1650 die "invalid hotplug feature '$feature'\n";
1656 PVE
::JSONSchema
::register_format
('pve-hotplug-features', \
&pve_verify_hotplug_features
);
1657 sub pve_verify_hotplug_features
{
1658 my ($value, $noerr) = @_;
1660 return $value if parse_hotplug_features
($value);
1662 return undef if $noerr;
1664 die "unable to parse hotplug option\n";
1667 # ideX = [volume=]volume-id[,media=d][,cyls=c,heads=h,secs=s[,trans=t]]
1668 # [,snapshot=on|off][,cache=on|off][,format=f][,backup=yes|no]
1669 # [,rerror=ignore|report|stop][,werror=enospc|ignore|report|stop]
1670 # [,aio=native|threads][,discard=ignore|on][,detect_zeroes=on|off]
1671 # [,iothread=on][,serial=serial][,model=model]
1674 my ($key, $data) = @_;
1676 my ($interface, $index);
1678 if ($key =~ m/^([^\d]+)(\d+)$/) {
1685 my $desc = $key =~ /^unused\d+$/ ?
$alldrive_fmt
1686 : $confdesc->{$key}->{format
};
1688 warn "invalid drive key: $key\n";
1691 my $res = eval { PVE
::JSONSchema
::parse_property_string
($desc, $data) };
1692 return undef if !$res;
1693 $res->{interface
} = $interface;
1694 $res->{index} = $index;
1697 foreach my $opt (qw(bps bps_rd bps_wr)) {
1698 if (my $bps = defined(delete $res->{$opt})) {
1699 if (defined($res->{"m$opt"})) {
1700 warn "both $opt and m$opt specified\n";
1704 $res->{"m$opt"} = sprintf("%.3f", $bps / (1024*1024.0));
1708 # can't use the schema's 'requires' because of the mbps* => bps* "transforming aliases"
1709 for my $requirement (
1710 [mbps_max
=> 'mbps'],
1711 [mbps_rd_max
=> 'mbps_rd'],
1712 [mbps_wr_max
=> 'mbps_wr'],
1713 [miops_max
=> 'miops'],
1714 [miops_rd_max
=> 'miops_rd'],
1715 [miops_wr_max
=> 'miops_wr'],
1716 [bps_max_length
=> 'mbps_max'],
1717 [bps_rd_max_length
=> 'mbps_rd_max'],
1718 [bps_wr_max_length
=> 'mbps_wr_max'],
1719 [iops_max_length
=> 'iops_max'],
1720 [iops_rd_max_length
=> 'iops_rd_max'],
1721 [iops_wr_max_length
=> 'iops_wr_max']) {
1722 my ($option, $requires) = @$requirement;
1723 if ($res->{$option} && !$res->{$requires}) {
1724 warn "$option requires $requires\n";
1729 return undef if $error;
1731 return undef if $res->{mbps_rd
} && $res->{mbps
};
1732 return undef if $res->{mbps_wr
} && $res->{mbps
};
1733 return undef if $res->{iops_rd
} && $res->{iops
};
1734 return undef if $res->{iops_wr
} && $res->{iops
};
1736 if ($res->{media
} && ($res->{media
} eq 'cdrom')) {
1737 return undef if $res->{snapshot
} || $res->{trans
} || $res->{format
};
1738 return undef if $res->{heads
} || $res->{secs
} || $res->{cyls
};
1739 return undef if $res->{interface
} eq 'virtio';
1742 if (my $size = $res->{size
}) {
1743 return undef if !defined($res->{size
} = PVE
::JSONSchema
::parse_size
($size));
1750 my ($vmid, $drive) = @_;
1751 my $data = { %$drive };
1752 delete $data->{$_} for qw(index interface);
1753 return PVE
::JSONSchema
::print_property_string
($data, $alldrive_fmt);
1757 my($fh, $noerr) = @_;
1760 my $SG_GET_VERSION_NUM = 0x2282;
1762 my $versionbuf = "\x00" x
8;
1763 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1765 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
1768 my $version = unpack("I", $versionbuf);
1769 if ($version < 30000) {
1770 die "scsi generic interface too old\n" if !$noerr;
1774 my $buf = "\x00" x
36;
1775 my $sensebuf = "\x00" x
8;
1776 my $cmd = pack("C x3 C x1", 0x12, 36);
1778 # see /usr/include/scsi/sg.h
1779 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";
1781 my $packet = pack($sg_io_hdr_t, ord('S'), -3, length($cmd),
1782 length($sensebuf), 0, length($buf), $buf,
1783 $cmd, $sensebuf, 6000);
1785 $ret = ioctl($fh, $SG_IO, $packet);
1787 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
1791 my @res = unpack($sg_io_hdr_t, $packet);
1792 if ($res[17] || $res[18]) {
1793 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
1798 (my $byte0, my $byte1, $res->{vendor
},
1799 $res->{product
}, $res->{revision
}) = unpack("C C x6 A8 A16 A4", $buf);
1801 $res->{removable
} = $byte1 & 128 ?
1 : 0;
1802 $res->{type
} = $byte0 & 31;
1810 my $fh = IO
::File-
>new("+<$path") || return undef;
1811 my $res = scsi_inquiry
($fh, 1);
1817 sub machine_type_is_q35
{
1820 return $conf->{machine
} && ($conf->{machine
} =~ m/q35/) ?
1 : 0;
1823 sub print_tabletdevice_full
{
1824 my ($conf, $arch) = @_;
1826 my $q35 = machine_type_is_q35
($conf);
1828 # we use uhci for old VMs because tablet driver was buggy in older qemu
1830 if (machine_type_is_q35
($conf) || $arch eq 'aarch64') {
1836 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1839 sub print_keyboarddevice_full
{
1840 my ($conf, $arch, $machine) = @_;
1842 return undef if $arch ne 'aarch64';
1844 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1847 sub print_drivedevice_full
{
1848 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1853 if ($drive->{interface
} eq 'virtio') {
1854 my $pciaddr = print_pci_addr
("$drive->{interface}$drive->{index}", $bridges, $arch, $machine_type);
1855 $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
1856 $device .= ",iothread=iothread-$drive->{interface}$drive->{index}" if $drive->{iothread
};
1857 } elsif ($drive->{interface
} eq 'scsi') {
1859 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
1860 my $unit = $drive->{index} % $maxdev;
1861 my $devicetype = 'hd';
1863 if (drive_is_cdrom
($drive)) {
1866 if ($drive->{file
} =~ m
|^/|) {
1867 $path = $drive->{file
};
1868 if (my $info = path_is_scsi
($path)) {
1869 if ($info->{type
} == 0 && $drive->{scsiblock
}) {
1870 $devicetype = 'block';
1871 } elsif ($info->{type
} == 1) { # tape
1872 $devicetype = 'generic';
1876 $path = PVE
::Storage
::path
($storecfg, $drive->{file
});
1879 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1880 if ($path =~ m/^iscsi\:\/\
// &&
1881 !qemu_machine_feature_enabled
($machine_type, undef, 4, 1)) {
1882 $devicetype = 'generic';
1886 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)){
1887 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
1889 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0,lun=$drive->{index},drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
1892 if ($drive->{ssd
} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1893 $device .= ",rotation_rate=1";
1895 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1897 } elsif ($drive->{interface
} eq 'ide' || $drive->{interface
} eq 'sata') {
1898 my $maxdev = ($drive->{interface
} eq 'sata') ?
$MAX_SATA_DISKS : 2;
1899 my $controller = int($drive->{index} / $maxdev);
1900 my $unit = $drive->{index} % $maxdev;
1901 my $devicetype = ($drive->{media
} && $drive->{media
} eq 'cdrom') ?
"cd" : "hd";
1903 $device = "ide-$devicetype";
1904 if ($drive->{interface
} eq 'ide') {
1905 $device .= ",bus=ide.$controller,unit=$unit";
1907 $device .= ",bus=ahci$controller.$unit";
1909 $device .= ",drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
1911 if ($devicetype eq 'hd') {
1912 if (my $model = $drive->{model
}) {
1913 $model = URI
::Escape
::uri_unescape
($model);
1914 $device .= ",model=$model";
1916 if ($drive->{ssd
}) {
1917 $device .= ",rotation_rate=1";
1920 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1921 } elsif ($drive->{interface
} eq 'usb') {
1923 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1925 die "unsupported interface type";
1928 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex
};
1930 if (my $serial = $drive->{serial
}) {
1931 $serial = URI
::Escape
::uri_unescape
($serial);
1932 $device .= ",serial=$serial";
1939 sub get_initiator_name
{
1942 my $fh = IO
::File-
>new('/etc/iscsi/initiatorname.iscsi') || return undef;
1943 while (defined(my $line = <$fh>)) {
1944 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1953 sub print_drive_full
{
1954 my ($storecfg, $vmid, $drive) = @_;
1957 my $volid = $drive->{file
};
1960 if (drive_is_cdrom
($drive)) {
1961 $path = get_iso_path
($storecfg, $vmid, $volid);
1963 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1965 $path = PVE
::Storage
::path
($storecfg, $volid);
1966 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
1967 $format = qemu_img_format
($scfg, $volname);
1975 my @qemu_drive_options = qw(heads secs cyls trans media format cache rerror werror aio discard);
1976 foreach my $o (@qemu_drive_options) {
1977 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1980 # snapshot only accepts on|off
1981 if (defined($drive->{snapshot
})) {
1982 my $v = $drive->{snapshot
} ?
'on' : 'off';
1983 $opts .= ",snapshot=$v";
1986 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1987 my ($dir, $qmpname) = @$type;
1988 if (my $v = $drive->{"mbps$dir"}) {
1989 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1991 if (my $v = $drive->{"mbps${dir}_max"}) {
1992 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1994 if (my $v = $drive->{"bps${dir}_max_length"}) {
1995 $opts .= ",throttling.bps$qmpname-max-length=$v";
1997 if (my $v = $drive->{"iops${dir}"}) {
1998 $opts .= ",throttling.iops$qmpname=$v";
2000 if (my $v = $drive->{"iops${dir}_max"}) {
2001 $opts .= ",throttling.iops$qmpname-max=$v";
2003 if (my $v = $drive->{"iops${dir}_max_length"}) {
2004 $opts .= ",throttling.iops$qmpname-max-length=$v";
2008 $opts .= ",format=$format" if $format && !$drive->{format
};
2010 my $cache_direct = 0;
2012 if (my $cache = $drive->{cache
}) {
2013 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
2014 } elsif (!drive_is_cdrom
($drive)) {
2015 $opts .= ",cache=none";
2019 # aio native works only with O_DIRECT
2020 if (!$drive->{aio
}) {
2022 $opts .= ",aio=native";
2024 $opts .= ",aio=threads";
2028 if (!drive_is_cdrom
($drive)) {
2030 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
2031 $detectzeroes = 'off';
2032 } elsif ($drive->{discard
}) {
2033 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
2035 # This used to be our default with discard not being specified:
2036 $detectzeroes = 'on';
2038 $opts .= ",detect-zeroes=$detectzeroes" if $detectzeroes;
2041 my $pathinfo = $path ?
"file=$path," : '';
2043 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
2046 sub print_netdevice_full
{
2047 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type) = @_;
2049 my $bootorder = $conf->{boot
} || $confdesc->{boot
}->{default};
2051 my $device = $net->{model
};
2052 if ($net->{model
} eq 'virtio') {
2053 $device = 'virtio-net-pci';
2056 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
2057 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
2058 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
2059 #Consider we have N queues, the number of vectors needed is 2*N + 2 (plus one config interrupt and control vq)
2060 my $vectors = $net->{queues
} * 2 + 2;
2061 $tmpstr .= ",vectors=$vectors,mq=on";
2063 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
2065 if ($use_old_bios_files) {
2067 if ($device eq 'virtio-net-pci') {
2068 $romfile = 'pxe-virtio.rom';
2069 } elsif ($device eq 'e1000') {
2070 $romfile = 'pxe-e1000.rom';
2071 } elsif ($device eq 'ne2k') {
2072 $romfile = 'pxe-ne2k_pci.rom';
2073 } elsif ($device eq 'pcnet') {
2074 $romfile = 'pxe-pcnet.rom';
2075 } elsif ($device eq 'rtl8139') {
2076 $romfile = 'pxe-rtl8139.rom';
2078 $tmpstr .= ",romfile=$romfile" if $romfile;
2084 sub print_netdev_full
{
2085 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
2088 if ($netid =~ m/^net(\d+)$/) {
2092 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
2094 my $ifname = "tap${vmid}i$i";
2096 # kvm uses TUNSETIFF ioctl, and that limits ifname length
2097 die "interface name '$ifname' is too long (max 15 character)\n"
2098 if length($ifname) >= 16;
2100 my $vhostparam = '';
2101 if (is_native
($arch)) {
2102 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
2105 my $vmname = $conf->{name
} || "vm$vmid";
2108 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
2110 if ($net->{bridge
}) {
2111 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script,downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
2113 $netdev = "type=user,id=$netid,hostname=$vmname";
2116 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
2122 sub print_cpu_device
{
2123 my ($conf, $id) = @_;
2125 my $kvm = $conf->{kvm
} // 1;
2126 my $cpu = $kvm ?
"kvm64" : "qemu64";
2127 if (my $cputype = $conf->{cpu
}) {
2128 my $cpuconf = PVE
::JSONSchema
::parse_property_string
($cpu_fmt, $cputype)
2129 or die "Cannot parse cpu description: $cputype\n";
2130 $cpu = $cpuconf->{cputype
};
2133 my $cores = $conf->{cores
} || 1;
2135 my $current_core = ($id - 1) % $cores;
2136 my $current_socket = int(($id - 1 - $current_core)/$cores);
2138 return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0";
2142 'cirrus' => 'cirrus-vga',
2144 'vmware' => 'vmware-svga',
2145 'virtio' => 'virtio-vga',
2148 sub print_vga_device
{
2149 my ($conf, $vga, $arch, $kvmver, $machine, $id, $qxlnum, $bridges) = @_;
2151 my $type = $vga_map->{$vga->{type
}};
2152 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
2153 $type = 'virtio-gpu';
2155 my $vgamem_mb = $vga->{memory
};
2157 my $max_outputs = '';
2159 $type = $id ?
'qxl' : 'qxl-vga';
2161 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
2162 # set max outputs so linux can have up to 4 qxl displays with one device
2163 if (qemu_machine_feature_enabled
($machine, $kvmver, 4, 1)) {
2164 $max_outputs = ",max_outputs=4";
2169 die "no devicetype for $vga->{type}\n" if !$type;
2173 if ($vga->{type
} eq 'virtio') {
2174 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
2175 $memory = ",max_hostmem=$bytes";
2177 # from https://www.spice-space.org/multiple-monitors.html
2178 $memory = ",vgamem_mb=$vga->{memory}";
2179 my $ram = $vgamem_mb * 4;
2180 my $vram = $vgamem_mb * 2;
2181 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
2183 $memory = ",vgamem_mb=$vga->{memory}";
2185 } elsif ($qxlnum && $id) {
2186 $memory = ",ram_size=67108864,vram_size=33554432";
2189 my $q35 = machine_type_is_q35
($conf);
2190 my $vgaid = "vga" . ($id // '');
2193 if ($q35 && $vgaid eq 'vga') {
2194 # the first display uses pcie.0 bus on q35 machines
2195 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
2197 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
2200 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}";
2203 sub drive_is_cloudinit
{
2205 return $drive->{file
} =~ m
@[:/]vm-\d
+-cloudinit
(?
:\
.$QEMU_FORMAT_RE)?
$@;
2208 sub drive_is_cdrom
{
2209 my ($drive, $exclude_cloudinit) = @_;
2211 return 0 if $exclude_cloudinit && drive_is_cloudinit
($drive);
2213 return $drive && $drive->{media
} && ($drive->{media
} eq 'cdrom');
2217 sub parse_number_sets
{
2220 foreach my $part (split(/;/, $set)) {
2221 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
2222 die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1;
2223 push @$res, [ $1, $2 ];
2225 die "invalid range: $part\n";
2234 my $res = PVE
::JSONSchema
::parse_property_string
($numa_fmt, $data);
2235 $res->{cpus
} = parse_number_sets
($res->{cpus
}) if defined($res->{cpus
});
2236 $res->{hostnodes
} = parse_number_sets
($res->{hostnodes
}) if defined($res->{hostnodes
});
2243 return undef if !$value;
2245 my $res = PVE
::JSONSchema
::parse_property_string
($hostpci_fmt, $value);
2247 my @idlist = split(/;/, $res->{host
});
2248 delete $res->{host
};
2249 foreach my $id (@idlist) {
2250 if ($id =~ m/\./) { # full id 00:00.1
2251 push @{$res->{pciid
}}, {
2254 } else { # partial id 00:00
2255 $res->{pciid
} = PVE
::SysFSTools
::lspci
($id);
2261 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
2265 my $res = eval { PVE
::JSONSchema
::parse_property_string
($net_fmt, $data) };
2270 if (!defined($res->{macaddr
})) {
2271 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
2272 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
2277 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
2278 sub parse_ipconfig
{
2281 my $res = eval { PVE
::JSONSchema
::parse_property_string
($ipconfig_fmt, $data) };
2287 if ($res->{gw
} && !$res->{ip
}) {
2288 warn 'gateway specified without specifying an IP address';
2291 if ($res->{gw6
} && !$res->{ip6
}) {
2292 warn 'IPv6 gateway specified without specifying an IPv6 address';
2295 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
2296 warn 'gateway specified together with DHCP';
2299 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
2301 warn "IPv6 gateway specified together with $res->{ip6} address";
2305 if (!$res->{ip
} && !$res->{ip6
}) {
2306 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
2315 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
2318 sub add_random_macs
{
2319 my ($settings) = @_;
2321 foreach my $opt (keys %$settings) {
2322 next if $opt !~ m/^net(\d+)$/;
2323 my $net = parse_net
($settings->{$opt});
2325 $settings->{$opt} = print_net
($net);
2329 sub vm_is_volid_owner
{
2330 my ($storecfg, $vmid, $volid) = @_;
2332 if ($volid !~ m
|^/|) {
2334 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
2335 if ($owner && ($owner == $vmid)) {
2343 sub vmconfig_register_unused_drive
{
2344 my ($storecfg, $vmid, $conf, $drive) = @_;
2346 if (drive_is_cloudinit
($drive)) {
2347 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
2349 } elsif (!drive_is_cdrom
($drive)) {
2350 my $volid = $drive->{file
};
2351 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
2352 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
2357 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
2361 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
2362 format_description
=> 'UUID',
2363 description
=> "Set SMBIOS1 UUID.",
2368 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2369 format_description
=> 'Base64 encoded string',
2370 description
=> "Set SMBIOS1 version.",
2375 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2376 format_description
=> 'Base64 encoded string',
2377 description
=> "Set SMBIOS1 serial number.",
2382 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2383 format_description
=> 'Base64 encoded string',
2384 description
=> "Set SMBIOS1 manufacturer.",
2389 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2390 format_description
=> 'Base64 encoded string',
2391 description
=> "Set SMBIOS1 product ID.",
2396 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2397 format_description
=> 'Base64 encoded string',
2398 description
=> "Set SMBIOS1 SKU string.",
2403 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2404 format_description
=> 'Base64 encoded string',
2405 description
=> "Set SMBIOS1 family string.",
2410 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
2418 my $res = eval { PVE
::JSONSchema
::parse_property_string
($smbios1_fmt, $data) };
2425 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2428 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2430 PVE
::JSONSchema
::register_format
('pve-qm-bootdisk', \
&verify_bootdisk
);
2431 sub verify_bootdisk
{
2432 my ($value, $noerr) = @_;
2434 return $value if is_valid_drivename
($value);
2436 return undef if $noerr;
2438 die "invalid boot disk '$value'\n";
2441 sub parse_watchdog
{
2444 return undef if !$value;
2446 my $res = eval { PVE
::JSONSchema
::parse_property_string
($watchdog_fmt, $value) };
2451 sub parse_guest_agent
{
2454 return {} if !defined($value->{agent
});
2456 my $res = eval { PVE
::JSONSchema
::parse_property_string
($agent_fmt, $value->{agent
}) };
2459 # if the agent is disabled ignore the other potentially set properties
2460 return {} if !$res->{enabled
};
2467 return {} if !$value;
2468 my $res = eval { PVE
::JSONSchema
::parse_property_string
($vga_fmt, $value) };
2473 PVE
::JSONSchema
::register_format
('pve-qm-usb-device', \
&verify_usb_device
);
2474 sub verify_usb_device
{
2475 my ($value, $noerr) = @_;
2477 return $value if parse_usb_device
($value);
2479 return undef if $noerr;
2481 die "unable to parse usb device\n";
2484 # add JSON properties for create and set function
2485 sub json_config_properties
{
2488 foreach my $opt (keys %$confdesc) {
2489 next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'vmstate' || $opt eq 'runningmachine';
2490 $prop->{$opt} = $confdesc->{$opt};
2496 # return copy of $confdesc_cloudinit to generate documentation
2497 sub cloudinit_config_properties
{
2499 return dclone
($confdesc_cloudinit);
2503 my ($key, $value) = @_;
2505 die "unknown setting '$key'\n" if !$confdesc->{$key};
2507 my $type = $confdesc->{$key}->{type
};
2509 if (!defined($value)) {
2510 die "got undefined value\n";
2513 if ($value =~ m/[\n\r]/) {
2514 die "property contains a line feed\n";
2517 if ($type eq 'boolean') {
2518 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2519 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2520 die "type check ('boolean') failed - got '$value'\n";
2521 } elsif ($type eq 'integer') {
2522 return int($1) if $value =~ m/^(\d+)$/;
2523 die "type check ('integer') failed - got '$value'\n";
2524 } elsif ($type eq 'number') {
2525 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2526 die "type check ('number') failed - got '$value'\n";
2527 } elsif ($type eq 'string') {
2528 if (my $fmt = $confdesc->{$key}->{format
}) {
2529 PVE
::JSONSchema
::check_format
($fmt, $value);
2532 $value =~ s/^\"(.*)\"$/$1/;
2535 die "internal error"
2540 my ($storecfg, $vmid, $skiplock, $replacement_conf) = @_;
2542 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2544 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
2546 if ($conf->{template
}) {
2547 # check if any base image is still used by a linked clone
2548 foreach_drive
($conf, sub {
2549 my ($ds, $drive) = @_;
2550 return if drive_is_cdrom
($drive);
2552 my $volid = $drive->{file
};
2553 return if !$volid || $volid =~ m
|^/|;
2555 die "base volume '$volid' is still in use by linked cloned\n"
2556 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2561 # only remove disks owned by this VM
2562 foreach_drive
($conf, sub {
2563 my ($ds, $drive) = @_;
2564 return if drive_is_cdrom
($drive, 1);
2566 my $volid = $drive->{file
};
2567 return if !$volid || $volid =~ m
|^/|;
2569 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2570 return if !$path || !$owner || ($owner != $vmid);
2572 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2573 warn "Could not remove disk '$volid', check manually: $@" if $@;
2576 # also remove unused disk
2577 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid);
2578 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2579 my ($volid, $sid, $volname, $d) = @_;
2580 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2584 if (defined $replacement_conf) {
2585 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2587 PVE
::QemuConfig-
>destroy_config($vmid);
2591 sub parse_vm_config
{
2592 my ($filename, $raw) = @_;
2594 return undef if !defined($raw);
2597 digest
=> Digest
::SHA
::sha1_hex
($raw),
2602 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2603 || die "got strange filename '$filename'";
2611 my @lines = split(/\n/, $raw);
2612 foreach my $line (@lines) {
2613 next if $line =~ m/^\s*$/;
2615 if ($line =~ m/^\[PENDING\]\s*$/i) {
2616 $section = 'pending';
2617 if (defined($descr)) {
2619 $conf->{description
} = $descr;
2622 $conf = $res->{$section} = {};
2625 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2627 if (defined($descr)) {
2629 $conf->{description
} = $descr;
2632 $conf = $res->{snapshots
}->{$section} = {};
2636 if ($line =~ m/^\#(.*)\s*$/) {
2637 $descr = '' if !defined($descr);
2638 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2642 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2643 $descr = '' if !defined($descr);
2644 $descr .= PVE
::Tools
::decode_text
($2);
2645 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2646 $conf->{snapstate
} = $1;
2647 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2650 $conf->{$key} = $value;
2651 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2653 if ($section eq 'pending') {
2654 $conf->{delete} = $value; # we parse this later
2656 warn "vm $vmid - propertry 'delete' is only allowed in [PENDING]\n";
2658 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2661 eval { $value = check_type
($key, $value); };
2663 warn "vm $vmid - unable to parse value of '$key' - $@";
2665 $key = 'ide2' if $key eq 'cdrom';
2666 my $fmt = $confdesc->{$key}->{format
};
2667 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2668 my $v = parse_drive
($key, $value);
2669 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2670 $v->{file
} = $volid;
2671 $value = print_drive
($vmid, $v);
2673 warn "vm $vmid - unable to parse value of '$key'\n";
2678 $conf->{$key} = $value;
2683 if (defined($descr)) {
2685 $conf->{description
} = $descr;
2687 delete $res->{snapstate
}; # just to be sure
2692 sub write_vm_config
{
2693 my ($filename, $conf) = @_;
2695 delete $conf->{snapstate
}; # just to be sure
2697 if ($conf->{cdrom
}) {
2698 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2699 $conf->{ide2
} = $conf->{cdrom
};
2700 delete $conf->{cdrom
};
2703 # we do not use 'smp' any longer
2704 if ($conf->{sockets
}) {
2705 delete $conf->{smp
};
2706 } elsif ($conf->{smp
}) {
2707 $conf->{sockets
} = $conf->{smp
};
2708 delete $conf->{cores
};
2709 delete $conf->{smp
};
2712 my $used_volids = {};
2714 my $cleanup_config = sub {
2715 my ($cref, $pending, $snapname) = @_;
2717 foreach my $key (keys %$cref) {
2718 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2719 $key eq 'snapstate' || $key eq 'pending';
2720 my $value = $cref->{$key};
2721 if ($key eq 'delete') {
2722 die "propertry 'delete' is only allowed in [PENDING]\n"
2724 # fixme: check syntax?
2727 eval { $value = check_type
($key, $value); };
2728 die "unable to parse value of '$key' - $@" if $@;
2730 $cref->{$key} = $value;
2732 if (!$snapname && is_valid_drivename
($key)) {
2733 my $drive = parse_drive
($key, $value);
2734 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2739 &$cleanup_config($conf);
2741 &$cleanup_config($conf->{pending
}, 1);
2743 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2744 die "internal error" if $snapname eq 'pending';
2745 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2748 # remove 'unusedX' settings if we re-add a volume
2749 foreach my $key (keys %$conf) {
2750 my $value = $conf->{$key};
2751 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2752 delete $conf->{$key};
2756 my $generate_raw_config = sub {
2757 my ($conf, $pending) = @_;
2761 # add description as comment to top of file
2762 if (defined(my $descr = $conf->{description
})) {
2764 foreach my $cl (split(/\n/, $descr)) {
2765 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2768 $raw .= "#\n" if $pending;
2772 foreach my $key (sort keys %$conf) {
2773 next if $key eq 'digest' || $key eq 'description' || $key eq 'pending' || $key eq 'snapshots';
2774 $raw .= "$key: $conf->{$key}\n";
2779 my $raw = &$generate_raw_config($conf);
2781 if (scalar(keys %{$conf->{pending
}})){
2782 $raw .= "\n[PENDING]\n";
2783 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2786 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2787 $raw .= "\n[$snapname]\n";
2788 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2798 # we use static defaults from our JSON schema configuration
2799 foreach my $key (keys %$confdesc) {
2800 if (defined(my $default = $confdesc->{$key}->{default})) {
2801 $res->{$key} = $default;
2809 my $vmlist = PVE
::Cluster
::get_vmlist
();
2811 return $res if !$vmlist || !$vmlist->{ids
};
2812 my $ids = $vmlist->{ids
};
2814 foreach my $vmid (keys %$ids) {
2815 my $d = $ids->{$vmid};
2816 next if !$d->{node
} || $d->{node
} ne $nodename;
2817 next if !$d->{type
} || $d->{type
} ne 'qemu';
2818 $res->{$vmid}->{exists} = 1;
2823 # test if VM uses local resources (to prevent migration)
2824 sub check_local_resources
{
2825 my ($conf, $noerr) = @_;
2829 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2830 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2832 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2834 foreach my $k (keys %$conf) {
2835 next if $k =~ m/^usb/ && ($conf->{$k} =~ m/^spice(?![^,])/);
2836 # sockets are safe: they will recreated be on the target side post-migrate
2837 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2838 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2841 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2846 # check if used storages are available on all nodes (use by migrate)
2847 sub check_storage_availability
{
2848 my ($storecfg, $conf, $node) = @_;
2850 foreach_drive
($conf, sub {
2851 my ($ds, $drive) = @_;
2853 my $volid = $drive->{file
};
2856 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2859 # check if storage is available on both nodes
2860 my $scfg = PVE
::Storage
::storage_check_node
($storecfg, $sid);
2861 PVE
::Storage
::storage_check_node
($storecfg, $sid, $node);
2865 # list nodes where all VM images are available (used by has_feature API)
2867 my ($conf, $storecfg) = @_;
2869 my $nodelist = PVE
::Cluster
::get_nodelist
();
2870 my $nodehash = { map { $_ => 1 } @$nodelist };
2871 my $nodename = PVE
::INotify
::nodename
();
2873 foreach_drive
($conf, sub {
2874 my ($ds, $drive) = @_;
2876 my $volid = $drive->{file
};
2879 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2881 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2882 if ($scfg->{disable
}) {
2884 } elsif (my $avail = $scfg->{nodes
}) {
2885 foreach my $node (keys %$nodehash) {
2886 delete $nodehash->{$node} if !$avail->{$node};
2888 } elsif (!$scfg->{shared
}) {
2889 foreach my $node (keys %$nodehash) {
2890 delete $nodehash->{$node} if $node ne $nodename
2899 sub check_local_storage_availability
{
2900 my ($conf, $storecfg) = @_;
2902 my $nodelist = PVE
::Cluster
::get_nodelist
();
2903 my $nodehash = { map { $_ => {} } @$nodelist };
2905 foreach_drive
($conf, sub {
2906 my ($ds, $drive) = @_;
2908 my $volid = $drive->{file
};
2911 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2913 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2915 if ($scfg->{disable
}) {
2916 foreach my $node (keys %$nodehash) {
2917 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2919 } elsif (my $avail = $scfg->{nodes
}) {
2920 foreach my $node (keys %$nodehash) {
2921 if (!$avail->{$node}) {
2922 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2929 foreach my $node (values %$nodehash) {
2930 if (my $unavail = $node->{unavailable_storages
}) {
2931 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2938 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2940 my ($vmid, $nocheck, $node) = @_;
2942 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2943 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2948 my $vzlist = config_list
();
2950 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2952 while (defined(my $de = $fd->read)) {
2953 next if $de !~ m/^(\d+)\.pid$/;
2955 next if !defined($vzlist->{$vmid});
2956 if (my $pid = check_running
($vmid)) {
2957 $vzlist->{$vmid}->{pid
} = $pid;
2965 my ($storecfg, $conf) = @_;
2967 my $bootdisk = $conf->{bootdisk
};
2968 return undef if !$bootdisk;
2969 return undef if !is_valid_drivename
($bootdisk);
2971 return undef if !$conf->{$bootdisk};
2973 my $drive = parse_drive
($bootdisk, $conf->{$bootdisk});
2974 return undef if !defined($drive);
2976 return undef if drive_is_cdrom
($drive);
2978 my $volid = $drive->{file
};
2979 return undef if !$volid;
2981 return $drive->{size
};
2984 our $vmstatus_return_properties = {
2985 vmid
=> get_standard_option
('pve-vmid'),
2987 description
=> "Qemu process status.",
2989 enum
=> ['stopped', 'running'],
2992 description
=> "Maximum memory in bytes.",
2995 renderer
=> 'bytes',
2998 description
=> "Root disk size in bytes.",
3001 renderer
=> 'bytes',
3004 description
=> "VM name.",
3009 description
=> "Qemu QMP agent status.",
3014 description
=> "PID of running qemu process.",
3019 description
=> "Uptime.",
3022 renderer
=> 'duration',
3025 description
=> "Maximum usable CPUs.",
3030 description
=> "The current config lock, if any.",
3036 my $last_proc_pid_stat;
3038 # get VM status information
3039 # This must be fast and should not block ($full == false)
3040 # We only query KVM using QMP if $full == true (this can be slow)
3042 my ($opt_vmid, $full) = @_;
3046 my $storecfg = PVE
::Storage
::config
();
3048 my $list = vzlist
();
3049 my $defaults = load_defaults
();
3051 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
(1);
3053 my $cpucount = $cpuinfo->{cpus
} || 1;
3055 foreach my $vmid (keys %$list) {
3056 next if $opt_vmid && ($vmid ne $opt_vmid);
3058 my $conf = PVE
::QemuConfig-
>load_config($vmid);
3060 my $d = { vmid
=> $vmid };
3061 $d->{pid
} = $list->{$vmid}->{pid
};
3063 # fixme: better status?
3064 $d->{status
} = $list->{$vmid}->{pid
} ?
'running' : 'stopped';
3066 my $size = disksize
($storecfg, $conf);
3067 if (defined($size)) {
3068 $d->{disk
} = 0; # no info available
3069 $d->{maxdisk
} = $size;
3075 $d->{cpus
} = ($conf->{sockets
} || $defaults->{sockets
})
3076 * ($conf->{cores
} || $defaults->{cores
});
3077 $d->{cpus
} = $cpucount if $d->{cpus
} > $cpucount;
3078 $d->{cpus
} = $conf->{vcpus
} if $conf->{vcpus
};
3080 $d->{name
} = $conf->{name
} || "VM $vmid";
3081 $d->{maxmem
} = $conf->{memory
} ?
$conf->{memory
}*(1024*1024)
3082 : $defaults->{memory
}*(1024*1024);
3084 if ($conf->{balloon
}) {
3085 $d->{balloon_min
} = $conf->{balloon
}*(1024*1024);
3086 $d->{shares
} = defined($conf->{shares
}) ?
$conf->{shares
}
3087 : $defaults->{shares
};
3098 $d->{diskwrite
} = 0;
3100 $d->{template
} = PVE
::QemuConfig-
>is_template($conf);
3102 $d->{serial
} = 1 if conf_has_serial
($conf);
3103 $d->{lock} = $conf->{lock} if $conf->{lock};
3108 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
3109 foreach my $dev (keys %$netdev) {
3110 next if $dev !~ m/^tap([1-9]\d*)i/;
3112 my $d = $res->{$vmid};
3115 $d->{netout
} += $netdev->{$dev}->{receive
};
3116 $d->{netin
} += $netdev->{$dev}->{transmit
};
3119 $d->{nics
}->{$dev}->{netout
} = $netdev->{$dev}->{receive
};
3120 $d->{nics
}->{$dev}->{netin
} = $netdev->{$dev}->{transmit
};
3125 my $ctime = gettimeofday
;
3127 foreach my $vmid (keys %$list) {
3129 my $d = $res->{$vmid};
3130 my $pid = $d->{pid
};
3133 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
3134 next if !$pstat; # not running
3136 my $used = $pstat->{utime} + $pstat->{stime
};
3138 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
3140 if ($pstat->{vsize
}) {
3141 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
3144 my $old = $last_proc_pid_stat->{$pid};
3146 $last_proc_pid_stat->{$pid} = {
3154 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
3156 if ($dtime > 1000) {
3157 my $dutime = $used - $old->{used
};
3159 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
3160 $last_proc_pid_stat->{$pid} = {
3166 $d->{cpu
} = $old->{cpu
};
3170 return $res if !$full;
3172 my $qmpclient = PVE
::QMPClient-
>new();
3174 my $ballooncb = sub {
3175 my ($vmid, $resp) = @_;
3177 my $info = $resp->{'return'};
3178 return if !$info->{max_mem
};
3180 my $d = $res->{$vmid};
3182 # use memory assigned to VM
3183 $d->{maxmem
} = $info->{max_mem
};
3184 $d->{balloon
} = $info->{actual
};
3186 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
3187 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
3188 $d->{freemem
} = $info->{free_mem
};
3191 $d->{ballooninfo
} = $info;
3194 my $blockstatscb = sub {
3195 my ($vmid, $resp) = @_;
3196 my $data = $resp->{'return'} || [];
3197 my $totalrdbytes = 0;
3198 my $totalwrbytes = 0;
3200 for my $blockstat (@$data) {
3201 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
3202 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
3204 $blockstat->{device
} =~ s/drive-//;
3205 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
3207 $res->{$vmid}->{diskread
} = $totalrdbytes;
3208 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
3211 my $statuscb = sub {
3212 my ($vmid, $resp) = @_;
3214 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
3215 # this fails if ballon driver is not loaded, so this must be
3216 # the last commnand (following command are aborted if this fails).
3217 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
3219 my $status = 'unknown';
3220 if (!defined($status = $resp->{'return'}->{status
})) {
3221 warn "unable to get VM status\n";
3225 $res->{$vmid}->{qmpstatus
} = $resp->{'return'}->{status
};
3228 foreach my $vmid (keys %$list) {
3229 next if $opt_vmid && ($vmid ne $opt_vmid);
3230 next if !$res->{$vmid}->{pid
}; # not running
3231 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
3234 $qmpclient->queue_execute(undef, 2);
3236 foreach my $vmid (keys %$list) {
3237 next if $opt_vmid && ($vmid ne $opt_vmid);
3238 $res->{$vmid}->{qmpstatus
} = $res->{$vmid}->{status
} if !$res->{$vmid}->{qmpstatus
};
3245 my ($conf, $func, @param) = @_;
3247 foreach my $ds (valid_drive_names
()) {
3248 next if !defined($conf->{$ds});
3250 my $drive = parse_drive
($ds, $conf->{$ds});
3253 &$func($ds, $drive, @param);
3258 my ($conf, $func, @param) = @_;
3262 my $test_volid = sub {
3263 my ($volid, $is_cdrom, $replicate, $shared, $snapname, $size) = @_;
3267 $volhash->{$volid}->{cdrom
} //= 1;
3268 $volhash->{$volid}->{cdrom
} = 0 if !$is_cdrom;
3270 $volhash->{$volid}->{replicate
} //= 0;
3271 $volhash->{$volid}->{replicate
} = 1 if $replicate;
3273 $volhash->{$volid}->{shared
} //= 0;
3274 $volhash->{$volid}->{shared
} = 1 if $shared;
3276 $volhash->{$volid}->{referenced_in_config
} //= 0;
3277 $volhash->{$volid}->{referenced_in_config
} = 1 if !defined($snapname);
3279 $volhash->{$volid}->{referenced_in_snapshot
}->{$snapname} = 1
3280 if defined($snapname);
3281 $volhash->{$volid}->{size
} = $size if $size;
3284 foreach_drive
($conf, sub {
3285 my ($ds, $drive) = @_;
3286 $test_volid->($drive->{file
}, drive_is_cdrom
($drive), $drive->{replicate
} // 1, $drive->{shared
}, undef, $drive->{size
});
3289 foreach my $snapname (keys %{$conf->{snapshots
}}) {
3290 my $snap = $conf->{snapshots
}->{$snapname};
3291 $test_volid->($snap->{vmstate
}, 0, 1, $snapname);
3292 foreach_drive
($snap, sub {
3293 my ($ds, $drive) = @_;
3294 $test_volid->($drive->{file
}, drive_is_cdrom
($drive), $drive->{replicate
} // 1, $drive->{shared
}, $snapname);
3298 foreach my $volid (keys %$volhash) {
3299 &$func($volid, $volhash->{$volid}, @param);
3303 sub conf_has_serial
{
3306 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3307 if ($conf->{"serial$i"}) {
3315 sub conf_has_audio
{
3316 my ($conf, $id) = @_;
3319 my $audio = $conf->{"audio$id"};
3320 return undef if !defined($audio);
3322 my $audioproperties = PVE
::JSONSchema
::parse_property_string
($audio_fmt, $audio);
3323 my $audiodriver = $audioproperties->{driver
} // 'spice';
3326 dev
=> $audioproperties->{device
},
3327 dev_id
=> "audiodev$id",
3328 backend
=> $audiodriver,
3329 backend_id
=> "$audiodriver-backend${id}",
3333 sub vga_conf_has_spice
{
3336 my $vgaconf = parse_vga
($vga);
3337 my $vgatype = $vgaconf->{type
};
3338 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
3345 return get_host_arch
() eq $arch;
3348 my $default_machines = {
3353 sub get_basic_machine_info
{
3354 my ($conf, $forcemachine) = @_;
3356 my $arch = $conf->{arch
} // get_host_arch
();
3357 my $machine = $forcemachine || $conf->{machine
} || $default_machines->{$arch};
3358 return ($arch, $machine);
3361 sub get_ovmf_files
($) {
3364 my $ovmf = $OVMF->{$arch}
3365 or die "no OVMF images known for architecture '$arch'\n";
3371 aarch64
=> '/usr/bin/qemu-system-aarch64',
3372 x86_64
=> '/usr/bin/qemu-system-x86_64',
3374 sub get_command_for_arch
($) {
3376 return '/usr/bin/kvm' if is_native
($arch);
3378 my $cmd = $Arch2Qemu->{$arch}
3379 or die "don't know how to emulate architecture '$arch'\n";
3383 sub get_cpu_options
{
3384 my ($conf, $arch, $kvm, $machine_type, $kvm_off, $kvmver, $winversion, $gpu_passthrough) = @_;
3387 my $ostype = $conf->{ostype
};
3389 my $cpu = $kvm ?
"kvm64" : "qemu64";
3390 if ($arch eq 'aarch64') {
3391 $cpu = 'cortex-a57';
3394 if (my $cputype = $conf->{cpu
}) {
3395 my $cpuconf = PVE
::JSONSchema
::parse_property_string
($cpu_fmt, $cputype)
3396 or die "Cannot parse cpu description: $cputype\n";
3397 $cpu = $cpuconf->{cputype
};
3398 $kvm_off = 1 if $cpuconf->{hidden
};
3399 $hv_vendor_id = $cpuconf->{'hv-vendor-id'};
3401 if (defined(my $flags = $cpuconf->{flags
})) {
3402 push @$cpuFlags, split(";", $flags);
3406 push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64' && $arch eq 'x86_64';
3408 push @$cpuFlags , '-x2apic' if $ostype && $ostype eq 'solaris';
3410 push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
3412 push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
3414 if (qemu_machine_feature_enabled
($machine_type, $kvmver, 2, 3) && $arch eq 'x86_64') {
3416 push @$cpuFlags , '+kvm_pv_unhalt' if $kvm;
3417 push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
3420 add_hyperv_enlightenments
($cpuFlags, $winversion, $machine_type, $kvmver, $conf->{bios
}, $gpu_passthrough, $hv_vendor_id) if $kvm;
3422 push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm && $arch eq 'x86_64';
3424 push @$cpuFlags, 'kvm=off' if $kvm_off;
3426 if (my $cpu_vendor = $cpu_vendor_list->{$cpu}) {
3427 push @$cpuFlags, "vendor=${cpu_vendor}"
3428 if $cpu_vendor ne 'default';
3429 } elsif ($arch ne 'aarch64') {
3430 die "internal error"; # should not happen
3433 $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
3435 return ('-cpu', $cpu);
3438 sub config_to_command
{
3439 my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_;
3442 my $globalFlags = [];
3443 my $machineFlags = [];
3448 my $vernum = 0; # unknown
3449 my $ostype = $conf->{ostype
};
3450 my $winversion = windows_version
($ostype);
3451 my $kvm = $conf->{kvm
};
3453 my ($arch, $machine_type) = get_basic_machine_info
($conf, $forcemachine);
3454 my $kvm_binary = get_command_for_arch
($arch);
3455 my $kvmver = kvm_user_version
($kvm_binary);
3456 $kvm //= 1 if is_native
($arch);
3459 die "KVM virtualisation configured, but not available. Either disable in VM configuration or enable in BIOS.\n"
3460 if !defined kvm_version
();
3463 if ($kvmver =~ m/^(\d+)\.(\d+)$/) {
3464 $vernum = $1*1000000+$2*1000;
3465 } elsif ($kvmver =~ m/^(\d+)\.(\d+)\.(\d+)$/) {
3466 $vernum = $1*1000000+$2*1000+$3;
3469 die "detected old qemu-kvm binary ($kvmver)\n" if $vernum < 15000;
3471 my $q35 = machine_type_is_q35
($conf);
3472 my $hotplug_features = parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
3473 my $use_old_bios_files = undef;
3474 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3476 my $cpuunits = defined($conf->{cpuunits
}) ?
3477 $conf->{cpuunits
} : $defaults->{cpuunits
};
3479 push @$cmd, $kvm_binary;
3481 push @$cmd, '-id', $vmid;
3483 my $vmname = $conf->{name
} || "vm$vmid";
3485 push @$cmd, '-name', $vmname;
3489 my $qmpsocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid);
3490 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait";
3491 push @$cmd, '-mon', "chardev=qmp,mode=control";
3493 if (qemu_machine_feature_enabled
($machine_type, $kvmver, 2, 12)) {
3494 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3495 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3498 push @$cmd, '-pidfile' , PVE
::QemuServer
::Helpers
::pidfile_name
($vmid);
3500 push @$cmd, '-daemonize';
3502 if ($conf->{smbios1
}) {
3503 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
3504 if ($smbios_conf->{base64
}) {
3505 # Do not pass base64 flag to qemu
3506 delete $smbios_conf->{base64
};
3507 my $smbios_string = "";
3508 foreach my $key (keys %$smbios_conf) {
3510 if ($key eq "uuid") {
3511 $value = $smbios_conf->{uuid
}
3513 $value = decode_base64
($smbios_conf->{$key});
3515 # qemu accepts any binary data, only commas need escaping by double comma
3517 $smbios_string .= "," . $key . "=" . $value if $value;
3519 push @$cmd, '-smbios', "type=1" . $smbios_string;
3521 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3525 if ($conf->{vmgenid
}) {
3526 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid
};
3529 my ($ovmf_code, $ovmf_vars) = get_ovmf_files
($arch);
3530 if ($conf->{bios
} && $conf->{bios
} eq 'ovmf') {
3531 die "uefi base image not found\n" if ! -f
$ovmf_code;
3535 if (my $efidisk = $conf->{efidisk0
}) {
3536 my $d = PVE
::JSONSchema
::parse_property_string
($efidisk_fmt, $efidisk);
3537 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($d->{file
}, 1);
3538 $format = $d->{format
};
3540 $path = PVE
::Storage
::path
($storecfg, $d->{file
});
3541 if (!defined($format)) {
3542 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
3543 $format = qemu_img_format
($scfg, $volname);
3547 die "efidisk format must be specified\n"
3548 if !defined($format);
3551 warn "no efidisk configured! Using temporary efivars disk.\n";
3552 $path = "/tmp/$vmid-ovmf.fd";
3553 PVE
::Tools
::file_copy
($ovmf_vars, $path, -s
$ovmf_vars);
3557 push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$ovmf_code";
3558 push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0,file=$path";
3563 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3564 if (qemu_machine_feature_enabled
($machine_type, $kvmver, 4, 0)) {
3565 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3567 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3571 # add usb controllers
3572 my @usbcontrollers = PVE
::QemuServer
::USB
::get_usb_controllers
($conf, $bridges, $arch, $machine_type, $usbdesc->{format
}, $MAX_USB_DEVICES);
3573 push @$devices, @usbcontrollers if @usbcontrollers;
3574 my $vga = parse_vga
($conf->{vga
});
3576 my $qxlnum = vga_conf_has_spice
($conf->{vga
});
3577 $vga->{type
} = 'qxl' if $qxlnum;
3579 if (!$vga->{type
}) {
3580 if ($arch eq 'aarch64') {
3581 $vga->{type
} = 'virtio';
3582 } elsif (qemu_machine_feature_enabled
($machine_type, $kvmver, 2, 9)) {
3583 $vga->{type
} = (!$winversion || $winversion >= 6) ?
'std' : 'cirrus';
3585 $vga->{type
} = ($winversion >= 6) ?
'std' : 'cirrus';
3589 # enable absolute mouse coordinates (needed by vnc)
3591 if (defined($conf->{tablet
})) {
3592 $tablet = $conf->{tablet
};
3594 $tablet = $defaults->{tablet
};
3595 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3596 $tablet = 0 if $vga->{type
} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3600 push @$devices, '-device', print_tabletdevice_full
($conf, $arch) if $tablet;
3601 my $kbd = print_keyboarddevice_full
($conf, $arch);
3602 push @$devices, '-device', $kbd if defined($kbd);
3606 my $gpu_passthrough;
3609 for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
3610 my $id = "hostpci$i";
3611 my $d = parse_hostpci
($conf->{$id});
3614 if (my $pcie = $d->{pcie
}) {
3615 die "q35 machine model is not enabled" if !$q35;
3616 # win7 wants to have the pcie devices directly on the pcie bus
3617 # instead of in the root port
3618 if ($winversion == 7) {
3619 $pciaddr = print_pcie_addr
("${id}bus0");
3621 # add more root ports if needed, 4 are present by default
3622 # by pve-q35 cfgs, rest added here on demand.
3624 push @$devices, '-device', print_pcie_root_port
($i);
3626 $pciaddr = print_pcie_addr
($id);
3629 $pciaddr = print_pci_addr
($id, $bridges, $arch, $machine_type);
3633 if ($d->{'x-vga'}) {
3634 $xvga = ',x-vga=on' if !($conf->{bios
} && $conf->{bios
} eq 'ovmf');
3636 $vga->{type
} = 'none' if !defined($conf->{vga
});
3637 $gpu_passthrough = 1;
3640 my $pcidevices = $d->{pciid
};
3641 my $multifunction = 1 if @$pcidevices > 1;
3644 if ($d->{mdev
} && scalar(@$pcidevices) == 1) {
3645 my $pci_id = $pcidevices->[0]->{id
};
3646 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $i);
3647 $sysfspath = "/sys/bus/pci/devices/0000:$pci_id/$uuid";
3648 } elsif ($d->{mdev
}) {
3649 warn "ignoring mediated device '$id' with multifunction device\n";
3653 foreach my $pcidevice (@$pcidevices) {
3654 my $devicestr = "vfio-pci";
3657 $devicestr .= ",sysfsdev=$sysfspath";
3659 $devicestr .= ",host=$pcidevice->{id}";
3662 my $mf_addr = $multifunction ?
".$j" : '';
3663 $devicestr .= ",id=${id}${mf_addr}${pciaddr}${mf_addr}";
3666 $devicestr .= ',rombar=0' if defined($d->{rombar
}) && !$d->{rombar
};
3667 $devicestr .= "$xvga";
3668 $devicestr .= ",multifunction=on" if $multifunction;
3669 $devicestr .= ",romfile=/usr/share/kvm/$d->{romfile}" if $d->{romfile
};
3672 push @$devices, '-device', $devicestr;
3678 my $usb_dev_features = {};
3679 $usb_dev_features->{spice_usb3
} = 1 if qemu_machine_feature_enabled
($machine_type, $kvmver, 4, 0);
3681 my @usbdevices = PVE
::QemuServer
::USB
::get_usb_devices
($conf, $usbdesc->{format
}, $MAX_USB_DEVICES, $usb_dev_features);
3682 push @$devices, @usbdevices if @usbdevices;
3684 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3685 if (my $path = $conf->{"serial$i"}) {
3686 if ($path eq 'socket') {
3687 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3688 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server,nowait";
3689 # On aarch64, serial0 is the UART device. Qemu only allows
3690 # connecting UART devices via the '-serial' command line, as
3691 # the device has a fixed slot on the hardware...
3692 if ($arch eq 'aarch64' && $i == 0) {
3693 push @$devices, '-serial', "chardev:serial$i";
3695 push @$devices, '-device', "isa-serial,chardev=serial$i";
3698 die "no such serial device\n" if ! -c
$path;
3699 push @$devices, '-chardev', "tty,id=serial$i,path=$path";
3700 push @$devices, '-device', "isa-serial,chardev=serial$i";
3706 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3707 if (my $path = $conf->{"parallel$i"}) {
3708 die "no such parallel device\n" if ! -c
$path;
3709 my $devtype = $path =~ m!^/dev/usb/lp! ?
'tty' : 'parport';
3710 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3711 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3715 if (my $audio = conf_has_audio
($conf)) {
3717 my $audiopciaddr = print_pci_addr
("audio0", $bridges, $arch, $machine_type);
3719 my $id = $audio->{dev_id
};
3720 if ($audio->{dev
} eq 'AC97') {
3721 push @$devices, '-device', "AC97,id=${id}${audiopciaddr}";
3722 } elsif ($audio->{dev
} =~ /intel\-hda$/) {
3723 push @$devices, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
3724 push @$devices, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0";
3725 push @$devices, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1";
3727 die "unkown audio device '$audio->{dev}', implement me!";
3730 push @$devices, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
3734 $sockets = $conf->{smp
} if $conf->{smp
}; # old style - no longer iused
3735 $sockets = $conf->{sockets
} if $conf->{sockets
};
3737 my $cores = $conf->{cores
} || 1;
3739 my $maxcpus = $sockets * $cores;
3741 my $vcpus = $conf->{vcpus
} ?
$conf->{vcpus
} : $maxcpus;
3743 my $allowed_vcpus = $cpuinfo->{cpus
};
3745 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n"
3746 if ($allowed_vcpus < $maxcpus);
3748 if($hotplug_features->{cpu
} && qemu_machine_feature_enabled
($machine_type, $kvmver, 2, 7)) {
3750 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3751 for (my $i = 2; $i <= $vcpus; $i++) {
3752 my $cpustr = print_cpu_device
($conf,$i);
3753 push @$cmd, '-device', $cpustr;
3758 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3760 push @$cmd, '-nodefaults';
3762 my $bootorder = $conf->{boot
} || $confdesc->{boot
}->{default};
3764 my $bootindex_hash = {};
3766 foreach my $o (split(//, $bootorder)) {
3767 $bootindex_hash->{$o} = $i*100;
3771 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3773 push @$cmd, '-no-acpi' if defined($conf->{acpi
}) && $conf->{acpi
} == 0;
3775 push @$cmd, '-no-reboot' if defined($conf->{reboot
}) && $conf->{reboot
} == 0;
3777 if ($vga->{type
} && $vga->{type
} !~ m/^serial\d+$/ && $vga->{type
} ne 'none'){
3778 push @$devices, '-device', print_vga_device
($conf, $vga, $arch, $kvmver, $machine_type, undef, $qxlnum, $bridges);
3779 my $socket = PVE
::QemuServer
::Helpers
::vnc_socket
($vmid);
3780 push @$cmd, '-vnc', "unix:$socket,password";
3782 push @$cmd, '-vga', 'none' if $vga->{type
} eq 'none';
3783 push @$cmd, '-nographic';
3787 my $tdf = defined($conf->{tdf
}) ?
$conf->{tdf
} : $defaults->{tdf
};
3789 my $useLocaltime = $conf->{localtime};
3791 if ($winversion >= 5) { # windows
3792 $useLocaltime = 1 if !defined($conf->{localtime});
3794 # use time drift fix when acpi is enabled
3795 if (!(defined($conf->{acpi
}) && $conf->{acpi
} == 0)) {
3796 $tdf = 1 if !defined($conf->{tdf
});
3800 if ($winversion >= 6) {
3801 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3802 push @$cmd, '-no-hpet';
3805 push @$rtcFlags, 'driftfix=slew' if $tdf;
3808 push @$machineFlags, 'accel=tcg';
3811 if ($machine_type) {
3812 push @$machineFlags, "type=${machine_type}";
3815 if (($conf->{startdate
}) && ($conf->{startdate
} ne 'now')) {
3816 push @$rtcFlags, "base=$conf->{startdate}";
3817 } elsif ($useLocaltime) {
3818 push @$rtcFlags, 'base=localtime';
3821 push @$cmd, get_cpu_options
($conf, $arch, $kvm, $machine_type, $kvm_off, $kvmver, $winversion, $gpu_passthrough);
3823 PVE
::QemuServer
::Memory
::config
($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
3825 push @$cmd, '-S' if $conf->{freeze
};
3827 push @$cmd, '-k', $conf->{keyboard
} if defined($conf->{keyboard
});
3829 my $guest_agent = parse_guest_agent
($conf);
3831 if ($guest_agent->{enabled
}) {
3832 my $qgasocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid, 1);
3833 push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0";
3835 if (!$guest_agent->{type
} || $guest_agent->{type
} eq 'virtio') {
3836 my $pciaddr = print_pci_addr
("qga0", $bridges, $arch, $machine_type);
3837 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3838 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3839 } elsif ($guest_agent->{type
} eq 'isa') {
3840 push @$devices, '-device', "isa-serial,chardev=qga0";
3849 for(my $i = 1; $i < $qxlnum; $i++){
3850 push @$devices, '-device', print_vga_device
($conf, $vga, $arch, $kvmver, $machine_type, $i, $qxlnum, $bridges);
3853 # assume other OS works like Linux
3854 my ($ram, $vram) = ("134217728", "67108864");
3855 if ($vga->{memory
}) {
3856 $ram = PVE
::Tools
::convert_size
($qxlnum*4*$vga->{memory
}, 'mb' => 'b');
3857 $vram = PVE
::Tools
::convert_size
($qxlnum*2*$vga->{memory
}, 'mb' => 'b');
3859 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3860 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3864 my $pciaddr = print_pci_addr
("spice", $bridges, $arch, $machine_type);
3866 my $nodename = PVE
::INotify
::nodename
();
3867 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
3868 my @nodeaddrs = PVE
::Tools
::getaddrinfo_all
('localhost', family
=> $pfamily);
3869 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3871 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3872 push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
3873 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3875 my $localhost = PVE
::Network
::addr_to_ip
($nodeaddrs[0]->{addr
});
3876 $spice_port = PVE
::Tools
::next_spice_port
($pfamily, $localhost);
3878 my $spice_enhancement = PVE
::JSONSchema
::parse_property_string
($spice_enhancements_fmt, $conf->{spice_enhancements
} // '');
3879 if ($spice_enhancement->{foldersharing
}) {
3880 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3881 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3884 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3885 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}" if $spice_enhancement->{videostreaming
};
3886 push @$devices, '-spice', "$spice_opts";
3889 # enable balloon by default, unless explicitly disabled
3890 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
3891 $pciaddr = print_pci_addr
("balloon0", $bridges, $arch, $machine_type);
3892 push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr";
3895 if ($conf->{watchdog
}) {
3896 my $wdopts = parse_watchdog
($conf->{watchdog
});
3897 $pciaddr = print_pci_addr
("watchdog", $bridges, $arch, $machine_type);
3898 my $watchdog = $wdopts->{model
} || 'i6300esb';
3899 push @$devices, '-device', "$watchdog$pciaddr";
3900 push @$devices, '-watchdog-action', $wdopts->{action
} if $wdopts->{action
};
3904 my $scsicontroller = {};
3905 my $ahcicontroller = {};
3906 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : $defaults->{scsihw
};
3908 # Add iscsi initiator name if available
3909 if (my $initiator = get_initiator_name
()) {
3910 push @$devices, '-iscsi', "initiator-name=$initiator";
3913 foreach_drive
($conf, sub {
3914 my ($ds, $drive) = @_;
3916 if (PVE
::Storage
::parse_volume_id
($drive->{file
}, 1)) {
3917 push @$vollist, $drive->{file
};
3920 # ignore efidisk here, already added in bios/fw handling code above
3921 return if $drive->{interface
} eq 'efidisk';
3923 $use_virtio = 1 if $ds =~ m/^virtio/;
3925 if (drive_is_cdrom
($drive)) {
3926 if ($bootindex_hash->{d
}) {
3927 $drive->{bootindex
} = $bootindex_hash->{d
};
3928 $bootindex_hash->{d
} += 1;
3931 if ($bootindex_hash->{c
}) {
3932 $drive->{bootindex
} = $bootindex_hash->{c
} if $conf->{bootdisk
} && ($conf->{bootdisk
} eq $ds);
3933 $bootindex_hash->{c
} += 1;
3937 if($drive->{interface
} eq 'virtio'){
3938 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread
};
3941 if ($drive->{interface
} eq 'scsi') {
3943 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
3945 $pciaddr = print_pci_addr
("$controller_prefix$controller", $bridges, $arch, $machine_type);
3946 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ?
"virtio-scsi-pci" : $scsihw;
3949 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{iothread
}){
3950 $iothread .= ",iothread=iothread-$controller_prefix$controller";
3951 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
3952 } elsif ($drive->{iothread
}) {
3953 warn "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n";
3957 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{queues
}){
3958 $queues = ",num_queues=$drive->{queues}";
3961 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues" if !$scsicontroller->{$controller};
3962 $scsicontroller->{$controller}=1;
3965 if ($drive->{interface
} eq 'sata') {
3966 my $controller = int($drive->{index} / $MAX_SATA_DISKS);
3967 $pciaddr = print_pci_addr
("ahci$controller", $bridges, $arch, $machine_type);
3968 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
3969 $ahcicontroller->{$controller}=1;
3972 my $drive_cmd = print_drive_full
($storecfg, $vmid, $drive);
3973 push @$devices, '-drive',$drive_cmd;
3974 push @$devices, '-device', print_drivedevice_full
($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
3977 for (my $i = 0; $i < $MAX_NETS; $i++) {
3978 next if !$conf->{"net$i"};
3979 my $d = parse_net
($conf->{"net$i"});
3982 $use_virtio = 1 if $d->{model
} eq 'virtio';
3984 if ($bootindex_hash->{n
}) {
3985 $d->{bootindex
} = $bootindex_hash->{n
};
3986 $bootindex_hash->{n
} += 1;
3989 my $netdevfull = print_netdev_full
($vmid, $conf, $arch, $d, "net$i");
3990 push @$devices, '-netdev', $netdevfull;
3992 my $netdevicefull = print_netdevice_full
($vmid, $conf, $d, "net$i", $bridges, $use_old_bios_files, $arch, $machine_type);
3993 push @$devices, '-device', $netdevicefull;
3996 if ($conf->{ivshmem
}) {
3997 my $ivshmem = PVE
::JSONSchema
::parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
4001 $bus = print_pcie_addr
("ivshmem");
4003 $bus = print_pci_addr
("ivshmem", $bridges, $arch, $machine_type);
4006 my $ivshmem_name = $ivshmem->{name
} // $vmid;
4007 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
4009 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
4010 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path,size=$ivshmem->{size}M";
4015 if (qemu_machine_feature_enabled
($machine_type, $kvmver, 2, 3)) {
4020 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
4022 for my $k (sort {$b cmp $a} keys %$bridges) {
4023 $pciaddr = print_pci_addr
("pci.$k", undef, $arch, $machine_type);
4024 unshift @$devices, '-device', "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr" if $k > 0;
4028 push @$cmd, @$devices;
4029 push @$cmd, '-rtc', join(',', @$rtcFlags)
4030 if scalar(@$rtcFlags);
4031 push @$cmd, '-machine', join(',', @$machineFlags)
4032 if scalar(@$machineFlags);
4033 push @$cmd, '-global', join(',', @$globalFlags)
4034 if scalar(@$globalFlags);
4036 if (my $vmstate = $conf->{vmstate
}) {
4037 my $statepath = PVE
::Storage
::path
($storecfg, $vmstate);
4038 push @$vollist, $vmstate;
4039 push @$cmd, '-loadstate', $statepath;
4043 if ($conf->{args
}) {
4044 my $aa = PVE
::Tools
::split_args
($conf->{args
});
4048 return wantarray ?
($cmd, $vollist, $spice_port) : $cmd;
4054 my $res = mon_cmd
($vmid, 'query-spice');
4056 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
4059 sub vm_devices_list
{
4062 my $res = mon_cmd
($vmid, 'query-pci');
4063 my $devices_to_check = [];
4065 foreach my $pcibus (@$res) {
4066 push @$devices_to_check, @{$pcibus->{devices
}},
4069 while (@$devices_to_check) {
4071 for my $d (@$devices_to_check) {
4072 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
4073 next if !$d->{'pci_bridge'};
4075 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices
}});
4076 push @$to_check, @{$d->{'pci_bridge'}->{devices
}};
4078 $devices_to_check = $to_check;
4081 my $resblock = mon_cmd
($vmid, 'query-block');
4082 foreach my $block (@$resblock) {
4083 if($block->{device
} =~ m/^drive-(\S+)/){
4088 my $resmice = mon_cmd
($vmid, 'query-mice');
4089 foreach my $mice (@$resmice) {
4090 if ($mice->{name
} eq 'QEMU HID Tablet') {
4091 $devices->{tablet
} = 1;
4096 # for usb devices there is no query-usb
4097 # but we can iterate over the entries in
4098 # qom-list path=/machine/peripheral
4099 my $resperipheral = mon_cmd
($vmid, 'qom-list', path
=> '/machine/peripheral');
4100 foreach my $per (@$resperipheral) {
4101 if ($per->{name
} =~ m/^usb\d+$/) {
4102 $devices->{$per->{name
}} = 1;
4110 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4112 my $q35 = machine_type_is_q35
($conf);
4114 my $devices_list = vm_devices_list
($vmid);
4115 return 1 if defined($devices_list->{$deviceid});
4117 qemu_add_pci_bridge
($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type); # add PCI bridge if we need it for the device
4119 if ($deviceid eq 'tablet') {
4121 qemu_deviceadd
($vmid, print_tabletdevice_full
($conf, $arch));
4123 } elsif ($deviceid eq 'keyboard') {
4125 qemu_deviceadd
($vmid, print_keyboarddevice_full
($conf, $arch));
4127 } elsif ($deviceid =~ m/^usb(\d+)$/) {
4129 die "usb hotplug currently not reliable\n";
4130 # since we can't reliably hot unplug all added usb devices
4131 # and usb passthrough disables live migration
4132 # we disable usb hotplugging for now
4133 qemu_deviceadd
($vmid, PVE
::QemuServer
::USB
::print_usbdevice_full
($conf, $deviceid, $device));
4135 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4137 qemu_iothread_add
($vmid, $deviceid, $device);
4139 qemu_driveadd
($storecfg, $vmid, $device);
4140 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, $arch, $machine_type);
4142 qemu_deviceadd
($vmid, $devicefull);
4143 eval { qemu_deviceaddverify
($vmid, $deviceid); };
4145 eval { qemu_drivedel
($vmid, $deviceid); };
4150 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4153 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : "lsi";
4154 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4155 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ?
"virtio-scsi-pci" : $scsihw;
4157 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
4159 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread
}) {
4160 qemu_iothread_add
($vmid, $deviceid, $device);
4161 $devicefull .= ",iothread=iothread-$deviceid";
4164 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues
}) {
4165 $devicefull .= ",num_queues=$device->{queues}";
4168 qemu_deviceadd
($vmid, $devicefull);
4169 qemu_deviceaddverify
($vmid, $deviceid);
4171 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4173 qemu_findorcreatescsihw
($storecfg,$conf, $vmid, $device, $arch, $machine_type);
4174 qemu_driveadd
($storecfg, $vmid, $device);
4176 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, $arch, $machine_type);
4177 eval { qemu_deviceadd
($vmid, $devicefull); };
4179 eval { qemu_drivedel
($vmid, $deviceid); };
4184 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4186 return undef if !qemu_netdevadd
($vmid, $conf, $arch, $device, $deviceid);
4188 my $machine_type = PVE
::QemuServer
::qemu_machine_pxe
($vmid, $conf);
4189 my $use_old_bios_files = undef;
4190 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
4192 my $netdevicefull = print_netdevice_full
($vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type);
4193 qemu_deviceadd
($vmid, $netdevicefull);
4195 qemu_deviceaddverify
($vmid, $deviceid);
4196 qemu_set_link_status
($vmid, $deviceid, !$device->{link_down
});
4199 eval { qemu_netdevdel
($vmid, $deviceid); };
4204 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
4207 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4208 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
4210 qemu_deviceadd
($vmid, $devicefull);
4211 qemu_deviceaddverify
($vmid, $deviceid);
4214 die "can't hotplug device '$deviceid'\n";
4220 # fixme: this should raise exceptions on error!
4221 sub vm_deviceunplug
{
4222 my ($vmid, $conf, $deviceid) = @_;
4224 my $devices_list = vm_devices_list
($vmid);
4225 return 1 if !defined($devices_list->{$deviceid});
4227 die "can't unplug bootdisk" if $conf->{bootdisk
} && $conf->{bootdisk
} eq $deviceid;
4229 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard') {
4231 qemu_devicedel
($vmid, $deviceid);
4233 } elsif ($deviceid =~ m/^usb\d+$/) {
4235 die "usb hotplug currently not reliable\n";
4236 # when unplugging usb devices this way,
4237 # there may be remaining usb controllers/hubs
4238 # so we disable it for now
4239 qemu_devicedel
($vmid, $deviceid);
4240 qemu_devicedelverify
($vmid, $deviceid);
4242 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4244 qemu_devicedel
($vmid, $deviceid);
4245 qemu_devicedelverify
($vmid, $deviceid);
4246 qemu_drivedel
($vmid, $deviceid);
4247 qemu_iothread_del
($conf, $vmid, $deviceid);
4249 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4251 qemu_devicedel
($vmid, $deviceid);
4252 qemu_devicedelverify
($vmid, $deviceid);
4253 qemu_iothread_del
($conf, $vmid, $deviceid);
4255 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4257 qemu_devicedel
($vmid, $deviceid);
4258 qemu_drivedel
($vmid, $deviceid);
4259 qemu_deletescsihw
($conf, $vmid, $deviceid);
4261 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4263 qemu_devicedel
($vmid, $deviceid);
4264 qemu_devicedelverify
($vmid, $deviceid);
4265 qemu_netdevdel
($vmid, $deviceid);
4268 die "can't unplug device '$deviceid'\n";
4274 sub qemu_deviceadd
{
4275 my ($vmid, $devicefull) = @_;
4277 $devicefull = "driver=".$devicefull;
4278 my %options = split(/[=,]/, $devicefull);
4280 mon_cmd
($vmid, "device_add" , %options);
4283 sub qemu_devicedel
{
4284 my ($vmid, $deviceid) = @_;
4286 my $ret = mon_cmd
($vmid, "device_del", id
=> $deviceid);
4289 sub qemu_iothread_add
{
4290 my($vmid, $deviceid, $device) = @_;
4292 if ($device->{iothread
}) {
4293 my $iothreads = vm_iothreads_list
($vmid);
4294 qemu_objectadd
($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4298 sub qemu_iothread_del
{
4299 my($conf, $vmid, $deviceid) = @_;
4301 my $confid = $deviceid;
4302 if ($deviceid =~ m/^(?:virtioscsi|scsihw)(\d+)$/) {
4303 $confid = 'scsi' . $1;
4305 my $device = parse_drive
($confid, $conf->{$confid});
4306 if ($device->{iothread
}) {
4307 my $iothreads = vm_iothreads_list
($vmid);
4308 qemu_objectdel
($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4312 sub qemu_objectadd
{
4313 my($vmid, $objectid, $qomtype) = @_;
4315 mon_cmd
($vmid, "object-add", id
=> $objectid, "qom-type" => $qomtype);
4320 sub qemu_objectdel
{
4321 my($vmid, $objectid) = @_;
4323 mon_cmd
($vmid, "object-del", id
=> $objectid);
4329 my ($storecfg, $vmid, $device) = @_;
4331 my $drive = print_drive_full
($storecfg, $vmid, $device);
4332 $drive =~ s/\\/\\\\/g;
4333 my $ret = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "drive_add auto \"$drive\"");
4335 # If the command succeeds qemu prints: "OK
"
4336 return 1 if $ret =~ m/OK/s;
4338 die "adding drive failed
: $ret\n";
4342 my($vmid, $deviceid) = @_;
4344 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-
$deviceid");
4347 return 1 if $ret eq "";
4349 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4350 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4352 die "deleting drive
$deviceid failed
: $ret\n";
4355 sub qemu_deviceaddverify {
4356 my ($vmid, $deviceid) = @_;
4358 for (my $i = 0; $i <= 5; $i++) {
4359 my $devices_list = vm_devices_list($vmid);
4360 return 1 if defined($devices_list->{$deviceid});
4364 die "error on hotplug device
'$deviceid'\n";
4368 sub qemu_devicedelverify {
4369 my ($vmid, $deviceid) = @_;
4371 # need to verify that the device is correctly removed as device_del
4372 # is async and empty return is not reliable
4374 for (my $i = 0; $i <= 5; $i++) {
4375 my $devices_list = vm_devices_list($vmid);
4376 return 1 if !defined($devices_list->{$deviceid});
4380 die "error on hot-unplugging device
'$deviceid'\n";
4383 sub qemu_findorcreatescsihw {
4384 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4386 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4388 my $scsihwid="$controller_prefix$controller";
4389 my $devices_list = vm_devices_list($vmid);
4391 if(!defined($devices_list->{$scsihwid})) {
4392 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4398 sub qemu_deletescsihw {
4399 my ($conf, $vmid, $opt) = @_;
4401 my $device = parse_drive($opt, $conf->{$opt});
4403 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4404 vm_deviceunplug($vmid, $conf, "virtioscsi
$device->{index}");
4408 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4410 my $devices_list = vm_devices_list($vmid);
4411 foreach my $opt (keys %{$devices_list}) {
4412 if (PVE::QemuServer::is_valid_drivename($opt)) {
4413 my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt});
4414 if($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4420 my $scsihwid="scsihw
$controller";
4422 vm_deviceunplug($vmid, $conf, $scsihwid);
4427 sub qemu_add_pci_bridge {
4428 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4434 print_pci_addr($device, $bridges, $arch, $machine_type);
4436 while (my ($k, $v) = each %$bridges) {
4439 return 1 if !defined($bridgeid) || $bridgeid < 1;
4441 my $bridge = "pci
.$bridgeid";
4442 my $devices_list = vm_devices_list($vmid);
4444 if (!defined($devices_list->{$bridge})) {
4445 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4451 sub qemu_set_link_status {
4452 my ($vmid, $device, $up) = @_;
4454 mon_cmd($vmid, "set_link
", name => $device,
4455 up => $up ? JSON::true : JSON::false);
4458 sub qemu_netdevadd {
4459 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4461 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4462 my %options = split(/[=,]/, $netdev);
4464 mon_cmd($vmid, "netdev_add
", %options);
4468 sub qemu_netdevdel {
4469 my ($vmid, $deviceid) = @_;
4471 mon_cmd($vmid, "netdev_del
", id => $deviceid);
4474 sub qemu_usb_hotplug {
4475 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4479 # remove the old one first
4480 vm_deviceunplug($vmid, $conf, $deviceid);
4482 # check if xhci controller is necessary and available
4483 if ($device->{usb3}) {
4485 my $devicelist = vm_devices_list($vmid);
4487 if (!$devicelist->{xhci}) {
4488 my $pciaddr = print_pci_addr("xhci
", undef, $arch, $machine_type);
4489 qemu_deviceadd($vmid, "nec-usb-xhci
,id
=xhci
$pciaddr");
4492 my $d = parse_usb_device($device->{host});
4493 $d->{usb3} = $device->{usb3};
4496 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d, $arch, $machine_type);
4499 sub qemu_cpu_hotplug {
4500 my ($vmid, $conf, $vcpus) = @_;
4502 my $machine_type = PVE::QemuServer::get_current_qemu_machine($vmid);
4505 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4506 $sockets = $conf->{sockets} if $conf->{sockets};
4507 my $cores = $conf->{cores} || 1;
4508 my $maxcpus = $sockets * $cores;
4510 $vcpus = $maxcpus if !$vcpus;
4512 die "you can
't add more vcpus than maxcpus\n"
4513 if $vcpus > $maxcpus;
4515 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4517 if ($vcpus < $currentvcpus) {
4519 if (qemu_machine_feature_enabled ($machine_type, undef, 2, 7)) {
4521 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4522 qemu_devicedel($vmid, "cpu$i");
4524 my $currentrunningvcpus = undef;
4526 $currentrunningvcpus = mon_cmd($vmid, "query-cpus");
4527 last if scalar(@{$currentrunningvcpus}) == $i-1;
4528 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4532 #update conf after each succesfull cpu unplug
4533 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4534 PVE::QemuConfig->write_config($vmid, $conf);
4537 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4543 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus");
4544 die "vcpus in running vm does not match its configuration\n"
4545 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4547 if (qemu_machine_feature_enabled ($machine_type, undef, 2, 7)) {
4549 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4550 my $cpustr = print_cpu_device($conf, $i);
4551 qemu_deviceadd($vmid, $cpustr);
4554 my $currentrunningvcpus = undef;
4556 $currentrunningvcpus = mon_cmd($vmid, "query-cpus");
4557 last if scalar(@{$currentrunningvcpus}) == $i;
4558 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4562 #update conf after each succesfull cpu hotplug
4563 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4564 PVE::QemuConfig->write_config($vmid, $conf);
4568 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4569 mon_cmd($vmid, "cpu-add", id => int($i));
4574 sub qemu_block_set_io_throttle {
4575 my ($vmid, $deviceid,
4576 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4577 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4578 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4579 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4581 return if !check_running($vmid) ;
4583 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4585 bps_rd => int($bps_rd),
4586 bps_wr => int($bps_wr),
4588 iops_rd => int($iops_rd),
4589 iops_wr => int($iops_wr),
4590 bps_max => int($bps_max),
4591 bps_rd_max => int($bps_rd_max),
4592 bps_wr_max => int($bps_wr_max),
4593 iops_max => int($iops_max),
4594 iops_rd_max => int($iops_rd_max),
4595 iops_wr_max => int($iops_wr_max),
4596 bps_max_length => int($bps_max_length),
4597 bps_rd_max_length => int($bps_rd_max_length),
4598 bps_wr_max_length => int($bps_wr_max_length),
4599 iops_max_length => int($iops_max_length),
4600 iops_rd_max_length => int($iops_rd_max_length),
4601 iops_wr_max_length => int($iops_wr_max_length),
4606 # old code, only used to shutdown old VM after update
4608 my ($fh, $timeout) = @_;
4610 my $sel = new IO::Select;
4617 while (scalar (@ready = $sel->can_read($timeout))) {
4619 if ($count = $fh->sysread($buf, 8192)) {
4620 if ($buf =~ /^(.*)\(qemu\) $/s) {
4627 if (!defined($count)) {
4634 die "monitor read timeout\n" if !scalar(@ready);
4639 sub qemu_block_resize {
4640 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4642 my $running = check_running($vmid);
4644 $size = 0 if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4646 return if !$running;
4648 mon_cmd($vmid, "block_resize", device => $deviceid, size => int($size));
4652 sub qemu_volume_snapshot {
4653 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4655 my $running = check_running($vmid);
4657 if ($running && do_snapshots_with_qemu($storecfg, $volid)){
4658 mon_cmd($vmid, 'blockdev-snapshot-internal-sync
', device => $deviceid, name => $snap);
4660 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4664 sub qemu_volume_snapshot_delete {
4665 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4667 my $running = check_running($vmid);
4672 my $conf = PVE::QemuConfig->load_config($vmid);
4673 foreach_drive($conf, sub {
4674 my ($ds, $drive) = @_;
4675 $running = 1 if $drive->{file} eq $volid;
4679 if ($running && do_snapshots_with_qemu($storecfg, $volid)){
4680 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync
', device => $deviceid, name => $snap);
4682 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4686 sub set_migration_caps {
4692 "auto-converge" => 1,
4694 "x-rdma-pin-all" => 0,
4699 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4701 for my $supported_capability (@$supported_capabilities) {
4703 capability => $supported_capability->{capability},
4704 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4708 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4711 my $fast_plug_option = {
4719 'vmstatestorage
' => 1,
4723 # hotplug changes in [PENDING]
4724 # $selection hash can be used to only apply specified options, for
4725 # example: { cores => 1 } (only apply changed 'cores
')
4726 # $errors ref is used to return error messages
4727 sub vmconfig_hotplug_pending {
4728 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4730 my $defaults = load_defaults();
4731 my ($arch, $machine_type) = get_basic_machine_info($conf, undef);
4733 # commit values which do not have any impact on running VM first
4734 # Note: those option cannot raise errors, we we do not care about
4735 # $selection and always apply them.
4737 my $add_error = sub {
4738 my ($opt, $msg) = @_;
4739 $errors->{$opt} = "hotplug problem - $msg";
4743 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4744 if ($fast_plug_option->{$opt}) {
4745 $conf->{$opt} = $conf->{pending}->{$opt};
4746 delete $conf->{pending}->{$opt};
4752 PVE::QemuConfig->write_config($vmid, $conf);
4753 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
4756 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
4758 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4759 foreach my $opt (sort keys %$pending_delete_hash) {
4760 next if $selection && !$selection->{$opt};
4761 my $force = $pending_delete_hash->{$opt}->{force};
4763 if ($opt eq 'hotplug
') {
4764 die "skip\n" if ($conf->{hotplug} =~ /memory/);
4765 } elsif ($opt eq 'tablet
') {
4766 die "skip\n" if !$hotplug_features->{usb};
4767 if ($defaults->{tablet}) {
4768 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4769 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4770 if $arch eq 'aarch64
';
4772 vm_deviceunplug($vmid, $conf, 'tablet
');
4773 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4775 } elsif ($opt =~ m/^usb\d+/) {
4777 # since we cannot reliably hot unplug usb devices
4778 # we are disabling it
4779 die "skip\n" if !$hotplug_features->{usb} || $conf->{$opt} =~ m/spice/i;
4780 vm_deviceunplug($vmid, $conf, $opt);
4781 } elsif ($opt eq 'vcpus
') {
4782 die "skip\n" if !$hotplug_features->{cpu};
4783 qemu_cpu_hotplug($vmid, $conf, undef);
4784 } elsif ($opt eq 'balloon
') {
4785 # enable balloon device is not hotpluggable
4786 die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
4787 # here we reset the ballooning value to memory
4788 my $balloon = $conf->{memory} || $defaults->{memory};
4789 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4790 } elsif ($fast_plug_option->{$opt}) {
4792 } elsif ($opt =~ m/^net(\d+)$/) {
4793 die "skip\n" if !$hotplug_features->{network};
4794 vm_deviceunplug($vmid, $conf, $opt);
4795 } elsif (is_valid_drivename($opt)) {
4796 die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
4797 vm_deviceunplug($vmid, $conf, $opt);
4798 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4799 } elsif ($opt =~ m/^memory$/) {
4800 die "skip\n" if !$hotplug_features->{memory};
4801 PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
4802 } elsif ($opt eq 'cpuunits
') {
4803 cgroups_write("cpu", $vmid, "cpu.shares", $defaults->{cpuunits});
4804 } elsif ($opt eq 'cpulimit
') {
4805 cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", -1);
4811 &$add_error($opt, $err) if $err ne "skip\n";
4813 # save new config if hotplug was successful
4814 delete $conf->{$opt};
4815 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4816 PVE::QemuConfig->write_config($vmid, $conf);
4817 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
4821 my ($apply_pending_cloudinit, $apply_pending_cloudinit_done);
4822 $apply_pending_cloudinit = sub {
4823 return if $apply_pending_cloudinit_done; # once is enough
4824 $apply_pending_cloudinit_done = 1; # once is enough
4826 my ($key, $value) = @_;
4828 my @cloudinit_opts = keys %$confdesc_cloudinit;
4829 foreach my $opt (keys %{$conf->{pending}}) {
4830 next if !grep { $_ eq $opt } @cloudinit_opts;
4831 $conf->{$opt} = delete $conf->{pending}->{$opt};
4834 my $new_conf = { %$conf };
4835 $new_conf->{$key} = $value;
4836 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($new_conf, $vmid);
4839 foreach my $opt (keys %{$conf->{pending}}) {
4840 next if $selection && !$selection->{$opt};
4841 my $value = $conf->{pending}->{$opt};
4843 if ($opt eq 'hotplug
') {
4844 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/);
4845 } elsif ($opt eq 'tablet
') {
4846 die "skip\n" if !$hotplug_features->{usb};
4848 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4849 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4850 if $arch eq 'aarch64
';
4851 } elsif ($value == 0) {
4852 vm_deviceunplug($vmid, $conf, 'tablet
');
4853 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4855 } elsif ($opt =~ m/^usb\d+$/) {
4857 # since we cannot reliably hot unplug usb devices
4858 # we are disabling it
4859 die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
4860 my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format}, $value) };
4861 die "skip\n" if !$d;
4862 qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d, $arch, $machine_type);
4863 } elsif ($opt eq 'vcpus
') {
4864 die "skip\n" if !$hotplug_features->{cpu};
4865 qemu_cpu_hotplug($vmid, $conf, $value);
4866 } elsif ($opt eq 'balloon
') {
4867 # enable/disable balloning device is not hotpluggable
4868 my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon});
4869 my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon});
4870 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
4872 # allow manual ballooning if shares is set to zero
4873 if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
4874 my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
4875 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4877 } elsif ($opt =~ m/^net(\d+)$/) {
4878 # some changes can be done without hotplug
4879 vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
4880 $vmid, $opt, $value, $arch, $machine_type);
4881 } elsif (is_valid_drivename($opt)) {
4882 # some changes can be done without hotplug
4883 my $drive = parse_drive($opt, $value);
4884 if (drive_is_cloudinit($drive)) {
4885 &$apply_pending_cloudinit($opt, $value);
4887 vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
4888 $vmid, $opt, $value, 1, $arch, $machine_type);
4889 } elsif ($opt =~ m/^memory$/) { #dimms
4890 die "skip\n" if !$hotplug_features->{memory};
4891 $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
4892 } elsif ($opt eq 'cpuunits
') {
4893 cgroups_write("cpu", $vmid, "cpu.shares", $conf->{pending}->{$opt});
4894 } elsif ($opt eq 'cpulimit
') {
4895 my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
4896 cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", $cpulimit);
4898 die "skip\n"; # skip non-hot-pluggable options
4902 &$add_error($opt, $err) if $err ne "skip\n";
4904 # save new config if hotplug was successful
4905 $conf->{$opt} = $value;
4906 delete $conf->{pending}->{$opt};
4907 PVE::QemuConfig->write_config($vmid, $conf);
4908 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
4913 sub try_deallocate_drive {
4914 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
4916 if (($force || $key =~ /^unused/) && !drive_is_cdrom($drive, 1)) {
4917 my $volid = $drive->{file};
4918 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
4919 my $sid = PVE::Storage::parse_volume_id($volid);
4920 $rpcenv->check($authuser, "/storage/$sid", ['Datastore
.AllocateSpace
']);
4922 # check if the disk is really unused
4923 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
4924 if is_volume_in_use($storecfg, $conf, $key, $volid);
4925 PVE::Storage::vdisk_free($storecfg, $volid);
4928 # If vm is not owner of this disk remove from config
4936 sub vmconfig_delete_or_detach_drive {
4937 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
4939 my $drive = parse_drive($opt, $conf->{$opt});
4941 my $rpcenv = PVE::RPCEnvironment::get();
4942 my $authuser = $rpcenv->get_user();
4945 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM
.Config
.Disk
']);
4946 try_deallocate_drive($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
4948 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $drive);
4954 sub vmconfig_apply_pending {
4955 my ($vmid, $conf, $storecfg) = @_;
4959 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4960 foreach my $opt (sort keys %$pending_delete_hash) {
4961 die "internal error" if $opt =~ m/^unused/;
4962 my $force = $pending_delete_hash->{$opt}->{force};
4963 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
4964 if (!defined($conf->{$opt})) {
4965 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4966 PVE::QemuConfig->write_config($vmid, $conf);
4967 } elsif (is_valid_drivename($opt)) {
4968 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4969 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4970 delete $conf->{$opt};
4971 PVE::QemuConfig->write_config($vmid, $conf);
4973 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4974 delete $conf->{$opt};
4975 PVE::QemuConfig->write_config($vmid, $conf);
4979 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
4981 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4982 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
4984 if (defined($conf->{$opt}) && ($conf->{$opt} eq $conf->{pending}->{$opt})) {
4985 # skip if nothing changed
4986 } elsif (is_valid_drivename($opt)) {
4987 vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
4988 if defined($conf->{$opt});
4989 $conf->{$opt} = $conf->{pending}->{$opt};
4991 $conf->{$opt} = $conf->{pending}->{$opt};
4994 delete $conf->{pending}->{$opt};
4995 PVE::QemuConfig->write_config($vmid, $conf);
4999 my $safe_num_ne = sub {
5002 return 0 if !defined($a) && !defined($b);
5003 return 1 if !defined($a);
5004 return 1 if !defined($b);
5009 my $safe_string_ne = sub {
5012 return 0 if !defined($a) && !defined($b);
5013 return 1 if !defined($a);
5014 return 1 if !defined($b);
5019 sub vmconfig_update_net {
5020 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5022 my $newnet = parse_net($value);
5024 if ($conf->{$opt}) {
5025 my $oldnet = parse_net($conf->{$opt});
5027 if (&$safe_string_ne($oldnet->{model}, $newnet->{model}) ||
5028 &$safe_string_ne($oldnet->{macaddr}, $newnet->{macaddr}) ||
5029 &$safe_num_ne($oldnet->{queues}, $newnet->{queues}) ||
5030 !($newnet->{bridge} && $oldnet->{bridge})) { # bridge/nat mode change
5032 # for non online change, we try to hot-unplug
5033 die "skip\n" if !$hotplug;
5034 vm_deviceunplug($vmid, $conf, $opt);
5037 die "internal error" if $opt !~ m/net(\d+)/;
5038 my $iface = "tap${vmid}i$1";
5040 if (&$safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
5041 &$safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
5042 &$safe_string_ne($oldnet->{trunks}, $newnet->{trunks}) ||
5043 &$safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
5044 PVE::Network::tap_unplug($iface);
5045 PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
5046 } elsif (&$safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
5047 # Rate can be applied on its own but any change above needs to
5048 # include the rate in tap_plug since OVS resets everything.
5049 PVE::Network::tap_rate_limit($iface, $newnet->{rate});
5052 if (&$safe_string_ne($oldnet->{link_down}, $newnet->{link_down})) {
5053 qemu_set_link_status($vmid, $opt, !$newnet->{link_down});
5061 vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
5067 sub vmconfig_update_disk {
5068 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $force, $arch, $machine_type) = @_;
5070 # fixme: do we need force?
5072 my $drive = parse_drive($opt, $value);
5074 if ($conf->{$opt}) {
5076 if (my $old_drive = parse_drive($opt, $conf->{$opt})) {
5078 my $media = $drive->{media} || 'disk
';
5079 my $oldmedia = $old_drive->{media} || 'disk
';
5080 die "unable to change media type\n" if $media ne $oldmedia;
5082 if (!drive_is_cdrom($old_drive)) {
5084 if ($drive->{file} ne $old_drive->{file}) {
5086 die "skip\n" if !$hotplug;
5088 # unplug and register as unused
5089 vm_deviceunplug($vmid, $conf, $opt);
5090 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive)
5093 # update existing disk
5095 # skip non hotpluggable value
5096 if (&$safe_string_ne($drive->{discard}, $old_drive->{discard}) ||
5097 &$safe_string_ne($drive->{iothread}, $old_drive->{iothread}) ||
5098 &$safe_string_ne($drive->{queues}, $old_drive->{queues}) ||
5099 &$safe_string_ne($drive->{cache}, $old_drive->{cache})) {
5104 if (&$safe_num_ne($drive->{mbps}, $old_drive->{mbps}) ||
5105 &$safe_num_ne($drive->{mbps_rd}, $old_drive->{mbps_rd}) ||
5106 &$safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) ||
5107 &$safe_num_ne($drive->{iops}, $old_drive->{iops}) ||
5108 &$safe_num_ne($drive->{iops_rd}, $old_drive->{iops_rd}) ||
5109 &$safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr}) ||
5110 &$safe_num_ne($drive->{mbps_max}, $old_drive->{mbps_max}) ||
5111 &$safe_num_ne($drive->{mbps_rd_max}, $old_drive->{mbps_rd_max}) ||
5112 &$safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) ||
5113 &$safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) ||
5114 &$safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) ||
5115 &$safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max}) ||
5116 &$safe_num_ne($drive->{bps_max_length}, $old_drive->{bps_max_length}) ||
5117 &$safe_num_ne($drive->{bps_rd_max_length}, $old_drive->{bps_rd_max_length}) ||
5118 &$safe_num_ne($drive->{bps_wr_max_length}, $old_drive->{bps_wr_max_length}) ||
5119 &$safe_num_ne($drive->{iops_max_length}, $old_drive->{iops_max_length}) ||
5120 &$safe_num_ne($drive->{iops_rd_max_length}, $old_drive->{iops_rd_max_length}) ||
5121 &$safe_num_ne($drive->{iops_wr_max_length}, $old_drive->{iops_wr_max_length})) {
5123 qemu_block_set_io_throttle($vmid,"drive-$opt",
5124 ($drive->{mbps} || 0)*1024*1024,
5125 ($drive->{mbps_rd} || 0)*1024*1024,
5126 ($drive->{mbps_wr} || 0)*1024*1024,
5127 $drive->{iops} || 0,
5128 $drive->{iops_rd} || 0,
5129 $drive->{iops_wr} || 0,
5130 ($drive->{mbps_max} || 0)*1024*1024,
5131 ($drive->{mbps_rd_max} || 0)*1024*1024,
5132 ($drive->{mbps_wr_max} || 0)*1024*1024,
5133 $drive->{iops_max} || 0,
5134 $drive->{iops_rd_max} || 0,
5135 $drive->{iops_wr_max} || 0,
5136 $drive->{bps_max_length} || 1,
5137 $drive->{bps_rd_max_length} || 1,
5138 $drive->{bps_wr_max_length} || 1,
5139 $drive->{iops_max_length} || 1,
5140 $drive->{iops_rd_max_length} || 1,
5141 $drive->{iops_wr_max_length} || 1);
5150 if ($drive->{file} eq 'none
') {
5151 mon_cmd($vmid, "eject",force => JSON::true,device => "drive-$opt");
5152 if (drive_is_cloudinit($old_drive)) {
5153 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive);
5156 my $path = get_iso_path($storecfg, $vmid, $drive->{file});
5157 mon_cmd($vmid, "eject", force => JSON::true,device => "drive-$opt"); # force eject if locked
5158 mon_cmd($vmid, "change", device => "drive-$opt",target => "$path") if $path;
5166 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
5168 PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
5169 vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
5173 my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused,
5174 $forcemachine, $spice_ticket, $migration_network, $migration_type, $targetstorage) = @_;
5176 PVE::QemuConfig->lock_config($vmid, sub {
5177 my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom);
5179 die "you can't start a vm
if it
's a template\n" if PVE::QemuConfig->is_template($conf);
5181 my $is_suspended = PVE::QemuConfig->has_lock($conf, 'suspended
');
5183 PVE::QemuConfig->check_lock($conf)
5184 if !($skiplock || $is_suspended);
5186 die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
5188 # clean up leftover reboot request files
5189 eval { clear_reboot_request($vmid); };
5192 if (!$statefile && scalar(keys %{$conf->{pending}})) {
5193 vmconfig_apply_pending($vmid, $conf, $storecfg);
5194 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
5197 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
5199 my $defaults = load_defaults();
5201 # set environment variable useful inside network script
5202 $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
5204 my $local_volumes = {};
5206 if ($targetstorage) {
5207 foreach_drive($conf, sub {
5208 my ($ds, $drive) = @_;
5210 return if drive_is_cdrom($drive);
5212 my $volid = $drive->{file};
5216 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
5218 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5219 return if $scfg->{shared};
5220 $local_volumes->{$ds} = [$volid, $storeid, $volname];
5225 foreach my $opt (sort keys %$local_volumes) {
5227 my ($volid, $storeid, $volname) = @{$local_volumes->{$opt}};
5228 my $drive = parse_drive($opt, $conf->{$opt});
5230 #if remote storage is specified, use default format
5231 if ($targetstorage && $targetstorage ne "1") {
5232 $storeid = $targetstorage;
5233 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
5234 $format = $defFormat;
5236 #else we use same format than original
5237 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5238 $format = qemu_img_format($scfg, $volid);
5241 my $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $format, undef, ($drive->{size}/1024));
5242 my $newdrive = $drive;
5243 $newdrive->{format} = $format;
5244 $newdrive->{file} = $newvolid;
5245 my $drivestr = PVE::QemuServer::print_drive($vmid, $newdrive);
5246 $local_volumes->{$opt} = $drivestr;
5247 #pass drive to conf for command line
5248 $conf->{$opt} = $drivestr;
5252 PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-start
', 1);
5254 if ($is_suspended) {
5255 # enforce machine type on suspended vm to ensure HW compatibility
5256 $forcemachine = $conf->{runningmachine};
5257 print "Resuming suspended VM\n";
5260 my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
5263 my $get_migration_ip = sub {
5264 my ($cidr, $nodename) = @_;
5266 return $migration_ip if defined($migration_ip);
5268 if (!defined($cidr)) {
5269 my $dc_conf = PVE::Cluster::cfs_read_file('datacenter
.cfg
');
5270 $cidr = $dc_conf->{migration}->{network};
5273 if (defined($cidr)) {
5274 my $ips = PVE::Network::get_local_ip_from_cidr($cidr);
5276 die "could not get IP: no address configured on local " .
5277 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5279 die "could not get IP: multiple addresses configured on local " .
5280 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5282 $migration_ip = @$ips[0];
5285 $migration_ip = PVE::Cluster::remote_node_ip($nodename, 1)
5286 if !defined($migration_ip);
5288 return $migration_ip;
5293 if ($statefile eq 'tcp
') {
5294 my $localip = "localhost";
5295 my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter
.cfg
');
5296 my $nodename = PVE::INotify::nodename();
5298 if (!defined($migration_type)) {
5299 if (defined($datacenterconf->{migration}->{type})) {
5300 $migration_type = $datacenterconf->{migration}->{type};
5302 $migration_type = 'secure
';
5306 if ($migration_type eq 'insecure
') {
5307 $localip = $get_migration_ip->($migration_network, $nodename);
5308 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5311 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5312 my $migrate_port = PVE::Tools::next_migrate_port($pfamily);
5313 $migrate_uri = "tcp:${localip}:${migrate_port}";
5314 push @$cmd, '-incoming
', $migrate_uri;
5317 } elsif ($statefile eq 'unix
') {
5318 # should be default for secure migrations as a ssh TCP forward
5319 # tunnel is not deterministic reliable ready and fails regurarly
5320 # to set up in time, so use UNIX socket forwards
5321 my $socket_addr = "/run/qemu-server/$vmid.migrate";
5322 unlink $socket_addr;
5324 $migrate_uri = "unix:$socket_addr";
5326 push @$cmd, '-incoming
', $migrate_uri;
5329 } elsif (-e $statefile) {
5330 push @$cmd, '-loadstate
', $statefile;
5332 my $statepath = PVE::Storage::path($storecfg, $statefile);
5333 push @$vollist, $statefile;
5334 push @$cmd, '-loadstate
', $statepath;
5341 for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
5342 my $d = parse_hostpci($conf->{"hostpci$i"});
5344 my $pcidevices = $d->{pciid};
5345 foreach my $pcidevice (@$pcidevices) {
5346 my $pciid = $pcidevice->{id};
5348 my $info = PVE::SysFSTools::pci_device_info("0000:$pciid");
5349 die "IOMMU not present\n" if !PVE::SysFSTools::check_iommu_support();
5350 die "no pci device info for device '$pciid'\n" if !$info;
5353 my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
5354 PVE::SysFSTools::pci_create_mdev_device($pciid, $uuid, $d->{mdev});
5356 die "can't unbind
/bind pci group to vfio
'$pciid'\n"
5357 if !PVE::SysFSTools::pci_dev_group_bind_to_vfio($pciid);
5358 die "can
't reset pci device '$pciid'\n"
5359 if $info->{has_fl_reset} and !PVE::SysFSTools::pci_dev_reset($info);
5364 PVE::Storage::activate_volumes($storecfg, $vollist);
5367 run_command(['/bin/systemctl
', 'stop
', "$vmid.scope"],
5368 outfunc => sub {}, errfunc => sub {});
5370 # Issues with the above 'stop
' not being fully completed are extremely rare, a very low
5371 # timeout should be more than enough here...
5372 PVE::Systemd::wait_for_unit_removed("$vmid.scope", 5);
5374 my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
5375 : $defaults->{cpuunits};
5377 my $start_timeout = ($conf->{hugepages} || $is_suspended) ? 300 : 30;
5378 my %run_params = (timeout => $statefile ? undef : $start_timeout, umask => 0077);
5381 Slice => 'qemu
.slice
',
5383 CPUShares => $cpuunits
5386 if (my $cpulimit = $conf->{cpulimit}) {
5387 $properties{CPUQuota} = int($cpulimit * 100);
5389 $properties{timeout} = 10 if $statefile; # setting up the scope shoul be quick
5391 my $run_qemu = sub {
5392 PVE::Tools::run_fork sub {
5393 PVE::Systemd::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %properties);
5394 run_command($cmd, %run_params);
5398 if ($conf->{hugepages}) {
5401 my $hugepages_topology = PVE::QemuServer::Memory::hugepages_topology($conf);
5402 my $hugepages_host_topology = PVE::QemuServer::Memory::hugepages_host_topology();
5404 PVE::QemuServer::Memory::hugepages_mount();
5405 PVE::QemuServer::Memory::hugepages_allocate($hugepages_topology, $hugepages_host_topology);
5407 eval { $run_qemu->() };
5409 PVE::QemuServer::Memory::hugepages_reset($hugepages_host_topology);
5413 PVE::QemuServer::Memory::hugepages_pre_deallocate($hugepages_topology);
5415 eval { PVE::QemuServer::Memory::hugepages_update_locked($code); };
5418 eval { $run_qemu->() };
5422 # deactivate volumes if start fails
5423 eval { PVE::Storage::deactivate_volumes($storecfg, $vollist); };
5424 die "start failed: $err";
5427 print "migration listens on $migrate_uri\n" if $migrate_uri;
5429 if ($statefile && $statefile ne 'tcp
' && $statefile ne 'unix
') {
5430 eval { mon_cmd($vmid, "cont"); };
5434 #start nbd server for storage migration
5435 if ($targetstorage) {
5436 my $nodename = PVE::INotify::nodename();
5437 my $localip = $get_migration_ip->($migration_network, $nodename);
5438 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5439 my $storage_migrate_port = PVE::Tools::next_migrate_port($pfamily);
5441 mon_cmd($vmid, "nbd-server-start", addr => { type => 'inet
', data => { host => "${localip}", port => "${storage_migrate_port}" } } );
5443 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5445 foreach my $opt (sort keys %$local_volumes) {
5446 my $volid = $local_volumes->{$opt};
5447 mon_cmd($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
5448 my $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}:exportname=drive-$opt";
5449 print "storage migration listens on $migrate_storage_uri volume:$volid\n";
5453 if ($migratedfrom) {
5455 set_migration_caps($vmid);
5460 print "spice listens on port $spice_port\n";
5461 if ($spice_ticket) {
5462 mon_cmd($vmid, "set_password", protocol => 'spice
', password => $spice_ticket);
5463 mon_cmd($vmid, "expire_password", protocol => 'spice
', time => "+30");
5468 mon_cmd($vmid, "balloon", value => $conf->{balloon}*1024*1024)
5469 if !$statefile && $conf->{balloon};
5471 foreach my $opt (keys %$conf) {
5472 next if $opt !~ m/^net\d+$/;
5473 my $nicconf = parse_net($conf->{$opt});
5474 qemu_set_link_status($vmid, $opt, 0) if $nicconf->{link_down};
5478 mon_cmd($vmid, 'qom-set
',
5479 path => "machine/peripheral/balloon0",
5480 property => "guest-stats-polling-interval",
5481 value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
5483 if ($is_suspended && (my $vmstate = $conf->{vmstate})) {
5484 print "Resumed VM, removing state\n";
5485 delete $conf->@{qw(lock vmstate runningmachine)};
5486 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
5487 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
5488 PVE
::QemuConfig-
>write_config($vmid, $conf);
5491 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'post-start');
5495 sub vm_commandline
{
5496 my ($storecfg, $vmid, $snapname) = @_;
5498 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5502 my $snapshot = $conf->{snapshots
}->{$snapname};
5503 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
5505 # check for a 'runningmachine' in snapshot
5506 $forcemachine = $snapshot->{runningmachine
} if $snapshot->{runningmachine
};
5508 $snapshot->{digest
} = $conf->{digest
}; # keep file digest for API
5513 my $defaults = load_defaults
();
5515 my $cmd = config_to_command
($storecfg, $vmid, $conf, $defaults, $forcemachine);
5517 return PVE
::Tools
::cmd2string
($cmd);
5521 my ($vmid, $skiplock) = @_;
5523 PVE
::QemuConfig-
>lock_config($vmid, sub {
5525 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5527 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5529 mon_cmd
($vmid, "system_reset");
5533 sub get_vm_volumes
{
5537 foreach_volid
($conf, sub {
5538 my ($volid, $attr) = @_;
5540 return if $volid =~ m
|^/|;
5542 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
5545 push @$vollist, $volid;
5551 sub vm_stop_cleanup
{
5552 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
5557 my $vollist = get_vm_volumes
($conf);
5558 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
5561 foreach my $ext (qw(mon qmp pid vnc qga)) {
5562 unlink "/var/run/qemu-server/${vmid}.$ext";
5565 if ($conf->{ivshmem
}) {
5566 my $ivshmem = PVE
::JSONSchema
::parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
5567 # just delete it for now, VMs which have this already open do not
5568 # are affected, but new VMs will get a separated one. If this
5569 # becomes an issue we either add some sort of ref-counting or just
5570 # add a "don't delete on stop" flag to the ivshmem format.
5571 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name
} // $vmid);
5574 foreach my $key (keys %$conf) {
5575 next if $key !~ m/^hostpci(\d+)$/;
5576 my $hostpciindex = $1;
5577 my $d = parse_hostpci
($conf->{$key});
5578 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $hostpciindex);
5580 foreach my $pci (@{$d->{pciid
}}) {
5581 my $pciid = $pci->{id
};
5582 PVE
::SysFSTools
::pci_cleanup_mdev_device
($pciid, $uuid);
5586 vmconfig_apply_pending
($vmid, $conf, $storecfg) if $apply_pending_changes;
5588 warn $@ if $@; # avoid errors - just warn
5591 # call only in locked context
5593 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
5595 my $pid = check_running
($vmid, $nocheck);
5600 $conf = PVE
::QemuConfig-
>load_config($vmid);
5601 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5602 if (!defined($timeout) && $shutdown && $conf->{startup
}) {
5603 my $opts = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
5604 $timeout = $opts->{down
} if $opts->{down
};
5606 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-stop');
5611 if (defined($conf) && parse_guest_agent
($conf)->{enabled
}) {
5612 mon_cmd
($vmid, "guest-shutdown", timeout
=> $timeout);
5614 mon_cmd
($vmid, "system_powerdown");
5617 mon_cmd
($vmid, "quit");
5623 $timeout = 60 if !defined($timeout);
5626 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
5631 if ($count >= $timeout) {
5633 warn "VM still running - terminating now with SIGTERM\n";
5636 die "VM quit/powerdown failed - got timeout\n";
5639 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5644 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
5647 die "VM quit/powerdown failed\n";
5655 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
5660 if ($count >= $timeout) {
5661 warn "VM still running - terminating now with SIGKILL\n";
5666 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5669 # Note: use $nocheck to skip tests if VM configuration file exists.
5670 # We need that when migration VMs to other nodes (files already moved)
5671 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
5673 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
5675 $force = 1 if !defined($force) && !$shutdown;
5678 my $pid = check_running
($vmid, $nocheck, $migratedfrom);
5679 kill 15, $pid if $pid;
5680 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migratedfrom);
5681 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 0);
5685 PVE
::QemuConfig-
>lock_config($vmid, sub {
5686 _do_vm_stop
($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
5691 my ($vmid, $timeout) = @_;
5693 PVE
::QemuConfig-
>lock_config($vmid, sub {
5696 # only reboot if running, as qmeventd starts it again on a stop event
5697 return if !check_running
($vmid);
5699 create_reboot_request
($vmid);
5701 my $storecfg = PVE
::Storage
::config
();
5702 _do_vm_stop
($storecfg, $vmid, undef, undef, $timeout, 1);
5706 # avoid that the next normal shutdown will be confused for a reboot
5707 clear_reboot_request
($vmid);
5714 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
5721 PVE
::QemuConfig-
>lock_config($vmid, sub {
5723 $conf = PVE
::QemuConfig-
>load_config($vmid);
5725 my $is_backing_up = PVE
::QemuConfig-
>has_lock($conf, 'backup');
5726 PVE
::QemuConfig-
>check_lock($conf)
5727 if !($skiplock || $is_backing_up);
5729 die "cannot suspend to disk during backup\n"
5730 if $is_backing_up && $includestate;
5732 if ($includestate) {
5733 $conf->{lock} = 'suspending';
5734 my $date = strftime
("%Y-%m-%d", localtime(time()));
5735 $storecfg = PVE
::Storage
::config
();
5736 $vmstate = PVE
::QemuConfig-
>__snapshot_save_vmstate($vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
5737 $path = PVE
::Storage
::path
($storecfg, $vmstate);
5738 PVE
::QemuConfig-
>write_config($vmid, $conf);
5740 mon_cmd
($vmid, "stop");
5744 if ($includestate) {
5746 PVE
::Storage
::activate_volumes
($storecfg, [$vmstate]);
5749 mon_cmd
($vmid, "savevm-start", statefile
=> $path);
5751 my $state = mon_cmd
($vmid, "query-savevm");
5752 if (!$state->{status
}) {
5753 die "savevm not active\n";
5754 } elsif ($state->{status
} eq 'active') {
5757 } elsif ($state->{status
} eq 'completed') {
5758 print "State saved, quitting\n";
5760 } elsif ($state->{status
} eq 'failed' && $state->{error
}) {
5761 die "query-savevm failed with error '$state->{error}'\n"
5763 die "query-savevm returned status '$state->{status}'\n";
5769 PVE
::QemuConfig-
>lock_config($vmid, sub {
5770 $conf = PVE
::QemuConfig-
>load_config($vmid);
5772 # cleanup, but leave suspending lock, to indicate something went wrong
5774 mon_cmd
($vmid, "savevm-end");
5775 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
5776 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
5777 delete $conf->@{qw(vmstate runningmachine)};
5778 PVE
::QemuConfig-
>write_config($vmid, $conf);
5784 die "lock changed unexpectedly\n"
5785 if !PVE
::QemuConfig-
>has_lock($conf, 'suspending');
5787 mon_cmd
($vmid, "quit");
5788 $conf->{lock} = 'suspended';
5789 PVE
::QemuConfig-
>write_config($vmid, $conf);
5795 my ($vmid, $skiplock, $nocheck) = @_;
5797 PVE
::QemuConfig-
>lock_config($vmid, sub {
5798 my $res = mon_cmd
($vmid, 'query-status');
5799 my $resume_cmd = 'cont';
5801 if ($res->{status
} && $res->{status
} eq 'suspended') {
5802 $resume_cmd = 'system_wakeup';
5807 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5809 PVE
::QemuConfig-
>check_lock($conf)
5810 if !($skiplock || PVE
::QemuConfig-
>has_lock($conf, 'backup'));
5813 mon_cmd
($vmid, $resume_cmd);
5818 my ($vmid, $skiplock, $key) = @_;
5820 PVE
::QemuConfig-
>lock_config($vmid, sub {
5822 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5824 # there is no qmp command, so we use the human monitor command
5825 my $res = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "sendkey $key");
5826 die $res if $res ne '';
5830 # vzdump restore implementaion
5832 sub tar_archive_read_firstfile
{
5833 my $archive = shift;
5835 die "ERROR: file '$archive' does not exist\n" if ! -f
$archive;
5837 # try to detect archive type first
5838 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
5839 die "unable to open file '$archive'\n";
5840 my $firstfile = <$fh>;
5844 die "ERROR: archive contaions no data\n" if !$firstfile;
5850 sub tar_restore_cleanup
{
5851 my ($storecfg, $statfile) = @_;
5853 print STDERR
"starting cleanup\n";
5855 if (my $fd = IO
::File-
>new($statfile, "r")) {
5856 while (defined(my $line = <$fd>)) {
5857 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
5860 if ($volid =~ m
|^/|) {
5861 unlink $volid || die 'unlink failed\n';
5863 PVE
::Storage
::vdisk_free
($storecfg, $volid);
5865 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
5867 print STDERR
"unable to cleanup '$volid' - $@" if $@;
5869 print STDERR
"unable to parse line in statfile - $line";
5876 sub restore_archive
{
5877 my ($archive, $vmid, $user, $opts) = @_;
5879 my $format = $opts->{format
};
5882 if ($archive =~ m/\.tgz$/ || $archive =~ m/\.tar\.gz$/) {
5883 $format = 'tar' if !$format;
5885 } elsif ($archive =~ m/\.tar$/) {
5886 $format = 'tar' if !$format;
5887 } elsif ($archive =~ m/.tar.lzo$/) {
5888 $format = 'tar' if !$format;
5890 } elsif ($archive =~ m/\.vma$/) {
5891 $format = 'vma' if !$format;
5892 } elsif ($archive =~ m/\.vma\.gz$/) {
5893 $format = 'vma' if !$format;
5895 } elsif ($archive =~ m/\.vma\.lzo$/) {
5896 $format = 'vma' if !$format;
5899 $format = 'vma' if !$format; # default
5902 # try to detect archive format
5903 if ($format eq 'tar') {
5904 return restore_tar_archive
($archive, $vmid, $user, $opts);
5906 return restore_vma_archive
($archive, $vmid, $user, $opts, $comp);
5910 sub restore_update_config_line
{
5911 my ($outfd, $cookie, $vmid, $map, $line, $unique) = @_;
5913 return if $line =~ m/^\#qmdump\#/;
5914 return if $line =~ m/^\#vzdump\#/;
5915 return if $line =~ m/^lock:/;
5916 return if $line =~ m/^unused\d+:/;
5917 return if $line =~ m/^parent:/;
5919 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5920 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
5921 # try to convert old 1.X settings
5922 my ($id, $ind, $ethcfg) = ($1, $2, $3);
5923 foreach my $devconfig (PVE
::Tools
::split_list
($ethcfg)) {
5924 my ($model, $macaddr) = split(/\=/, $devconfig);
5925 $macaddr = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if !$macaddr || $unique;
5928 bridge
=> "vmbr$ind",
5929 macaddr
=> $macaddr,
5931 my $netstr = print_net
($net);
5933 print $outfd "net$cookie->{netcount}: $netstr\n";
5934 $cookie->{netcount
}++;
5936 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
5937 my ($id, $netstr) = ($1, $2);
5938 my $net = parse_net
($netstr);
5939 $net->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if $net->{macaddr
};
5940 $netstr = print_net
($net);
5941 print $outfd "$id: $netstr\n";
5942 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk)\d+):\s*(\S+)\s*$/) {
5945 my $di = parse_drive
($virtdev, $value);
5946 if (defined($di->{backup
}) && !$di->{backup
}) {
5947 print $outfd "#$line";
5948 } elsif ($map->{$virtdev}) {
5949 delete $di->{format
}; # format can change on restore
5950 $di->{file
} = $map->{$virtdev};
5951 $value = print_drive
($vmid, $di);
5952 print $outfd "$virtdev: $value\n";
5956 } elsif (($line =~ m/^vmgenid: (.*)/)) {
5958 if ($vmgenid ne '0') {
5959 # always generate a new vmgenid if there was a valid one setup
5960 $vmgenid = generate_uuid
();
5962 print $outfd "vmgenid: $vmgenid\n";
5963 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
5964 my ($uuid, $uuid_str);
5965 UUID
::generate
($uuid);
5966 UUID
::unparse
($uuid, $uuid_str);
5967 my $smbios1 = parse_smbios1
($2);
5968 $smbios1->{uuid
} = $uuid_str;
5969 print $outfd $1.print_smbios1
($smbios1)."\n";
5976 my ($cfg, $vmid) = @_;
5978 my $info = PVE
::Storage
::vdisk_list
($cfg, undef, $vmid);
5980 my $volid_hash = {};
5981 foreach my $storeid (keys %$info) {
5982 foreach my $item (@{$info->{$storeid}}) {
5983 next if !($item->{volid
} && $item->{size
});
5984 $item->{path
} = PVE
::Storage
::path
($cfg, $item->{volid
});
5985 $volid_hash->{$item->{volid
}} = $item;
5992 sub is_volume_in_use
{
5993 my ($storecfg, $conf, $skip_drive, $volid) = @_;
5995 my $path = PVE
::Storage
::path
($storecfg, $volid);
5997 my $scan_config = sub {
5998 my ($cref, $snapname) = @_;
6000 foreach my $key (keys %$cref) {
6001 my $value = $cref->{$key};
6002 if (is_valid_drivename
($key)) {
6003 next if $skip_drive && $key eq $skip_drive;
6004 my $drive = parse_drive
($key, $value);
6005 next if !$drive || !$drive->{file
} || drive_is_cdrom
($drive);
6006 return 1 if $volid eq $drive->{file
};
6007 if ($drive->{file
} =~ m!^/!) {
6008 return 1 if $drive->{file
} eq $path;
6010 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
}, 1);
6012 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid, 1);
6014 return 1 if $path eq PVE
::Storage
::path
($storecfg, $drive->{file
}, $snapname);
6022 return 1 if &$scan_config($conf);
6026 foreach my $snapname (keys %{$conf->{snapshots
}}) {
6027 return 1 if &$scan_config($conf->{snapshots
}->{$snapname}, $snapname);
6033 sub update_disksize
{
6034 my ($vmid, $conf, $volid_hash) = @_;
6037 my $prefix = "VM $vmid:";
6039 # used and unused disks
6040 my $referenced = {};
6042 # Note: it is allowed to define multiple storages with same path (alias), so
6043 # we need to check both 'volid' and real 'path' (two different volid can point
6044 # to the same path).
6046 my $referencedpath = {};
6049 foreach my $opt (keys %$conf) {
6050 if (is_valid_drivename
($opt)) {
6051 my $drive = parse_drive
($opt, $conf->{$opt});
6052 my $volid = $drive->{file
};
6055 $referenced->{$volid} = 1;
6056 if ($volid_hash->{$volid} &&
6057 (my $path = $volid_hash->{$volid}->{path
})) {
6058 $referencedpath->{$path} = 1;
6061 next if drive_is_cdrom
($drive);
6062 next if !$volid_hash->{$volid};
6064 $drive->{size
} = $volid_hash->{$volid}->{size
};
6065 my $new = print_drive
($vmid, $drive);
6066 if ($new ne $conf->{$opt}) {
6068 $conf->{$opt} = $new;
6069 print "$prefix update disk '$opt' information.\n";
6074 # remove 'unusedX' entry if volume is used
6075 foreach my $opt (keys %$conf) {
6076 next if $opt !~ m/^unused\d+$/;
6077 my $volid = $conf->{$opt};
6078 my $path = $volid_hash->{$volid}->{path
} if $volid_hash->{$volid};
6079 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6080 print "$prefix remove entry '$opt', its volume '$volid' is in use.\n";
6082 delete $conf->{$opt};
6085 $referenced->{$volid} = 1;
6086 $referencedpath->{$path} = 1 if $path;
6089 foreach my $volid (sort keys %$volid_hash) {
6090 next if $volid =~ m/vm-$vmid-state-/;
6091 next if $referenced->{$volid};
6092 my $path = $volid_hash->{$volid}->{path
};
6093 next if !$path; # just to be sure
6094 next if $referencedpath->{$path};
6096 my $key = PVE
::QemuConfig-
>add_unused_volume($conf, $volid);
6097 print "$prefix add unreferenced volume '$volid' as '$key' to config.\n";
6098 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6105 my ($vmid, $nolock, $dryrun) = @_;
6107 my $cfg = PVE
::Storage
::config
();
6109 # FIXME: Remove once our RBD plugin can handle CT and VM on a single storage
6110 # see: https://pve.proxmox.com/pipermail/pve-devel/2018-July/032900.html
6111 foreach my $stor (keys %{$cfg->{ids
}}) {
6112 delete($cfg->{ids
}->{$stor}) if ! $cfg->{ids
}->{$stor}->{content
}->{images
};
6115 print "rescan volumes...\n";
6116 my $volid_hash = scan_volids
($cfg, $vmid);
6118 my $updatefn = sub {
6121 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6123 PVE
::QemuConfig-
>check_lock($conf);
6126 foreach my $volid (keys %$volid_hash) {
6127 my $info = $volid_hash->{$volid};
6128 $vm_volids->{$volid} = $info if $info->{vmid
} && $info->{vmid
} == $vmid;
6131 my $changes = update_disksize
($vmid, $conf, $vm_volids);
6133 PVE
::QemuConfig-
>write_config($vmid, $conf) if $changes && !$dryrun;
6136 if (defined($vmid)) {
6140 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6143 my $vmlist = config_list
();
6144 foreach my $vmid (keys %$vmlist) {
6148 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6154 sub restore_vma_archive
{
6155 my ($archive, $vmid, $user, $opts, $comp) = @_;
6157 my $readfrom = $archive;
6159 my $cfg = PVE
::Storage
::config
();
6161 my $bwlimit = $opts->{bwlimit
};
6163 my $dbg_cmdstring = '';
6164 my $add_pipe = sub {
6166 push @$commands, $cmd;
6167 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
6168 $dbg_cmdstring .= PVE
::Tools
::cmd2string
($cmd);
6173 if ($archive eq '-') {
6176 # If we use a backup from a PVE defined storage we also consider that
6177 # storage's rate limit:
6178 my (undef, $volid) = PVE
::Storage
::path_to_volume_id
($cfg, $archive);
6179 if (defined($volid)) {
6180 my ($sid, undef) = PVE
::Storage
::parse_volume_id
($volid);
6181 my $readlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [$sid], $bwlimit);
6183 print STDERR
"applying read rate limit: $readlimit\n";
6184 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
6185 $add_pipe->($cstream);
6192 if ($comp eq 'gzip') {
6193 $cmd = ['zcat', $readfrom];
6194 } elsif ($comp eq 'lzop') {
6195 $cmd = ['lzop', '-d', '-c', $readfrom];
6197 die "unknown compression method '$comp'\n";
6202 my $tmpdir = "/var/tmp/vzdumptmp$$";
6205 # disable interrupts (always do cleanups)
6209 local $SIG{HUP
} = sub { warn "got interrupt - ignored\n"; };
6211 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
6212 POSIX
::mkfifo
($mapfifo, 0600);
6215 my $openfifo = sub {
6216 open($fifofh, '>', $mapfifo) || die $!;
6219 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
6226 my $rpcenv = PVE
::RPCEnvironment
::get
();
6228 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6229 my $tmpfn = "$conffile.$$.tmp";
6231 # Note: $oldconf is undef if VM does not exists
6232 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
6233 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
6237 my $print_devmap = sub {
6238 my $virtdev_hash = {};
6240 my $cfgfn = "$tmpdir/qemu-server.conf";
6242 # we can read the config - that is already extracted
6243 my $fh = IO
::File-
>new($cfgfn, "r") ||
6244 "unable to read qemu-server.conf - $!\n";
6246 my $fwcfgfn = "$tmpdir/qemu-server.fw";
6248 my $pve_firewall_dir = '/etc/pve/firewall';
6249 mkdir $pve_firewall_dir; # make sure the dir exists
6250 PVE
::Tools
::file_copy
($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
6253 while (defined(my $line = <$fh>)) {
6254 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
6255 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
6256 die "archive does not contain data for drive '$virtdev'\n"
6257 if !$devinfo->{$devname};
6258 if (defined($opts->{storage
})) {
6259 $storeid = $opts->{storage
} || 'local';
6260 } elsif (!$storeid) {
6263 $format = 'raw' if !$format;
6264 $devinfo->{$devname}->{devname
} = $devname;
6265 $devinfo->{$devname}->{virtdev
} = $virtdev;
6266 $devinfo->{$devname}->{format
} = $format;
6267 $devinfo->{$devname}->{storeid
} = $storeid;
6269 # check permission on storage
6270 my $pool = $opts->{pool
}; # todo: do we need that?
6271 if ($user ne 'root@pam') {
6272 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
6275 $storage_limits{$storeid} = $bwlimit;
6277 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
6278 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
6280 my $drive = parse_drive
($virtdev, $2);
6281 if (drive_is_cloudinit
($drive)) {
6282 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
6283 my $scfg = PVE
::Storage
::storage_config
($cfg, $storeid);
6284 my $format = qemu_img_format
($scfg, $volname); # has 'raw' fallback
6288 storeid
=> $opts->{storage
} // $storeid,
6289 size
=> PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
,
6290 file
=> $drive->{file
}, # to make drive_is_cloudinit check possible
6291 name
=> "vm-$vmid-cloudinit",
6294 $virtdev_hash->{$virtdev} = $d;
6299 foreach my $key (keys %storage_limits) {
6300 my $limit = PVE
::Storage
::get_bandwidth_limit
('restore', [$key], $bwlimit);
6302 print STDERR
"rate limit for storage $key: $limit KiB/s\n";
6303 $storage_limits{$key} = $limit * 1024;
6306 foreach my $devname (keys %$devinfo) {
6307 die "found no device mapping information for device '$devname'\n"
6308 if !$devinfo->{$devname}->{virtdev
};
6311 # create empty/temp config
6313 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\n");
6314 foreach_drive
($oldconf, sub {
6315 my ($ds, $drive) = @_;
6317 return if drive_is_cdrom
($drive, 1);
6319 my $volid = $drive->{file
};
6320 return if !$volid || $volid =~ m
|^/|;
6322 my ($path, $owner) = PVE
::Storage
::path
($cfg, $volid);
6323 return if !$path || !$owner || ($owner != $vmid);
6325 # Note: only delete disk we want to restore
6326 # other volumes will become unused
6327 if ($virtdev_hash->{$ds}) {
6328 eval { PVE
::Storage
::vdisk_free
($cfg, $volid); };
6335 # delete vmstate files, after the restore we have no snapshots anymore
6336 foreach my $snapname (keys %{$oldconf->{snapshots
}}) {
6337 my $snap = $oldconf->{snapshots
}->{$snapname};
6338 if ($snap->{vmstate
}) {
6339 eval { PVE
::Storage
::vdisk_free
($cfg, $snap->{vmstate
}); };
6348 foreach my $virtdev (sort keys %$virtdev_hash) {
6349 my $d = $virtdev_hash->{$virtdev};
6350 my $alloc_size = int(($d->{size
} + 1024 - 1)/1024);
6351 my $storeid = $d->{storeid
};
6352 my $scfg = PVE
::Storage
::storage_config
($cfg, $storeid);
6355 if (my $limit = $storage_limits{$storeid}) {
6356 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
6359 # test if requested format is supported
6360 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($cfg, $storeid);
6361 my $supported = grep { $_ eq $d->{format
} } @$validFormats;
6362 $d->{format
} = $defFormat if !$supported;
6365 if ($d->{is_cloudinit
}) {
6367 $name .= ".$d->{format}" if $d->{format
} ne 'raw';
6370 my $volid = PVE
::Storage
::vdisk_alloc
($cfg, $storeid, $vmid, $d->{format
}, $name, $alloc_size);
6371 print STDERR
"new volume ID is '$volid'\n";
6372 $d->{volid
} = $volid;
6374 PVE
::Storage
::activate_volumes
($cfg, [$volid]);
6376 my $write_zeros = 1;
6377 if (PVE
::Storage
::volume_has_feature
($cfg, 'sparseinit', $volid)) {
6381 if (!$d->{is_cloudinit
}) {
6382 my $path = PVE
::Storage
::path
($cfg, $volid);
6384 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
6386 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
6388 $map->{$virtdev} = $volid;
6391 $fh->seek(0, 0) || die "seek failed - $!\n";
6393 my $outfd = new IO
::File
($tmpfn, "w") ||
6394 die "unable to write config for VM $vmid\n";
6396 my $cookie = { netcount
=> 0 };
6397 while (defined(my $line = <$fh>)) {
6398 restore_update_config_line
($outfd, $cookie, $vmid, $map, $line, $opts->{unique
});
6411 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6412 local $SIG{ALRM
} = sub { die "got timeout\n"; };
6414 $oldtimeout = alarm($timeout);
6421 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
6422 my ($dev_id, $size, $devname) = ($1, $2, $3);
6423 $devinfo->{$devname} = { size
=> $size, dev_id
=> $dev_id };
6424 } elsif ($line =~ m/^CTIME: /) {
6425 # we correctly received the vma config, so we can disable
6426 # the timeout now for disk allocation (set to 10 minutes, so
6427 # that we always timeout if something goes wrong)
6430 print $fifofh "done\n";
6431 my $tmp = $oldtimeout || 0;
6432 $oldtimeout = undef;
6438 print "restore vma archive: $dbg_cmdstring\n";
6439 run_command
($commands, input
=> $input, outfunc
=> $parser, afterfork
=> $openfifo);
6443 alarm($oldtimeout) if $oldtimeout;
6446 foreach my $devname (keys %$devinfo) {
6447 my $volid = $devinfo->{$devname}->{volid
};
6448 push @$vollist, $volid if $volid;
6451 PVE
::Storage
::deactivate_volumes
($cfg, $vollist);
6459 foreach my $devname (keys %$devinfo) {
6460 my $volid = $devinfo->{$devname}->{volid
};
6463 if ($volid =~ m
|^/|) {
6464 unlink $volid || die 'unlink failed\n';
6466 PVE
::Storage
::vdisk_free
($cfg, $volid);
6468 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6470 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6477 rename($tmpfn, $conffile) ||
6478 die "unable to commit configuration file '$conffile'\n";
6480 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6482 eval { rescan
($vmid, 1); };
6486 sub restore_tar_archive
{
6487 my ($archive, $vmid, $user, $opts) = @_;
6489 if ($archive ne '-') {
6490 my $firstfile = tar_archive_read_firstfile
($archive);
6491 die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n"
6492 if $firstfile ne 'qemu-server.conf';
6495 my $storecfg = PVE
::Storage
::config
();
6497 # avoid zombie disks when restoring over an existing VM -> cleanup first
6498 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
6499 # skiplock=1 because qmrestore has set the 'create' lock itself already
6500 my $vmcfgfn = PVE
::QemuConfig-
>config_file($vmid);
6501 destroy_vm
($storecfg, $vmid, 1, { lock => 'restore' }) if -f
$vmcfgfn;
6503 my $tocmd = "/usr/lib/qemu-server/qmextract";
6505 $tocmd .= " --storage " . PVE
::Tools
::shellquote
($opts->{storage
}) if $opts->{storage
};
6506 $tocmd .= " --pool " . PVE
::Tools
::shellquote
($opts->{pool
}) if $opts->{pool
};
6507 $tocmd .= ' --prealloc' if $opts->{prealloc
};
6508 $tocmd .= ' --info' if $opts->{info
};
6510 # tar option "xf" does not autodetect compression when read from STDIN,
6511 # so we pipe to zcat
6512 my $cmd = "zcat -f|tar xf " . PVE
::Tools
::shellquote
($archive) . " " .
6513 PVE
::Tools
::shellquote
("--to-command=$tocmd");
6515 my $tmpdir = "/var/tmp/vzdumptmp$$";
6518 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
6519 local $ENV{VZDUMP_VMID
} = $vmid;
6520 local $ENV{VZDUMP_USER
} = $user;
6522 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6523 my $tmpfn = "$conffile.$$.tmp";
6525 # disable interrupts (always do cleanups)
6529 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
6537 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6539 if ($archive eq '-') {
6540 print "extracting archive from STDIN\n";
6541 run_command
($cmd, input
=> "<&STDIN");
6543 print "extracting archive '$archive'\n";
6547 return if $opts->{info
};
6551 my $statfile = "$tmpdir/qmrestore.stat";
6552 if (my $fd = IO
::File-
>new($statfile, "r")) {
6553 while (defined (my $line = <$fd>)) {
6554 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6555 $map->{$1} = $2 if $1;
6557 print STDERR
"unable to parse line in statfile - $line\n";
6563 my $confsrc = "$tmpdir/qemu-server.conf";
6565 my $srcfd = new IO
::File
($confsrc, "r") ||
6566 die "unable to open file '$confsrc'\n";
6568 my $outfd = new IO
::File
($tmpfn, "w") ||
6569 die "unable to write config for VM $vmid\n";
6571 my $cookie = { netcount
=> 0 };
6572 while (defined (my $line = <$srcfd>)) {
6573 restore_update_config_line
($outfd, $cookie, $vmid, $map, $line, $opts->{unique
});
6581 tar_restore_cleanup
($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info
};
6587 rename $tmpfn, $conffile ||
6588 die "unable to commit configuration file '$conffile'\n";
6590 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6592 eval { rescan
($vmid, 1); };
6596 sub foreach_storage_used_by_vm
{
6597 my ($conf, $func) = @_;
6601 foreach_drive
($conf, sub {
6602 my ($ds, $drive) = @_;
6603 return if drive_is_cdrom
($drive);
6605 my $volid = $drive->{file
};
6607 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
6608 $sidhash->{$sid} = $sid if $sid;
6611 foreach my $sid (sort keys %$sidhash) {
6616 sub do_snapshots_with_qemu
{
6617 my ($storecfg, $volid) = @_;
6619 my $storage_name = PVE
::Storage
::parse_volume_id
($volid);
6620 my $scfg = $storecfg->{ids
}->{$storage_name};
6622 if ($qemu_snap_storage->{$scfg->{type
}} && !$scfg->{krbd
}){
6626 if ($volid =~ m/\.(qcow2|qed)$/){
6633 sub qga_check_running
{
6634 my ($vmid, $nowarn) = @_;
6636 eval { mon_cmd
($vmid, "guest-ping", timeout
=> 3); };
6638 warn "Qemu Guest Agent is not running - $@" if !$nowarn;
6644 sub template_create
{
6645 my ($vmid, $conf, $disk) = @_;
6647 my $storecfg = PVE
::Storage
::config
();
6649 foreach_drive
($conf, sub {
6650 my ($ds, $drive) = @_;
6652 return if drive_is_cdrom
($drive);
6653 return if $disk && $ds ne $disk;
6655 my $volid = $drive->{file
};
6656 return if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
6658 my $voliddst = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
6659 $drive->{file
} = $voliddst;
6660 $conf->{$ds} = print_drive
($vmid, $drive);
6661 PVE
::QemuConfig-
>write_config($vmid, $conf);
6665 sub convert_iscsi_path
{
6668 if ($path =~ m
|^iscsi
://([^/]+)/([^/]+)/(.+)$|) {
6673 my $initiator_name = get_initiator_name
();
6675 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
6676 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
6679 die "cannot convert iscsi path '$path', unkown format\n";
6682 sub qemu_img_convert
{
6683 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
6685 my $storecfg = PVE
::Storage
::config
();
6686 my ($src_storeid, $src_volname) = PVE
::Storage
::parse_volume_id
($src_volid, 1);
6687 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid, 1);
6689 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
6693 my $src_is_iscsi = 0;
6694 my $src_format = 'raw';
6697 PVE
::Storage
::activate_volumes
($storecfg, [$src_volid], $snapname);
6698 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
6699 $src_format = qemu_img_format
($src_scfg, $src_volname);
6700 $src_path = PVE
::Storage
::path
($storecfg, $src_volid, $snapname);
6701 $src_is_iscsi = ($src_path =~ m
|^iscsi
://|);
6702 $cachemode = 'none' if $src_scfg->{type
} eq 'zfspool';
6703 } elsif (-f
$src_volid) {
6704 $src_path = $src_volid;
6705 if ($src_path =~ m/\.($QEMU_FORMAT_RE)$/) {
6710 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
6712 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
6713 my $dst_format = qemu_img_format
($dst_scfg, $dst_volname);
6714 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
6715 my $dst_is_iscsi = ($dst_path =~ m
|^iscsi
://|);
6718 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
6719 push @$cmd, '-l', "snapshot.name=$snapname" if($snapname && $src_format eq "qcow2");
6720 push @$cmd, '-t', 'none' if $dst_scfg->{type
} eq 'zfspool';
6721 push @$cmd, '-T', $cachemode if defined($cachemode);
6723 if ($src_is_iscsi) {
6724 push @$cmd, '--image-opts';
6725 $src_path = convert_iscsi_path
($src_path);
6727 push @$cmd, '-f', $src_format;
6730 if ($dst_is_iscsi) {
6731 push @$cmd, '--target-image-opts';
6732 $dst_path = convert_iscsi_path
($dst_path);
6734 push @$cmd, '-O', $dst_format;
6737 push @$cmd, $src_path;
6739 if (!$dst_is_iscsi && $is_zero_initialized) {
6740 push @$cmd, "zeroinit:$dst_path";
6742 push @$cmd, $dst_path;
6747 if($line =~ m/\((\S+)\/100\
%\)/){
6749 my $transferred = int($size * $percent / 100);
6750 my $remaining = $size - $transferred;
6752 print "transferred: $transferred bytes remaining: $remaining bytes total: $size bytes progression: $percent %\n";
6757 eval { run_command
($cmd, timeout
=> undef, outfunc
=> $parser); };
6759 die "copy failed: $err" if $err;
6762 sub qemu_img_format
{
6763 my ($scfg, $volname) = @_;
6765 if ($scfg->{path
} && $volname =~ m/\.($QEMU_FORMAT_RE)$/) {
6772 sub qemu_drive_mirror
{
6773 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $skipcomplete, $qga, $bwlimit) = @_;
6775 $jobs = {} if !$jobs;
6779 $jobs->{"drive-$drive"} = {};
6781 if ($dst_volid =~ /^nbd:/) {
6782 $qemu_target = $dst_volid;
6785 my $storecfg = PVE
::Storage
::config
();
6786 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid);
6788 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
6790 $format = qemu_img_format
($dst_scfg, $dst_volname);
6792 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
6794 $qemu_target = $is_zero_initialized ?
"zeroinit:$dst_path" : $dst_path;
6797 my $opts = { timeout
=> 10, device
=> "drive-$drive", mode
=> "existing", sync
=> "full", target
=> $qemu_target };
6798 $opts->{format
} = $format if $format;
6800 if (defined($bwlimit)) {
6801 $opts->{speed
} = $bwlimit * 1024;
6802 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
6804 print "drive mirror is starting for drive-$drive\n";
6807 # if a job already runs for this device we get an error, catch it for cleanup
6808 eval { mon_cmd
($vmid, "drive-mirror", %$opts); };
6810 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
6812 die "mirroring error: $err\n";
6815 qemu_drive_mirror_monitor
($vmid, $vmiddst, $jobs, $skipcomplete, $qga);
6818 sub qemu_drive_mirror_monitor
{
6819 my ($vmid, $vmiddst, $jobs, $skipcomplete, $qga) = @_;
6822 my $err_complete = 0;
6825 die "storage migration timed out\n" if $err_complete > 300;
6827 my $stats = mon_cmd
($vmid, "query-block-jobs");
6829 my $running_mirror_jobs = {};
6830 foreach my $stat (@$stats) {
6831 next if $stat->{type
} ne 'mirror';
6832 $running_mirror_jobs->{$stat->{device
}} = $stat;
6835 my $readycounter = 0;
6837 foreach my $job (keys %$jobs) {
6839 if(defined($jobs->{$job}->{complete
}) && !defined($running_mirror_jobs->{$job})) {
6840 print "$job : finished\n";
6841 delete $jobs->{$job};
6845 die "$job: mirroring has been cancelled\n" if !defined($running_mirror_jobs->{$job});
6847 my $busy = $running_mirror_jobs->{$job}->{busy
};
6848 my $ready = $running_mirror_jobs->{$job}->{ready
};
6849 if (my $total = $running_mirror_jobs->{$job}->{len
}) {
6850 my $transferred = $running_mirror_jobs->{$job}->{offset
} || 0;
6851 my $remaining = $total - $transferred;
6852 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
6854 print "$job: transferred: $transferred bytes remaining: $remaining bytes total: $total bytes progression: $percent % busy: $busy ready: $ready \n";
6857 $readycounter++ if $running_mirror_jobs->{$job}->{ready
};
6860 last if scalar(keys %$jobs) == 0;
6862 if ($readycounter == scalar(keys %$jobs)) {
6863 print "all mirroring jobs are ready \n";
6864 last if $skipcomplete; #do the complete later
6866 if ($vmiddst && $vmiddst != $vmid) {
6867 my $agent_running = $qga && qga_check_running
($vmid);
6868 if ($agent_running) {
6869 print "freeze filesystem\n";
6870 eval { mon_cmd
($vmid, "guest-fsfreeze-freeze"); };
6872 print "suspend vm\n";
6873 eval { PVE
::QemuServer
::vm_suspend
($vmid, 1); };
6876 # if we clone a disk for a new target vm, we don't switch the disk
6877 PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs);
6879 if ($agent_running) {
6880 print "unfreeze filesystem\n";
6881 eval { mon_cmd
($vmid, "guest-fsfreeze-thaw"); };
6883 print "resume vm\n";
6884 eval { PVE
::QemuServer
::vm_resume
($vmid, 1, 1); };
6890 foreach my $job (keys %$jobs) {
6891 # try to switch the disk if source and destination are on the same guest
6892 print "$job: Completing block job...\n";
6894 eval { mon_cmd
($vmid, "block-job-complete", device
=> $job) };
6895 if ($@ =~ m/cannot be completed/) {
6896 print "$job: Block job cannot be completed, try again.\n";
6899 print "$job: Completed successfully.\n";
6900 $jobs->{$job}->{complete
} = 1;
6911 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
6912 die "mirroring error: $err";
6917 sub qemu_blockjobs_cancel
{
6918 my ($vmid, $jobs) = @_;
6920 foreach my $job (keys %$jobs) {
6921 print "$job: Cancelling block job\n";
6922 eval { mon_cmd
($vmid, "block-job-cancel", device
=> $job); };
6923 $jobs->{$job}->{cancel
} = 1;
6927 my $stats = mon_cmd
($vmid, "query-block-jobs");
6929 my $running_jobs = {};
6930 foreach my $stat (@$stats) {
6931 $running_jobs->{$stat->{device
}} = $stat;
6934 foreach my $job (keys %$jobs) {
6936 if (defined($jobs->{$job}->{cancel
}) && !defined($running_jobs->{$job})) {
6937 print "$job: Done.\n";
6938 delete $jobs->{$job};
6942 last if scalar(keys %$jobs) == 0;
6949 my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
6950 $newvmid, $storage, $format, $full, $newvollist, $jobs, $skipcomplete, $qga, $bwlimit) = @_;
6955 print "create linked clone of drive $drivename ($drive->{file})\n";
6956 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $drive->{file
}, $newvmid, $snapname);
6957 push @$newvollist, $newvolid;
6960 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
6961 $storeid = $storage if $storage;
6963 my $dst_format = resolve_dst_disk_format
($storecfg, $storeid, $volname, $format);
6964 my ($size) = PVE
::Storage
::volume_size_info
($storecfg, $drive->{file
}, 3);
6966 print "create full clone of drive $drivename ($drive->{file})\n";
6968 if (drive_is_cloudinit
($drive)) {
6969 $name = "vm-$newvmid-cloudinit";
6970 $name .= ".$dst_format" if $dst_format ne 'raw';
6972 $size = PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
;
6974 $newvolid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024));
6975 push @$newvollist, $newvolid;
6977 PVE
::Storage
::activate_volumes
($storecfg, [$newvolid]);
6979 if (drive_is_cloudinit
($drive)) {
6983 my $sparseinit = PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $newvolid);
6984 if (!$running || $snapname) {
6985 # TODO: handle bwlimits
6986 qemu_img_convert
($drive->{file
}, $newvolid, $size, $snapname, $sparseinit);
6989 my $kvmver = get_running_qemu_version
($vmid);
6990 if (!qemu_machine_feature_enabled
(undef, $kvmver, 2, 7)) {
6991 die "drive-mirror with iothread requires qemu version 2.7 or higher\n"
6992 if $drive->{iothread
};
6995 qemu_drive_mirror
($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs, $skipcomplete, $qga, $bwlimit);
7000 my ($size) = PVE
::Storage
::volume_size_info
($storecfg, $newvolid, 3);
7003 $disk->{format
} = undef;
7004 $disk->{file
} = $newvolid;
7005 $disk->{size
} = $size;
7010 # this only works if VM is running
7011 sub get_current_qemu_machine
{
7014 my $res = mon_cmd
($vmid, "query-machines");
7016 my ($current, $default);
7017 foreach my $e (@$res) {
7018 $default = $e->{name
} if $e->{'is-default'};
7019 $current = $e->{name
} if $e->{'is-current'};
7022 # fallback to the default machine if current is not supported by qemu
7023 return $current || $default || 'pc';
7026 sub get_running_qemu_version
{
7028 my $res = mon_cmd
($vmid, "query-version");
7029 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
7032 sub qemu_machine_feature_enabled
{
7033 my ($machine, $kvmver, $version_major, $version_minor) = @_;
7038 if ($machine && $machine =~ m/^((?:pc(-i440fx|-q35)?|virt)-(\d+)\.(\d+))/) {
7040 $current_major = $3;
7041 $current_minor = $4;
7043 } elsif ($kvmver =~ m/^(\d+)\.(\d+)/) {
7045 $current_major = $1;
7046 $current_minor = $2;
7049 return 1 if version_cmp
($current_major, $version_major, $current_minor, $version_minor) >= 0;
7052 # gets in pairs the versions you want to compares, i.e.:
7053 # ($a-major, $b-major, $a-minor, $b-minor, $a-extra, $b-extra, ...)
7054 # returns 0 if same, -1 if $a is older than $b, +1 if $a is newer than $b
7058 my $size = scalar(@versions);
7060 return 0 if $size == 0;
7061 die "cannot compare odd count of versions" if $size & 1;
7063 for (my $i = 0; $i < $size; $i += 2) {
7064 my ($a, $b) = splice(@versions, 0, 2);
7068 return 1 if $a > $b;
7069 return -1 if $a < $b;
7074 # dies if a) VM not running or not exisiting b) Version query failed
7075 # So, any defined return value is valid, any invalid state can be caught by eval
7076 sub runs_at_least_qemu_version
{
7077 my ($vmid, $major, $minor, $extra) = @_;
7079 my $v = mon_cmd
($vmid, "query-version");
7080 die "could not query currently running version for VM $vmid\n" if !defined($v);
7083 return version_cmp
($v->{major
}, $major, $v->{minor
}, $minor, $v->{micro
}, $extra) >= 0;
7086 sub qemu_machine_pxe
{
7087 my ($vmid, $conf) = @_;
7089 my $machine = PVE
::QemuServer
::get_current_qemu_machine
($vmid);
7091 if ($conf->{machine
} && $conf->{machine
} =~ m/\.pxe$/) {
7098 sub qemu_use_old_bios_files
{
7099 my ($machine_type) = @_;
7101 return if !$machine_type;
7103 my $use_old_bios_files = undef;
7105 if ($machine_type =~ m/^(\S+)\.pxe$/) {
7107 $use_old_bios_files = 1;
7109 my $kvmver = kvm_user_version
();
7110 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
7111 # load new efi bios files on migration. So this hack is required to allow
7112 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
7113 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
7114 $use_old_bios_files = !qemu_machine_feature_enabled
($machine_type, $kvmver, 2, 4);
7117 return ($use_old_bios_files, $machine_type);
7120 sub create_efidisk
($$$$$) {
7121 my ($storecfg, $storeid, $vmid, $fmt, $arch) = @_;
7123 my (undef, $ovmf_vars) = get_ovmf_files
($arch);
7124 die "EFI vars default image not found\n" if ! -f
$ovmf_vars;
7126 my $vars_size_b = -s
$ovmf_vars;
7127 my $vars_size = PVE
::Tools
::convert_size
($vars_size_b, 'b' => 'kb');
7128 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
7129 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
7131 qemu_img_convert
($ovmf_vars, $volid, $vars_size_b, undef, 0);
7133 return ($volid, $vars_size);
7136 sub vm_iothreads_list
{
7139 my $res = mon_cmd
($vmid, 'query-iothreads');
7142 foreach my $iothread (@$res) {
7143 $iothreads->{ $iothread->{id
} } = $iothread->{"thread-id"};
7150 my ($conf, $drive) = @_;
7154 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)) {
7156 } elsif ($conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single')) {
7162 my $controller = int($drive->{index} / $maxdev);
7163 my $controller_prefix = ($conf->{scsihw
} && $conf->{scsihw
} eq 'virtio-scsi-single') ?
"virtioscsi" : "scsihw";
7165 return ($maxdev, $controller, $controller_prefix);
7168 sub add_hyperv_enlightenments
{
7169 my ($cpuFlags, $winversion, $machine_type, $kvmver, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
7171 return if $winversion < 6;
7172 return if $bios && $bios eq 'ovmf' && $winversion < 8;
7174 if ($gpu_passthrough || defined($hv_vendor_id)) {
7175 $hv_vendor_id //= 'proxmox';
7176 push @$cpuFlags , "hv_vendor_id=$hv_vendor_id";
7179 if (qemu_machine_feature_enabled
($machine_type, $kvmver, 2, 3)) {
7180 push @$cpuFlags , 'hv_spinlocks=0x1fff';
7181 push @$cpuFlags , 'hv_vapic';
7182 push @$cpuFlags , 'hv_time';
7184 push @$cpuFlags , 'hv_spinlocks=0xffff';
7187 if (qemu_machine_feature_enabled
($machine_type, $kvmver, 2, 6)) {
7188 push @$cpuFlags , 'hv_reset';
7189 push @$cpuFlags , 'hv_vpindex';
7190 push @$cpuFlags , 'hv_runtime';
7193 if ($winversion >= 7) {
7194 push @$cpuFlags , 'hv_relaxed';
7196 if (qemu_machine_feature_enabled
($machine_type, $kvmver, 2, 12)) {
7197 push @$cpuFlags , 'hv_synic';
7198 push @$cpuFlags , 'hv_stimer';
7201 if (qemu_machine_feature_enabled
($machine_type, $kvmver, 3, 1)) {
7202 push @$cpuFlags , 'hv_ipi';
7207 sub windows_version
{
7210 return 0 if !$ostype;
7214 if($ostype eq 'wxp' || $ostype eq 'w2k3' || $ostype eq 'w2k') {
7216 } elsif($ostype eq 'w2k8' || $ostype eq 'wvista') {
7218 } elsif ($ostype =~ m/^win(\d+)$/) {
7225 sub resolve_dst_disk_format
{
7226 my ($storecfg, $storeid, $src_volname, $format) = @_;
7227 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
7230 # if no target format is specified, use the source disk format as hint
7232 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7233 $format = qemu_img_format
($scfg, $src_volname);
7239 # test if requested format is supported - else use default
7240 my $supported = grep { $_ eq $format } @$validFormats;
7241 $format = $defFormat if !$supported;
7245 sub resolve_first_disk
{
7247 my @disks = PVE
::QemuServer
::valid_drive_names
();
7249 foreach my $ds (reverse @disks) {
7250 next if !$conf->{$ds};
7251 my $disk = PVE
::QemuServer
::parse_drive
($ds, $conf->{$ds});
7252 next if PVE
::QemuServer
::drive_is_cdrom
($disk);
7259 my ($uuid, $uuid_str);
7260 UUID
::generate
($uuid);
7261 UUID
::unparse
($uuid, $uuid_str);
7265 sub generate_smbios1_uuid
{
7266 return "uuid=".generate_uuid
();
7272 mon_cmd
($vmid, 'nbd-server-stop');
7275 sub create_reboot_request
{
7277 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
7278 or die "failed to create reboot trigger file: $!\n";
7282 sub clear_reboot_request
{
7284 my $path = "/run/qemu-server/$vmid.reboot";
7287 $res = unlink($path);
7288 die "could not remove reboot request for $vmid: $!"
7289 if !$res && $! != POSIX
::ENOENT
;
7294 # bash completion helper
7296 sub complete_backup_archives
{
7297 my ($cmdname, $pname, $cvalue) = @_;
7299 my $cfg = PVE
::Storage
::config
();
7303 if ($cvalue =~ m/^([^:]+):/) {
7307 my $data = PVE
::Storage
::template_list
($cfg, $storeid, 'backup');
7310 foreach my $id (keys %$data) {
7311 foreach my $item (@{$data->{$id}}) {
7312 next if $item->{format
} !~ m/^vma\.(gz|lzo)$/;
7313 push @$res, $item->{volid
} if defined($item->{volid
});
7320 my $complete_vmid_full = sub {
7323 my $idlist = vmstatus
();
7327 foreach my $id (keys %$idlist) {
7328 my $d = $idlist->{$id};
7329 if (defined($running)) {
7330 next if $d->{template
};
7331 next if $running && $d->{status
} ne 'running';
7332 next if !$running && $d->{status
} eq 'running';
7341 return &$complete_vmid_full();
7344 sub complete_vmid_stopped
{
7345 return &$complete_vmid_full(0);
7348 sub complete_vmid_running
{
7349 return &$complete_vmid_full(1);
7352 sub complete_storage
{
7354 my $cfg = PVE
::Storage
::config
();
7355 my $ids = $cfg->{ids
};
7358 foreach my $sid (keys %$ids) {
7359 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, undef, 1);
7360 next if !$ids->{$sid}->{content
}->{images
};
7367 sub complete_migration_storage
{
7368 my ($cmd, $param, $current_value, $all_args) = @_;
7370 my $targetnode = @$all_args[1];
7372 my $cfg = PVE
::Storage
::config
();
7373 my $ids = $cfg->{ids
};
7376 foreach my $sid (keys %$ids) {
7377 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, $targetnode, 1);
7378 next if !$ids->{$sid}->{content
}->{images
};