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
qw(min_version);
45 use PVE
::QemuServer
::Cloudinit
;
46 use PVE
::QemuServer
::Machine
;
47 use PVE
::QemuServer
::Memory
;
48 use PVE
::QemuServer
::Monitor
qw(mon_cmd);
49 use PVE
::QemuServer
::PCI
qw(print_pci_addr print_pcie_addr print_pcie_root_port);
50 use PVE
::QemuServer
::USB
qw(parse_usb_device);
52 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
55 "$EDK2_FW_BASE/OVMF_CODE.fd",
56 "$EDK2_FW_BASE/OVMF_VARS.fd"
59 "$EDK2_FW_BASE/AAVMF_CODE.fd",
60 "$EDK2_FW_BASE/AAVMF_VARS.fd"
64 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
66 my $QEMU_FORMAT_RE = qr/raw|cow|qcow|qcow2|qed|vmdk|cloop/;
68 # Note about locking: we use flock on the config file protect
69 # against concurent actions.
70 # Aditionaly, we have a 'lock' setting in the config file. This
71 # can be set to 'migrate', 'backup', 'snapshot' or 'rollback'. Most actions are not
72 # allowed when such lock is set. But you can ignore this kind of
73 # lock with the --skiplock flag.
75 cfs_register_file
('/qemu-server/',
79 PVE
::JSONSchema
::register_standard_option
('pve-qm-stateuri', {
80 description
=> "Some command save/restore state from this location.",
86 PVE
::JSONSchema
::register_standard_option
('pve-qm-image-format', {
88 enum
=> [qw(raw cow qcow qed qcow2 vmdk cloop)],
89 description
=> "The drive's backing file's data format.",
93 PVE
::JSONSchema
::register_standard_option
('pve-qemu-machine', {
94 description
=> "Specifies the Qemu machine type.",
96 pattern
=> '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
101 #no warnings 'redefine';
104 my ($controller, $vmid, $option, $value) = @_;
106 my $path = "/sys/fs/cgroup/$controller/qemu.slice/$vmid.scope/$option";
107 PVE
::ProcFSTools
::write_proc_entry
($path, $value);
111 my $nodename = PVE
::INotify
::nodename
();
113 my $cpu_vendor_list = {
115 486 => 'GenuineIntel',
116 pentium
=> 'GenuineIntel',
117 pentium2
=> 'GenuineIntel',
118 pentium3
=> 'GenuineIntel',
119 coreduo
=> 'GenuineIntel',
120 core2duo
=> 'GenuineIntel',
121 Conroe
=> 'GenuineIntel',
122 Penryn
=> 'GenuineIntel',
123 Nehalem
=> 'GenuineIntel',
124 'Nehalem-IBRS' => 'GenuineIntel',
125 Westmere
=> 'GenuineIntel',
126 'Westmere-IBRS' => 'GenuineIntel',
127 SandyBridge
=> 'GenuineIntel',
128 'SandyBridge-IBRS' => 'GenuineIntel',
129 IvyBridge
=> 'GenuineIntel',
130 'IvyBridge-IBRS' => 'GenuineIntel',
131 Haswell
=> 'GenuineIntel',
132 'Haswell-IBRS' => 'GenuineIntel',
133 'Haswell-noTSX' => 'GenuineIntel',
134 'Haswell-noTSX-IBRS' => 'GenuineIntel',
135 Broadwell
=> 'GenuineIntel',
136 'Broadwell-IBRS' => 'GenuineIntel',
137 'Broadwell-noTSX' => 'GenuineIntel',
138 'Broadwell-noTSX-IBRS' => 'GenuineIntel',
139 'Skylake-Client' => 'GenuineIntel',
140 'Skylake-Client-IBRS' => 'GenuineIntel',
141 'Skylake-Server' => 'GenuineIntel',
142 'Skylake-Server-IBRS' => 'GenuineIntel',
143 'Cascadelake-Server' => 'GenuineIntel',
144 KnightsMill
=> 'GenuineIntel',
148 athlon
=> 'AuthenticAMD',
149 phenom
=> 'AuthenticAMD',
150 Opteron_G1
=> 'AuthenticAMD',
151 Opteron_G2
=> 'AuthenticAMD',
152 Opteron_G3
=> 'AuthenticAMD',
153 Opteron_G4
=> 'AuthenticAMD',
154 Opteron_G5
=> 'AuthenticAMD',
155 EPYC
=> 'AuthenticAMD',
156 'EPYC-IBPB' => 'AuthenticAMD',
158 # generic types, use vendor from host node
167 my @supported_cpu_flags = (
181 my $cpu_flag = qr/[+-](@{[join('|', @supported_cpu_flags)]})/;
185 description
=> "Emulated CPU type.",
187 enum
=> [ sort { "\L$a" cmp "\L$b" } keys %$cpu_vendor_list ],
192 description
=> "Do not identify as a KVM virtual machine.",
199 pattern
=> qr/[a-zA-Z0-9]{1,12}/,
200 format_description
=> 'vendor-id',
201 description
=> 'The Hyper-V vendor ID. Some drivers or programs inside Windows guests need a specific ID.',
205 description
=> "List of additional CPU flags separated by ';'."
206 . " Use '+FLAG' to enable, '-FLAG' to disable a flag."
207 . " Currently supported flags: @{[join(', ', @supported_cpu_flags)]}.",
208 format_description
=> '+FLAG[;-FLAG...]',
210 pattern
=> qr/$cpu_flag(;$cpu_flag)*/,
219 enum
=> [qw(i6300esb ib700)],
220 description
=> "Watchdog type to emulate.",
221 default => 'i6300esb',
226 enum
=> [qw(reset shutdown poweroff pause debug none)],
227 description
=> "The action to perform if after activation the guest fails to poll the watchdog in time.",
231 PVE
::JSONSchema
::register_format
('pve-qm-watchdog', $watchdog_fmt);
235 description
=> "Enable/disable Qemu GuestAgent.",
240 fstrim_cloned_disks
=> {
241 description
=> "Run fstrim after cloning/moving a disk.",
247 description
=> "Select the agent type",
251 enum
=> [qw(virtio isa)],
257 description
=> "Select the VGA type.",
262 enum
=> [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio vmware)],
265 description
=> "Sets the VGA memory (in MiB). Has no effect with serial display.",
277 description
=> "The size of the file in MB.",
281 pattern
=> '[a-zA-Z0-9\-]+',
283 format_description
=> 'string',
284 description
=> "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
291 enum
=> [qw(ich9-intel-hda intel-hda AC97)],
292 description
=> "Configure an audio device."
299 description
=> "Driver backend for the audio device."
303 my $spice_enhancements_fmt = {
308 description
=> "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
312 enum
=> ['off', 'all', 'filter'],
315 description
=> "Enable video streaming. Uses compression for detected video streams."
323 description
=> "Specifies whether a VM will be started during system bootup.",
329 description
=> "Automatic restart after crash (currently ignored).",
334 type
=> 'string', format
=> 'pve-hotplug-features',
335 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'.",
336 default => 'network,disk,usb',
341 description
=> "Allow reboot. If set to '0' the VM exit on reboot.",
347 description
=> "Lock/unlock the VM.",
348 enum
=> [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
353 description
=> "Limit of CPU usage.",
354 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.",
362 description
=> "CPU weight for a VM.",
363 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.",
371 description
=> "Amount of RAM for the VM in MB. This is the maximum available memory when you use the balloon device.",
378 description
=> "Amount of target RAM for the VM in MB. Using zero disables the ballon driver.",
384 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.",
392 description
=> "Keybord layout for vnc server. Default is read from the '/etc/pve/datacenter.cfg' configuration file.".
393 "It should not be necessary to set it.",
394 enum
=> PVE
::Tools
::kvmkeymaplist
(),
399 type
=> 'string', format
=> 'dns-name',
400 description
=> "Set a name for the VM. Only used on the configuration web interface.",
405 description
=> "SCSI controller model",
406 enum
=> [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
412 description
=> "Description for the VM. Only used on the configuration web interface. This is saved as comment inside the configuration file.",
417 enum
=> [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 l24 l26 solaris)],
418 description
=> "Specify guest operating system.",
419 verbose_description
=> <<EODESC,
420 Specify guest operating system. This is used to enable special
421 optimization/features for specific operating systems:
424 other;; unspecified OS
425 wxp;; Microsoft Windows XP
426 w2k;; Microsoft Windows 2000
427 w2k3;; Microsoft Windows 2003
428 w2k8;; Microsoft Windows 2008
429 wvista;; Microsoft Windows Vista
430 win7;; Microsoft Windows 7
431 win8;; Microsoft Windows 8/2012/2012r2
432 win10;; Microsoft Windows 10/2016
433 l24;; Linux 2.4 Kernel
434 l26;; Linux 2.6 - 5.X Kernel
435 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
441 description
=> "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n).",
442 pattern
=> '[acdn]{1,4}',
447 type
=> 'string', format
=> 'pve-qm-bootdisk',
448 description
=> "Enable booting from specified disk.",
449 pattern
=> '(ide|sata|scsi|virtio)\d+',
454 description
=> "The number of CPUs. Please use option -sockets instead.",
461 description
=> "The number of CPU sockets.",
468 description
=> "The number of cores per socket.",
475 description
=> "Enable/disable NUMA.",
481 description
=> "Enable/disable hugepages memory.",
482 enum
=> [qw(any 2 1024)],
487 description
=> "Number of hotplugged vcpus.",
494 description
=> "Enable/disable ACPI.",
499 description
=> "Enable/disable Qemu GuestAgent and its properties.",
501 format
=> $agent_fmt,
506 description
=> "Enable/disable KVM hardware virtualization.",
512 description
=> "Enable/disable time drift fix.",
518 description
=> "Set the real time clock to local time. This is enabled by default if ostype indicates a Microsoft OS.",
523 description
=> "Freeze CPU at startup (use 'c' monitor command to start execution).",
527 type
=> 'string', format
=> $vga_fmt,
528 description
=> "Configure the VGA hardware.",
529 verbose_description
=> "Configure the VGA Hardware. If you want to use ".
530 "high resolution modes (>= 1280x1024x16) you may need to increase " .
531 "the vga memory option. Since QEMU 2.9 the default VGA display type " .
532 "is 'std' for all OS types besides some Windows versions (XP and " .
533 "older) which use 'cirrus'. The 'qxl' option enables the SPICE " .
534 "display server. For win* OS you can select how many independent " .
535 "displays you want, Linux guests can add displays them self.\n".
536 "You can also run without any graphic card, using a serial device as terminal.",
540 type
=> 'string', format
=> 'pve-qm-watchdog',
541 description
=> "Create a virtual hardware watchdog device.",
542 verbose_description
=> "Create a virtual hardware watchdog device. Once enabled" .
543 " (by a guest action), the watchdog must be periodically polled " .
544 "by an agent inside the guest or else the watchdog will reset " .
545 "the guest (or execute the respective action specified)",
550 typetext
=> "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
551 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'.",
552 pattern
=> '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
555 startup
=> get_standard_option
('pve-startup-order'),
559 description
=> "Enable/disable Template.",
565 description
=> "Arbitrary arguments passed to kvm.",
566 verbose_description
=> <<EODESCR,
567 Arbitrary arguments passed to kvm, for example:
569 args: -no-reboot -no-hpet
571 NOTE: this option is for experts only.
578 description
=> "Enable/disable the USB tablet device.",
579 verbose_description
=> "Enable/disable the USB tablet device. This device is " .
580 "usually needed to allow absolute mouse positioning with VNC. " .
581 "Else the mouse runs out of sync with normal VNC clients. " .
582 "If you're running lots of console-only guests on one host, " .
583 "you may consider disabling this to save some context switches. " .
584 "This is turned off by default if you use spice (-vga=qxl).",
589 description
=> "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
593 migrate_downtime
=> {
596 description
=> "Set maximum tolerated downtime (in seconds) for migrations.",
602 type
=> 'string', format
=> 'pve-qm-ide',
603 typetext
=> '<volume>',
604 description
=> "This is an alias for option -ide2",
608 description
=> "Emulated CPU type.",
612 parent
=> get_standard_option
('pve-snapshot-name', {
614 description
=> "Parent snapshot name. This is used internally, and should not be modified.",
618 description
=> "Timestamp for snapshots.",
624 type
=> 'string', format
=> 'pve-volume-id',
625 description
=> "Reference to a volume which stores the VM state. This is used internally for snapshots.",
627 vmstatestorage
=> get_standard_option
('pve-storage-id', {
628 description
=> "Default storage for VM state volumes/files.",
631 runningmachine
=> get_standard_option
('pve-qemu-machine', {
632 description
=> "Specifies the Qemu machine type of the running vm. This is used internally for snapshots.",
634 machine
=> get_standard_option
('pve-qemu-machine'),
636 description
=> "Virtual processor architecture. Defaults to the host.",
639 enum
=> [qw(x86_64 aarch64)],
642 description
=> "Specify SMBIOS type 1 fields.",
643 type
=> 'string', format
=> 'pve-qm-smbios1',
650 description
=> "Sets the protection flag of the VM. This will disable the remove VM and remove disk operations.",
656 enum
=> [ qw(seabios ovmf) ],
657 description
=> "Select BIOS implementation.",
658 default => 'seabios',
662 pattern
=> '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
663 format_description
=> 'UUID',
664 description
=> "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0' to disable explicitly.",
665 verbose_description
=> "The VM generation ID (vmgenid) device exposes a".
666 " 128-bit integer value identifier to the guest OS. This allows to".
667 " notify the guest operating system when the virtual machine is".
668 " executed with a different configuration (e.g. snapshot execution".
669 " or creation from a template). The guest operating system notices".
670 " the change, and is then able to react as appropriate by marking".
671 " its copies of distributed databases as dirty, re-initializing its".
672 " random number generator, etc.\n".
673 "Note that auto-creation only works when done throug API/CLI create".
674 " or update methods, but not when manually editing the config file.",
675 default => "1 (autogenerated)",
680 format
=> 'pve-volume-id',
682 description
=> "Script that will be executed during various steps in the vms lifetime.",
686 format
=> $ivshmem_fmt,
687 description
=> "Inter-VM shared memory. Useful for direct communication between VMs, or to the host.",
692 format
=> $audio_fmt,
693 description
=> "Configure a audio device, useful in combination with QXL/Spice.",
696 spice_enhancements
=> {
698 format
=> $spice_enhancements_fmt,
699 description
=> "Configure additional enhancements for SPICE.",
703 type
=> 'string', format
=> 'pve-tag-list',
704 description
=> 'Tags of the VM. This is only meta information.',
713 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.',
714 format
=> 'pve-volume-id',
715 format_description
=> 'volume',
720 description
=> 'Specify a custom file containing all network data passed to the VM via cloud-init.',
721 format
=> 'pve-volume-id',
722 format_description
=> 'volume',
727 description
=> 'Specify a custom file containing all user data passed to the VM via cloud-init.',
728 format
=> 'pve-volume-id',
729 format_description
=> 'volume',
732 PVE
::JSONSchema
::register_format
('pve-qm-cicustom', $cicustom_fmt);
734 my $confdesc_cloudinit = {
738 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.',
739 enum
=> ['configdrive2', 'nocloud'],
744 description
=> "cloud-init: User name to change ssh keys and password for instead of the image's configured default user.",
749 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.',
754 description
=> 'cloud-init: Specify custom files to replace the automatically generated ones at start.',
755 format
=> 'pve-qm-cicustom',
760 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.",
764 type
=> 'string', format
=> 'address-list',
765 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.",
770 format
=> 'urlencoded',
771 description
=> "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
775 # what about other qemu settings ?
777 #machine => 'string',
790 ##soundhw => 'string',
792 while (my ($k, $v) = each %$confdesc) {
793 PVE
::JSONSchema
::register_standard_option
("pve-qm-$k", $v);
796 my $MAX_IDE_DISKS = 4;
797 my $MAX_SCSI_DISKS = 14;
798 my $MAX_VIRTIO_DISKS = 16;
799 my $MAX_SATA_DISKS = 6;
800 my $MAX_USB_DEVICES = 5;
802 my $MAX_UNUSED_DISKS = 256;
803 my $MAX_HOSTPCI_DEVICES = 16;
804 my $MAX_SERIAL_PORTS = 4;
805 my $MAX_PARALLEL_PORTS = 3;
811 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
812 description
=> "CPUs accessing this NUMA node.",
813 format_description
=> "id[-id];...",
817 description
=> "Amount of memory this NUMA node provides.",
822 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
823 description
=> "Host NUMA nodes to use.",
824 format_description
=> "id[-id];...",
829 enum
=> [qw(preferred bind interleave)],
830 description
=> "NUMA allocation policy.",
834 PVE
::JSONSchema
::register_format
('pve-qm-numanode', $numa_fmt);
837 type
=> 'string', format
=> $numa_fmt,
838 description
=> "NUMA topology.",
840 PVE
::JSONSchema
::register_standard_option
("pve-qm-numanode", $numadesc);
842 for (my $i = 0; $i < $MAX_NUMA; $i++) {
843 $confdesc->{"numa$i"} = $numadesc;
846 my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000', 'pcnet', 'virtio',
847 'ne2k_isa', 'i82551', 'i82557b', 'i82559er', 'vmxnet3',
848 'e1000-82540em', 'e1000-82544gc', 'e1000-82545em'];
849 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
851 my $net_fmt_bridge_descr = <<__EOD__;
852 Bridge to attach the network device to. The Proxmox VE standard bridge
855 If you do not specify a bridge, we create a kvm user (NATed) network
856 device, which provides DHCP and DNS services. The following addresses
863 The DHCP server assign addresses to the guest starting from 10.0.2.15.
867 macaddr
=> get_standard_option
('mac-addr', {
868 description
=> "MAC address. That address must be unique withing your network. This is automatically generated if not specified.",
872 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'.",
873 enum
=> $nic_model_list,
876 (map { $_ => { keyAlias
=> 'model', alias
=> 'macaddr' }} @$nic_model_list),
879 description
=> $net_fmt_bridge_descr,
880 format_description
=> 'bridge',
885 minimum
=> 0, maximum
=> 16,
886 description
=> 'Number of packet queues to be used on the device.',
892 description
=> "Rate limit in mbps (megabytes per second) as floating point number.",
897 minimum
=> 1, maximum
=> 4094,
898 description
=> 'VLAN tag to apply to packets on this interface.',
903 pattern
=> qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
904 description
=> 'VLAN trunks to pass through this interface.',
905 format_description
=> 'vlanid[;vlanid...]',
910 description
=> 'Whether this interface should be protected by the firewall.',
915 description
=> 'Whether this interface should be disconnected (like pulling the plug).',
922 type
=> 'string', format
=> $net_fmt,
923 description
=> "Specify network devices.",
926 PVE
::JSONSchema
::register_standard_option
("pve-qm-net", $netdesc);
931 format
=> 'pve-ipv4-config',
932 format_description
=> 'IPv4Format/CIDR',
933 description
=> 'IPv4 address in CIDR format.',
940 format_description
=> 'GatewayIPv4',
941 description
=> 'Default gateway for IPv4 traffic.',
947 format
=> 'pve-ipv6-config',
948 format_description
=> 'IPv6Format/CIDR',
949 description
=> 'IPv6 address in CIDR format.',
956 format_description
=> 'GatewayIPv6',
957 description
=> 'Default gateway for IPv6 traffic.',
962 PVE
::JSONSchema
::register_format
('pve-qm-ipconfig', $ipconfig_fmt);
965 type
=> 'string', format
=> 'pve-qm-ipconfig',
966 description
=> <<'EODESCR',
967 cloud-init: Specify IP addresses and gateways for the corresponding interface.
969 IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
971 The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit gateway should be provided.
972 For IPv6 the special string 'auto' can be used to use stateless autoconfiguration.
974 If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using dhcp on IPv4.
977 PVE
::JSONSchema
::register_standard_option
("pve-qm-ipconfig", $netdesc);
979 for (my $i = 0; $i < $MAX_NETS; $i++) {
980 $confdesc->{"net$i"} = $netdesc;
981 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
984 foreach my $key (keys %$confdesc_cloudinit) {
985 $confdesc->{$key} = $confdesc_cloudinit->{$key};
988 PVE
::JSONSchema
::register_format
('pve-volume-id-or-qm-path', \
&verify_volume_id_or_qm_path
);
989 sub verify_volume_id_or_qm_path
{
990 my ($volid, $noerr) = @_;
992 if ($volid eq 'none' || $volid eq 'cdrom' || $volid =~ m
|^/|) {
996 # if its neither 'none' nor 'cdrom' nor a path, check if its a volume-id
997 $volid = eval { PVE
::JSONSchema
::check_format
('pve-volume-id', $volid, '') };
999 return undef if $noerr;
1007 my %drivedesc_base = (
1008 volume
=> { alias
=> 'file' },
1011 format
=> 'pve-volume-id-or-qm-path',
1013 format_description
=> 'volume',
1014 description
=> "The drive's backing volume.",
1018 enum
=> [qw(cdrom disk)],
1019 description
=> "The drive's media type.",
1025 description
=> "Force the drive's physical geometry to have a specific cylinder count.",
1030 description
=> "Force the drive's physical geometry to have a specific head count.",
1035 description
=> "Force the drive's physical geometry to have a specific sector count.",
1040 enum
=> [qw(none lba auto)],
1041 description
=> "Force disk geometry bios translation mode.",
1046 description
=> "Controls qemu's snapshot mode feature."
1047 . " If activated, changes made to the disk are temporary and will"
1048 . " be discarded when the VM is shutdown.",
1053 enum
=> [qw(none writethrough writeback unsafe directsync)],
1054 description
=> "The drive's cache mode",
1057 format
=> get_standard_option
('pve-qm-image-format'),
1060 format
=> 'disk-size',
1061 format_description
=> 'DiskSize',
1062 description
=> "Disk size. This is purely informational and has no effect.",
1067 description
=> "Whether the drive should be included when making backups.",
1072 description
=> 'Whether the drive should considered for replication jobs.',
1078 enum
=> [qw(ignore report stop)],
1079 description
=> 'Read error action.',
1084 enum
=> [qw(enospc ignore report stop)],
1085 description
=> 'Write error action.',
1090 enum
=> [qw(native threads)],
1091 description
=> 'AIO type to use.',
1096 enum
=> [qw(ignore on)],
1097 description
=> 'Controls whether to pass discard/trim requests to the underlying storage.',
1102 description
=> 'Controls whether to detect and try to optimize writes of zeroes.',
1107 format
=> 'urlencoded',
1108 format_description
=> 'serial',
1109 maxLength
=> 20*3, # *3 since it's %xx url enoded
1110 description
=> "The drive's reported serial number, url-encoded, up to 20 bytes long.",
1115 description
=> 'Mark this locally-managed volume as available on all nodes',
1116 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!",
1122 my %iothread_fmt = ( iothread
=> {
1124 description
=> "Whether to use iothreads for this drive",
1131 format
=> 'urlencoded',
1132 format_description
=> 'model',
1133 maxLength
=> 40*3, # *3 since it's %xx url enoded
1134 description
=> "The drive's reported model name, url-encoded, up to 40 bytes long.",
1142 description
=> "Number of queues.",
1148 my %scsiblock_fmt = (
1151 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",
1160 description
=> "Whether to expose this drive as an SSD, rather than a rotational hard disk.",
1168 pattern
=> qr/^(0x)[0-9a-fA-F]{16}/,
1169 format_description
=> 'wwn',
1170 description
=> "The drive's worldwide name, encoded as 16 bytes hex string, prefixed by '0x'.",
1175 my $add_throttle_desc = sub {
1176 my ($key, $type, $what, $unit, $longunit, $minimum) = @_;
1179 format_description
=> $unit,
1180 description
=> "Maximum $what in $longunit.",
1183 $d->{minimum
} = $minimum if defined($minimum);
1184 $drivedesc_base{$key} = $d;
1186 # throughput: (leaky bucket)
1187 $add_throttle_desc->('bps', 'integer', 'r/w speed', 'bps', 'bytes per second');
1188 $add_throttle_desc->('bps_rd', 'integer', 'read speed', 'bps', 'bytes per second');
1189 $add_throttle_desc->('bps_wr', 'integer', 'write speed', 'bps', 'bytes per second');
1190 $add_throttle_desc->('mbps', 'number', 'r/w speed', 'mbps', 'megabytes per second');
1191 $add_throttle_desc->('mbps_rd', 'number', 'read speed', 'mbps', 'megabytes per second');
1192 $add_throttle_desc->('mbps_wr', 'number', 'write speed', 'mbps', 'megabytes per second');
1193 $add_throttle_desc->('iops', 'integer', 'r/w I/O', 'iops', 'operations per second');
1194 $add_throttle_desc->('iops_rd', 'integer', 'read I/O', 'iops', 'operations per second');
1195 $add_throttle_desc->('iops_wr', 'integer', 'write I/O', 'iops', 'operations per second');
1197 # pools: (pool of IO before throttling starts taking effect)
1198 $add_throttle_desc->('mbps_max', 'number', 'unthrottled r/w pool', 'mbps', 'megabytes per second');
1199 $add_throttle_desc->('mbps_rd_max', 'number', 'unthrottled read pool', 'mbps', 'megabytes per second');
1200 $add_throttle_desc->('mbps_wr_max', 'number', 'unthrottled write pool', 'mbps', 'megabytes per second');
1201 $add_throttle_desc->('iops_max', 'integer', 'unthrottled r/w I/O pool', 'iops', 'operations per second');
1202 $add_throttle_desc->('iops_rd_max', 'integer', 'unthrottled read I/O pool', 'iops', 'operations per second');
1203 $add_throttle_desc->('iops_wr_max', 'integer', 'unthrottled write I/O pool', 'iops', 'operations per second');
1206 $add_throttle_desc->('bps_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
1207 $add_throttle_desc->('bps_rd_max_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
1208 $add_throttle_desc->('bps_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
1209 $add_throttle_desc->('iops_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
1210 $add_throttle_desc->('iops_rd_max_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
1211 $add_throttle_desc->('iops_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
1214 $drivedesc_base{'bps_rd_length'} = { alias
=> 'bps_rd_max_length' };
1215 $drivedesc_base{'bps_wr_length'} = { alias
=> 'bps_wr_max_length' };
1216 $drivedesc_base{'iops_rd_length'} = { alias
=> 'iops_rd_max_length' };
1217 $drivedesc_base{'iops_wr_length'} = { alias
=> 'iops_wr_max_length' };
1225 PVE
::JSONSchema
::register_format
("pve-qm-ide", $ide_fmt);
1229 type
=> 'string', format
=> $ide_fmt,
1230 description
=> "Use volume as IDE hard disk or CD-ROM (n is 0 to " .($MAX_IDE_DISKS -1) . ").",
1232 PVE
::JSONSchema
::register_standard_option
("pve-qm-ide", $idedesc);
1244 type
=> 'string', format
=> $scsi_fmt,
1245 description
=> "Use volume as SCSI hard disk or CD-ROM (n is 0 to " . ($MAX_SCSI_DISKS - 1) . ").",
1247 PVE
::JSONSchema
::register_standard_option
("pve-qm-scsi", $scsidesc);
1256 type
=> 'string', format
=> $sata_fmt,
1257 description
=> "Use volume as SATA hard disk or CD-ROM (n is 0 to " . ($MAX_SATA_DISKS - 1). ").",
1259 PVE
::JSONSchema
::register_standard_option
("pve-qm-sata", $satadesc);
1267 type
=> 'string', format
=> $virtio_fmt,
1268 description
=> "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . ").",
1270 PVE
::JSONSchema
::register_standard_option
("pve-qm-virtio", $virtiodesc);
1272 my $alldrive_fmt = {
1283 volume
=> { alias
=> 'file' },
1286 format
=> 'pve-volume-id-or-qm-path',
1288 format_description
=> 'volume',
1289 description
=> "The drive's backing volume.",
1291 format
=> get_standard_option
('pve-qm-image-format'),
1294 format
=> 'disk-size',
1295 format_description
=> 'DiskSize',
1296 description
=> "Disk size. This is purely informational and has no effect.",
1301 my $efidisk_desc = {
1303 type
=> 'string', format
=> $efidisk_fmt,
1304 description
=> "Configure a Disk for storing EFI vars",
1307 PVE
::JSONSchema
::register_standard_option
("pve-qm-efidisk", $efidisk_desc);
1312 type
=> 'string', format
=> 'pve-qm-usb-device',
1313 format_description
=> 'HOSTUSBDEVICE|spice',
1314 description
=> <<EODESCR,
1315 The Host USB device or port or the value 'spice'. HOSTUSBDEVICE syntax is:
1317 'bus-port(.port)*' (decimal numbers) or
1318 'vendor_id:product_id' (hexadeciaml numbers) or
1321 You can use the 'lsusb -t' command to list existing usb devices.
1323 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
1325 The value 'spice' can be used to add a usb redirection devices for spice.
1331 description
=> "Specifies whether if given host option is a USB3 device or port.",
1338 type
=> 'string', format
=> $usb_fmt,
1339 description
=> "Configure an USB device (n is 0 to 4).",
1341 PVE
::JSONSchema
::register_standard_option
("pve-qm-usb", $usbdesc);
1343 my $PCIRE = qr/([a-f0-9]{4}:)?[a-f0-9]{2}:[a-f0-9]{2}(?:\.[a-f0-9])?/;
1348 pattern
=> qr/$PCIRE(;$PCIRE)*/,
1349 format_description
=> 'HOSTPCIID[;HOSTPCIID2...]',
1350 description
=> <<EODESCR,
1351 Host PCI device pass through. The PCI ID of a host's PCI device or a list
1352 of PCI virtual functions of the host. HOSTPCIID syntax is:
1354 'bus:dev.func' (hexadecimal numbers)
1356 You can us the 'lspci' command to list existing PCI devices.
1361 description
=> "Specify whether or not the device's ROM will be visible in the guest's memory map.",
1367 pattern
=> '[^,;]+',
1368 format_description
=> 'string',
1369 description
=> "Custom pci device rom filename (must be located in /usr/share/kvm/).",
1374 description
=> "Choose the PCI-express bus (needs the 'q35' machine model).",
1380 description
=> "Enable vfio-vga device support.",
1386 format_description
=> 'string',
1387 pattern
=> '[^/\.:]+',
1389 description
=> <<EODESCR
1390 The type of mediated device to use.
1391 An instance of this type will be created on startup of the VM and
1392 will be cleaned up when the VM stops.
1396 PVE
::JSONSchema
::register_format
('pve-qm-hostpci', $hostpci_fmt);
1400 type
=> 'string', format
=> 'pve-qm-hostpci',
1401 description
=> "Map host PCI devices into guest.",
1402 verbose_description
=> <<EODESCR,
1403 Map host PCI devices into guest.
1405 NOTE: This option allows direct access to host hardware. So it is no longer
1406 possible to migrate such machines - use with special care.
1408 CAUTION: Experimental! User reported problems with this option.
1411 PVE
::JSONSchema
::register_standard_option
("pve-qm-hostpci", $hostpcidesc);
1416 pattern
=> '(/dev/.+|socket)',
1417 description
=> "Create a serial device inside the VM (n is 0 to 3)",
1418 verbose_description
=> <<EODESCR,
1419 Create a serial device inside the VM (n is 0 to 3), and pass through a
1420 host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1421 host side (use 'qm terminal' to open a terminal connection).
1423 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines - use with special care.
1425 CAUTION: Experimental! User reported problems with this option.
1432 pattern
=> '/dev/parport\d+|/dev/usb/lp\d+',
1433 description
=> "Map host parallel devices (n is 0 to 2).",
1434 verbose_description
=> <<EODESCR,
1435 Map host parallel devices (n is 0 to 2).
1437 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
1439 CAUTION: Experimental! User reported problems with this option.
1443 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1444 $confdesc->{"parallel$i"} = $paralleldesc;
1447 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1448 $confdesc->{"serial$i"} = $serialdesc;
1451 for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
1452 $confdesc->{"hostpci$i"} = $hostpcidesc;
1455 for (my $i = 0; $i < $MAX_IDE_DISKS; $i++) {
1456 $drivename_hash->{"ide$i"} = 1;
1457 $confdesc->{"ide$i"} = $idedesc;
1460 for (my $i = 0; $i < $MAX_SATA_DISKS; $i++) {
1461 $drivename_hash->{"sata$i"} = 1;
1462 $confdesc->{"sata$i"} = $satadesc;
1465 for (my $i = 0; $i < $MAX_SCSI_DISKS; $i++) {
1466 $drivename_hash->{"scsi$i"} = 1;
1467 $confdesc->{"scsi$i"} = $scsidesc ;
1470 for (my $i = 0; $i < $MAX_VIRTIO_DISKS; $i++) {
1471 $drivename_hash->{"virtio$i"} = 1;
1472 $confdesc->{"virtio$i"} = $virtiodesc;
1475 $drivename_hash->{efidisk0
} = 1;
1476 $confdesc->{efidisk0
} = $efidisk_desc;
1478 for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
1479 $confdesc->{"usb$i"} = $usbdesc;
1484 type
=> 'string', format
=> 'pve-volume-id',
1485 description
=> "Reference to unused volumes. This is used internally, and should not be modified manually.",
1488 for (my $i = 0; $i < $MAX_UNUSED_DISKS; $i++) {
1489 $confdesc->{"unused$i"} = $unuseddesc;
1492 my $kvm_api_version = 0;
1495 return $kvm_api_version if $kvm_api_version;
1497 open my $fh, '<', '/dev/kvm'
1500 # 0xae00 => KVM_GET_API_VERSION
1501 $kvm_api_version = ioctl($fh, 0xae00, 0);
1503 return $kvm_api_version;
1506 my $kvm_user_version = {};
1509 sub kvm_user_version
{
1512 $binary //= get_command_for_arch
(get_host_arch
()); # get the native arch by default
1513 my $st = stat($binary);
1515 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1516 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1517 $cachedmtime == $st->mtime;
1519 $kvm_user_version->{$binary} = 'unknown';
1520 $kvm_mtime->{$binary} = $st->mtime;
1524 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1525 $kvm_user_version->{$binary} = $2;
1529 eval { run_command
([$binary, '--version'], outfunc
=> $code); };
1532 return $kvm_user_version->{$binary};
1536 sub kernel_has_vhost_net
{
1537 return -c
'/dev/vhost-net';
1540 sub valid_drive_names
{
1541 # order is important - used to autoselect boot disk
1542 return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))),
1543 (map { "scsi$_" } (0 .. ($MAX_SCSI_DISKS - 1))),
1544 (map { "virtio$_" } (0 .. ($MAX_VIRTIO_DISKS - 1))),
1545 (map { "sata$_" } (0 .. ($MAX_SATA_DISKS - 1))),
1549 sub is_valid_drivename
{
1552 return defined($drivename_hash->{$dev});
1557 return defined($confdesc->{$key});
1561 sub get_cdrom_path
{
1563 return $cdrom_path if $cdrom_path;
1565 return $cdrom_path = "/dev/cdrom" if -l
"/dev/cdrom";
1566 return $cdrom_path = "/dev/cdrom1" if -l
"/dev/cdrom1";
1567 return $cdrom_path = "/dev/cdrom2" if -l
"/dev/cdrom2";
1571 my ($storecfg, $vmid, $cdrom) = @_;
1573 if ($cdrom eq 'cdrom') {
1574 return get_cdrom_path
();
1575 } elsif ($cdrom eq 'none') {
1577 } elsif ($cdrom =~ m
|^/|) {
1580 return PVE
::Storage
::path
($storecfg, $cdrom);
1584 # try to convert old style file names to volume IDs
1585 sub filename_to_volume_id
{
1586 my ($vmid, $file, $media) = @_;
1588 if (!($file eq 'none' || $file eq 'cdrom' ||
1589 $file =~ m
|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1591 return undef if $file =~ m
|/|;
1593 if ($media && $media eq 'cdrom') {
1594 $file = "local:iso/$file";
1596 $file = "local:$vmid/$file";
1603 sub verify_media_type
{
1604 my ($opt, $vtype, $media) = @_;
1609 if ($media eq 'disk') {
1611 } elsif ($media eq 'cdrom') {
1614 die "internal error";
1617 return if ($vtype eq $etype);
1619 raise_param_exc
({ $opt => "unexpected media type ($vtype != $etype)" });
1622 sub cleanup_drive_path
{
1623 my ($opt, $storecfg, $drive) = @_;
1625 # try to convert filesystem paths to volume IDs
1627 if (($drive->{file
} !~ m/^(cdrom|none)$/) &&
1628 ($drive->{file
} !~ m
|^/dev/.+|) &&
1629 ($drive->{file
} !~ m/^([^:]+):(.+)$/) &&
1630 ($drive->{file
} !~ m/^\d+$/)) {
1631 my ($vtype, $volid) = PVE
::Storage
::path_to_volume_id
($storecfg, $drive->{file
});
1632 raise_param_exc
({ $opt => "unable to associate path '$drive->{file}' to any storage"}) if !$vtype;
1633 $drive->{media
} = 'cdrom' if !$drive->{media
} && $vtype eq 'iso';
1634 verify_media_type
($opt, $vtype, $drive->{media
});
1635 $drive->{file
} = $volid;
1638 $drive->{media
} = 'cdrom' if !$drive->{media
} && $drive->{file
} =~ m/^(cdrom|none)$/;
1641 sub parse_hotplug_features
{
1646 return $res if $data eq '0';
1648 $data = $confdesc->{hotplug
}->{default} if $data eq '1';
1650 foreach my $feature (PVE
::Tools
::split_list
($data)) {
1651 if ($feature =~ m/^(network|disk|cpu|memory|usb)$/) {
1654 die "invalid hotplug feature '$feature'\n";
1660 PVE
::JSONSchema
::register_format
('pve-hotplug-features', \
&pve_verify_hotplug_features
);
1661 sub pve_verify_hotplug_features
{
1662 my ($value, $noerr) = @_;
1664 return $value if parse_hotplug_features
($value);
1666 return undef if $noerr;
1668 die "unable to parse hotplug option\n";
1671 # ideX = [volume=]volume-id[,media=d][,cyls=c,heads=h,secs=s[,trans=t]]
1672 # [,snapshot=on|off][,cache=on|off][,format=f][,backup=yes|no]
1673 # [,rerror=ignore|report|stop][,werror=enospc|ignore|report|stop]
1674 # [,aio=native|threads][,discard=ignore|on][,detect_zeroes=on|off]
1675 # [,iothread=on][,serial=serial][,model=model]
1678 my ($key, $data) = @_;
1680 my ($interface, $index);
1682 if ($key =~ m/^([^\d]+)(\d+)$/) {
1689 my $desc = $key =~ /^unused\d+$/ ?
$alldrive_fmt
1690 : $confdesc->{$key}->{format
};
1692 warn "invalid drive key: $key\n";
1695 my $res = eval { PVE
::JSONSchema
::parse_property_string
($desc, $data) };
1696 return undef if !$res;
1697 $res->{interface
} = $interface;
1698 $res->{index} = $index;
1701 foreach my $opt (qw(bps bps_rd bps_wr)) {
1702 if (my $bps = defined(delete $res->{$opt})) {
1703 if (defined($res->{"m$opt"})) {
1704 warn "both $opt and m$opt specified\n";
1708 $res->{"m$opt"} = sprintf("%.3f", $bps / (1024*1024.0));
1712 # can't use the schema's 'requires' because of the mbps* => bps* "transforming aliases"
1713 for my $requirement (
1714 [mbps_max
=> 'mbps'],
1715 [mbps_rd_max
=> 'mbps_rd'],
1716 [mbps_wr_max
=> 'mbps_wr'],
1717 [miops_max
=> 'miops'],
1718 [miops_rd_max
=> 'miops_rd'],
1719 [miops_wr_max
=> 'miops_wr'],
1720 [bps_max_length
=> 'mbps_max'],
1721 [bps_rd_max_length
=> 'mbps_rd_max'],
1722 [bps_wr_max_length
=> 'mbps_wr_max'],
1723 [iops_max_length
=> 'iops_max'],
1724 [iops_rd_max_length
=> 'iops_rd_max'],
1725 [iops_wr_max_length
=> 'iops_wr_max']) {
1726 my ($option, $requires) = @$requirement;
1727 if ($res->{$option} && !$res->{$requires}) {
1728 warn "$option requires $requires\n";
1733 return undef if $error;
1735 return undef if $res->{mbps_rd
} && $res->{mbps
};
1736 return undef if $res->{mbps_wr
} && $res->{mbps
};
1737 return undef if $res->{iops_rd
} && $res->{iops
};
1738 return undef if $res->{iops_wr
} && $res->{iops
};
1740 if ($res->{media
} && ($res->{media
} eq 'cdrom')) {
1741 return undef if $res->{snapshot
} || $res->{trans
} || $res->{format
};
1742 return undef if $res->{heads
} || $res->{secs
} || $res->{cyls
};
1743 return undef if $res->{interface
} eq 'virtio';
1746 if (my $size = $res->{size
}) {
1747 return undef if !defined($res->{size
} = PVE
::JSONSchema
::parse_size
($size));
1755 my $data = { %$drive };
1756 delete $data->{$_} for qw(index interface);
1757 return PVE
::JSONSchema
::print_property_string
($data, $alldrive_fmt);
1761 my($fh, $noerr) = @_;
1764 my $SG_GET_VERSION_NUM = 0x2282;
1766 my $versionbuf = "\x00" x
8;
1767 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1769 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
1772 my $version = unpack("I", $versionbuf);
1773 if ($version < 30000) {
1774 die "scsi generic interface too old\n" if !$noerr;
1778 my $buf = "\x00" x
36;
1779 my $sensebuf = "\x00" x
8;
1780 my $cmd = pack("C x3 C x1", 0x12, 36);
1782 # see /usr/include/scsi/sg.h
1783 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";
1785 my $packet = pack($sg_io_hdr_t, ord('S'), -3, length($cmd),
1786 length($sensebuf), 0, length($buf), $buf,
1787 $cmd, $sensebuf, 6000);
1789 $ret = ioctl($fh, $SG_IO, $packet);
1791 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
1795 my @res = unpack($sg_io_hdr_t, $packet);
1796 if ($res[17] || $res[18]) {
1797 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
1802 (my $byte0, my $byte1, $res->{vendor
},
1803 $res->{product
}, $res->{revision
}) = unpack("C C x6 A8 A16 A4", $buf);
1805 $res->{removable
} = $byte1 & 128 ?
1 : 0;
1806 $res->{type
} = $byte0 & 31;
1814 my $fh = IO
::File-
>new("+<$path") || return undef;
1815 my $res = scsi_inquiry
($fh, 1);
1821 sub print_tabletdevice_full
{
1822 my ($conf, $arch) = @_;
1824 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
1826 # we use uhci for old VMs because tablet driver was buggy in older qemu
1828 if (PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf) || $arch eq 'aarch64') {
1834 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1837 sub print_keyboarddevice_full
{
1838 my ($conf, $arch, $machine) = @_;
1840 return undef if $arch ne 'aarch64';
1842 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1845 sub print_drivedevice_full
{
1846 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1851 if ($drive->{interface
} eq 'virtio') {
1852 my $pciaddr = print_pci_addr
("$drive->{interface}$drive->{index}", $bridges, $arch, $machine_type);
1853 $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
1854 $device .= ",iothread=iothread-$drive->{interface}$drive->{index}" if $drive->{iothread
};
1855 } elsif ($drive->{interface
} eq 'scsi') {
1857 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
1858 my $unit = $drive->{index} % $maxdev;
1859 my $devicetype = 'hd';
1861 if (drive_is_cdrom
($drive)) {
1864 if ($drive->{file
} =~ m
|^/|) {
1865 $path = $drive->{file
};
1866 if (my $info = path_is_scsi
($path)) {
1867 if ($info->{type
} == 0 && $drive->{scsiblock
}) {
1868 $devicetype = 'block';
1869 } elsif ($info->{type
} == 1) { # tape
1870 $devicetype = 'generic';
1874 $path = PVE
::Storage
::path
($storecfg, $drive->{file
});
1877 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1878 my $version = PVE
::QemuServer
::Machine
::extract_version
($machine_type, kvm_user_version
());
1879 if ($path =~ m/^iscsi\:\/\
// &&
1880 !min_version
($version, 4, 1)) {
1881 $devicetype = 'generic';
1885 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)){
1886 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
1888 $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}";
1891 if ($drive->{ssd
} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1892 $device .= ",rotation_rate=1";
1894 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1896 } elsif ($drive->{interface
} eq 'ide' || $drive->{interface
} eq 'sata') {
1897 my $maxdev = ($drive->{interface
} eq 'sata') ?
$MAX_SATA_DISKS : 2;
1898 my $controller = int($drive->{index} / $maxdev);
1899 my $unit = $drive->{index} % $maxdev;
1900 my $devicetype = ($drive->{media
} && $drive->{media
} eq 'cdrom') ?
"cd" : "hd";
1902 $device = "ide-$devicetype";
1903 if ($drive->{interface
} eq 'ide') {
1904 $device .= ",bus=ide.$controller,unit=$unit";
1906 $device .= ",bus=ahci$controller.$unit";
1908 $device .= ",drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
1910 if ($devicetype eq 'hd') {
1911 if (my $model = $drive->{model
}) {
1912 $model = URI
::Escape
::uri_unescape
($model);
1913 $device .= ",model=$model";
1915 if ($drive->{ssd
}) {
1916 $device .= ",rotation_rate=1";
1919 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn
};
1920 } elsif ($drive->{interface
} eq 'usb') {
1922 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1924 die "unsupported interface type";
1927 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex
};
1929 if (my $serial = $drive->{serial
}) {
1930 $serial = URI
::Escape
::uri_unescape
($serial);
1931 $device .= ",serial=$serial";
1938 sub get_initiator_name
{
1941 my $fh = IO
::File-
>new('/etc/iscsi/initiatorname.iscsi') || return undef;
1942 while (defined(my $line = <$fh>)) {
1943 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1952 sub print_drive_full
{
1953 my ($storecfg, $vmid, $drive) = @_;
1956 my $volid = $drive->{file
};
1959 if (drive_is_cdrom
($drive)) {
1960 $path = get_iso_path
($storecfg, $vmid, $volid);
1962 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
1964 $path = PVE
::Storage
::path
($storecfg, $volid);
1965 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
1966 $format = qemu_img_format
($scfg, $volname);
1974 my @qemu_drive_options = qw(heads secs cyls trans media format cache rerror werror aio discard);
1975 foreach my $o (@qemu_drive_options) {
1976 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1979 # snapshot only accepts on|off
1980 if (defined($drive->{snapshot
})) {
1981 my $v = $drive->{snapshot
} ?
'on' : 'off';
1982 $opts .= ",snapshot=$v";
1985 foreach my $type (['', '-total'], [_rd
=> '-read'], [_wr
=> '-write']) {
1986 my ($dir, $qmpname) = @$type;
1987 if (my $v = $drive->{"mbps$dir"}) {
1988 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1990 if (my $v = $drive->{"mbps${dir}_max"}) {
1991 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1993 if (my $v = $drive->{"bps${dir}_max_length"}) {
1994 $opts .= ",throttling.bps$qmpname-max-length=$v";
1996 if (my $v = $drive->{"iops${dir}"}) {
1997 $opts .= ",throttling.iops$qmpname=$v";
1999 if (my $v = $drive->{"iops${dir}_max"}) {
2000 $opts .= ",throttling.iops$qmpname-max=$v";
2002 if (my $v = $drive->{"iops${dir}_max_length"}) {
2003 $opts .= ",throttling.iops$qmpname-max-length=$v";
2007 $opts .= ",format=$format" if $format && !$drive->{format
};
2009 my $cache_direct = 0;
2011 if (my $cache = $drive->{cache
}) {
2012 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
2013 } elsif (!drive_is_cdrom
($drive)) {
2014 $opts .= ",cache=none";
2018 # aio native works only with O_DIRECT
2019 if (!$drive->{aio
}) {
2021 $opts .= ",aio=native";
2023 $opts .= ",aio=threads";
2027 if (!drive_is_cdrom
($drive)) {
2029 if (defined($drive->{detect_zeroes
}) && !$drive->{detect_zeroes
}) {
2030 $detectzeroes = 'off';
2031 } elsif ($drive->{discard
}) {
2032 $detectzeroes = $drive->{discard
} eq 'on' ?
'unmap' : 'on';
2034 # This used to be our default with discard not being specified:
2035 $detectzeroes = 'on';
2037 $opts .= ",detect-zeroes=$detectzeroes" if $detectzeroes;
2040 my $pathinfo = $path ?
"file=$path," : '';
2042 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
2045 sub print_netdevice_full
{
2046 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type) = @_;
2048 my $bootorder = $conf->{boot
} || $confdesc->{boot
}->{default};
2050 my $device = $net->{model
};
2051 if ($net->{model
} eq 'virtio') {
2052 $device = 'virtio-net-pci';
2055 my $pciaddr = print_pci_addr
("$netid", $bridges, $arch, $machine_type);
2056 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
2057 if ($net->{queues
} && $net->{queues
} > 1 && $net->{model
} eq 'virtio'){
2058 #Consider we have N queues, the number of vectors needed is 2*N + 2 (plus one config interrupt and control vq)
2059 my $vectors = $net->{queues
} * 2 + 2;
2060 $tmpstr .= ",vectors=$vectors,mq=on";
2062 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex
} ;
2064 if ($use_old_bios_files) {
2066 if ($device eq 'virtio-net-pci') {
2067 $romfile = 'pxe-virtio.rom';
2068 } elsif ($device eq 'e1000') {
2069 $romfile = 'pxe-e1000.rom';
2070 } elsif ($device eq 'ne2k') {
2071 $romfile = 'pxe-ne2k_pci.rom';
2072 } elsif ($device eq 'pcnet') {
2073 $romfile = 'pxe-pcnet.rom';
2074 } elsif ($device eq 'rtl8139') {
2075 $romfile = 'pxe-rtl8139.rom';
2077 $tmpstr .= ",romfile=$romfile" if $romfile;
2083 sub print_netdev_full
{
2084 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
2087 if ($netid =~ m/^net(\d+)$/) {
2091 die "got strange net id '$i'\n" if $i >= ${MAX_NETS
};
2093 my $ifname = "tap${vmid}i$i";
2095 # kvm uses TUNSETIFF ioctl, and that limits ifname length
2096 die "interface name '$ifname' is too long (max 15 character)\n"
2097 if length($ifname) >= 16;
2099 my $vhostparam = '';
2100 if (is_native
($arch)) {
2101 $vhostparam = ',vhost=on' if kernel_has_vhost_net
() && $net->{model
} eq 'virtio';
2104 my $vmname = $conf->{name
} || "vm$vmid";
2107 my $script = $hotplug ?
"pve-bridge-hotplug" : "pve-bridge";
2109 if ($net->{bridge
}) {
2110 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script,downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
2112 $netdev = "type=user,id=$netid,hostname=$vmname";
2115 $netdev .= ",queues=$net->{queues}" if ($net->{queues
} && $net->{model
} eq 'virtio');
2121 sub print_cpu_device
{
2122 my ($conf, $id) = @_;
2124 my $kvm = $conf->{kvm
} // 1;
2125 my $cpu = $kvm ?
"kvm64" : "qemu64";
2126 if (my $cputype = $conf->{cpu
}) {
2127 my $cpuconf = PVE
::JSONSchema
::parse_property_string
($cpu_fmt, $cputype)
2128 or die "Cannot parse cpu description: $cputype\n";
2129 $cpu = $cpuconf->{cputype
};
2132 my $cores = $conf->{cores
} || 1;
2134 my $current_core = ($id - 1) % $cores;
2135 my $current_socket = int(($id - 1 - $current_core)/$cores);
2137 return "$cpu-x86_64-cpu,id=cpu$id,socket-id=$current_socket,core-id=$current_core,thread-id=0";
2141 'cirrus' => 'cirrus-vga',
2143 'vmware' => 'vmware-svga',
2144 'virtio' => 'virtio-vga',
2147 sub print_vga_device
{
2148 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
2150 my $type = $vga_map->{$vga->{type
}};
2151 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
2152 $type = 'virtio-gpu';
2154 my $vgamem_mb = $vga->{memory
};
2156 my $max_outputs = '';
2158 $type = $id ?
'qxl' : 'qxl-vga';
2160 if (!$conf->{ostype
} || $conf->{ostype
} =~ m/^(?:l\d\d)|(?:other)$/) {
2161 # set max outputs so linux can have up to 4 qxl displays with one device
2162 if (min_version
($machine_version, 4, 1)) {
2163 $max_outputs = ",max_outputs=4";
2168 die "no devicetype for $vga->{type}\n" if !$type;
2172 if ($vga->{type
} eq 'virtio') {
2173 my $bytes = PVE
::Tools
::convert_size
($vgamem_mb, "mb" => "b");
2174 $memory = ",max_hostmem=$bytes";
2176 # from https://www.spice-space.org/multiple-monitors.html
2177 $memory = ",vgamem_mb=$vga->{memory}";
2178 my $ram = $vgamem_mb * 4;
2179 my $vram = $vgamem_mb * 2;
2180 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
2182 $memory = ",vgamem_mb=$vga->{memory}";
2184 } elsif ($qxlnum && $id) {
2185 $memory = ",ram_size=67108864,vram_size=33554432";
2188 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
2189 my $vgaid = "vga" . ($id // '');
2192 if ($q35 && $vgaid eq 'vga') {
2193 # the first display uses pcie.0 bus on q35 machines
2194 $pciaddr = print_pcie_addr
($vgaid, $bridges, $arch, $machine);
2196 $pciaddr = print_pci_addr
($vgaid, $bridges, $arch, $machine);
2199 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}";
2202 sub drive_is_cloudinit
{
2204 return $drive->{file
} =~ m
@[:/]vm-\d
+-cloudinit
(?
:\
.$QEMU_FORMAT_RE)?
$@;
2207 sub drive_is_cdrom
{
2208 my ($drive, $exclude_cloudinit) = @_;
2210 return 0 if $exclude_cloudinit && drive_is_cloudinit
($drive);
2212 return $drive && $drive->{media
} && ($drive->{media
} eq 'cdrom');
2216 sub parse_number_sets
{
2219 foreach my $part (split(/;/, $set)) {
2220 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
2221 die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1;
2222 push @$res, [ $1, $2 ];
2224 die "invalid range: $part\n";
2233 my $res = PVE
::JSONSchema
::parse_property_string
($numa_fmt, $data);
2234 $res->{cpus
} = parse_number_sets
($res->{cpus
}) if defined($res->{cpus
});
2235 $res->{hostnodes
} = parse_number_sets
($res->{hostnodes
}) if defined($res->{hostnodes
});
2242 return undef if !$value;
2244 my $res = PVE
::JSONSchema
::parse_property_string
($hostpci_fmt, $value);
2246 my @idlist = split(/;/, $res->{host
});
2247 delete $res->{host
};
2248 foreach my $id (@idlist) {
2249 my $devs = PVE
::SysFSTools
::lspci
($id);
2250 die "no PCI device found for '$id'\n" if !scalar(@$devs);
2251 push @{$res->{pciid
}}, @$devs;
2256 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
2260 my $res = eval { PVE
::JSONSchema
::parse_property_string
($net_fmt, $data) };
2265 if (!defined($res->{macaddr
})) {
2266 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
2267 $res->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
});
2272 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
2273 sub parse_ipconfig
{
2276 my $res = eval { PVE
::JSONSchema
::parse_property_string
($ipconfig_fmt, $data) };
2282 if ($res->{gw
} && !$res->{ip
}) {
2283 warn 'gateway specified without specifying an IP address';
2286 if ($res->{gw6
} && !$res->{ip6
}) {
2287 warn 'IPv6 gateway specified without specifying an IPv6 address';
2290 if ($res->{gw
} && $res->{ip
} eq 'dhcp') {
2291 warn 'gateway specified together with DHCP';
2294 if ($res->{gw6
} && $res->{ip6
} !~ /^$IPV6RE/) {
2296 warn "IPv6 gateway specified together with $res->{ip6} address";
2300 if (!$res->{ip
} && !$res->{ip6
}) {
2301 return { ip
=> 'dhcp', ip6
=> 'dhcp' };
2310 return PVE
::JSONSchema
::print_property_string
($net, $net_fmt);
2313 sub add_random_macs
{
2314 my ($settings) = @_;
2316 foreach my $opt (keys %$settings) {
2317 next if $opt !~ m/^net(\d+)$/;
2318 my $net = parse_net
($settings->{$opt});
2320 $settings->{$opt} = print_net
($net);
2324 sub vm_is_volid_owner
{
2325 my ($storecfg, $vmid, $volid) = @_;
2327 if ($volid !~ m
|^/|) {
2329 eval { ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid); };
2330 if ($owner && ($owner == $vmid)) {
2338 sub vmconfig_register_unused_drive
{
2339 my ($storecfg, $vmid, $conf, $drive) = @_;
2341 if (drive_is_cloudinit
($drive)) {
2342 eval { PVE
::Storage
::vdisk_free
($storecfg, $drive->{file
}) };
2344 } elsif (!drive_is_cdrom
($drive)) {
2345 my $volid = $drive->{file
};
2346 if (vm_is_volid_owner
($storecfg, $vmid, $volid)) {
2347 PVE
::QemuConfig-
>add_unused_volume($conf, $volid, $vmid);
2352 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
2356 pattern
=> '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
2357 format_description
=> 'UUID',
2358 description
=> "Set SMBIOS1 UUID.",
2363 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2364 format_description
=> 'Base64 encoded string',
2365 description
=> "Set SMBIOS1 version.",
2370 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2371 format_description
=> 'Base64 encoded string',
2372 description
=> "Set SMBIOS1 serial number.",
2377 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2378 format_description
=> 'Base64 encoded string',
2379 description
=> "Set SMBIOS1 manufacturer.",
2384 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2385 format_description
=> 'Base64 encoded string',
2386 description
=> "Set SMBIOS1 product ID.",
2391 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2392 format_description
=> 'Base64 encoded string',
2393 description
=> "Set SMBIOS1 SKU string.",
2398 pattern
=> '[A-Za-z0-9+\/]+={0,2}',
2399 format_description
=> 'Base64 encoded string',
2400 description
=> "Set SMBIOS1 family string.",
2405 description
=> 'Flag to indicate that the SMBIOS values are base64 encoded',
2413 my $res = eval { PVE
::JSONSchema
::parse_property_string
($smbios1_fmt, $data) };
2420 return PVE
::JSONSchema
::print_property_string
($smbios1, $smbios1_fmt);
2423 PVE
::JSONSchema
::register_format
('pve-qm-smbios1', $smbios1_fmt);
2425 PVE
::JSONSchema
::register_format
('pve-qm-bootdisk', \
&verify_bootdisk
);
2426 sub verify_bootdisk
{
2427 my ($value, $noerr) = @_;
2429 return $value if is_valid_drivename
($value);
2431 return undef if $noerr;
2433 die "invalid boot disk '$value'\n";
2436 sub parse_watchdog
{
2439 return undef if !$value;
2441 my $res = eval { PVE
::JSONSchema
::parse_property_string
($watchdog_fmt, $value) };
2446 sub parse_guest_agent
{
2449 return {} if !defined($value->{agent
});
2451 my $res = eval { PVE
::JSONSchema
::parse_property_string
($agent_fmt, $value->{agent
}) };
2454 # if the agent is disabled ignore the other potentially set properties
2455 return {} if !$res->{enabled
};
2462 return {} if !$value;
2463 my $res = eval { PVE
::JSONSchema
::parse_property_string
($vga_fmt, $value) };
2468 PVE
::JSONSchema
::register_format
('pve-qm-usb-device', \
&verify_usb_device
);
2469 sub verify_usb_device
{
2470 my ($value, $noerr) = @_;
2472 return $value if parse_usb_device
($value);
2474 return undef if $noerr;
2476 die "unable to parse usb device\n";
2479 # add JSON properties for create and set function
2480 sub json_config_properties
{
2483 foreach my $opt (keys %$confdesc) {
2484 next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'vmstate' || $opt eq 'runningmachine';
2485 $prop->{$opt} = $confdesc->{$opt};
2491 # return copy of $confdesc_cloudinit to generate documentation
2492 sub cloudinit_config_properties
{
2494 return dclone
($confdesc_cloudinit);
2498 my ($key, $value) = @_;
2500 die "unknown setting '$key'\n" if !$confdesc->{$key};
2502 my $type = $confdesc->{$key}->{type
};
2504 if (!defined($value)) {
2505 die "got undefined value\n";
2508 if ($value =~ m/[\n\r]/) {
2509 die "property contains a line feed\n";
2512 if ($type eq 'boolean') {
2513 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2514 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2515 die "type check ('boolean') failed - got '$value'\n";
2516 } elsif ($type eq 'integer') {
2517 return int($1) if $value =~ m/^(\d+)$/;
2518 die "type check ('integer') failed - got '$value'\n";
2519 } elsif ($type eq 'number') {
2520 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2521 die "type check ('number') failed - got '$value'\n";
2522 } elsif ($type eq 'string') {
2523 if (my $fmt = $confdesc->{$key}->{format
}) {
2524 PVE
::JSONSchema
::check_format
($fmt, $value);
2527 $value =~ s/^\"(.*)\"$/$1/;
2530 die "internal error"
2535 my ($storecfg, $vmid, $skiplock, $replacement_conf) = @_;
2537 my $conf = PVE
::QemuConfig-
>load_config($vmid);
2539 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
2541 if ($conf->{template
}) {
2542 # check if any base image is still used by a linked clone
2543 foreach_drive
($conf, sub {
2544 my ($ds, $drive) = @_;
2545 return if drive_is_cdrom
($drive);
2547 my $volid = $drive->{file
};
2548 return if !$volid || $volid =~ m
|^/|;
2550 die "base volume '$volid' is still in use by linked cloned\n"
2551 if PVE
::Storage
::volume_is_base_and_used
($storecfg, $volid);
2556 # only remove disks owned by this VM
2557 foreach_drive
($conf, sub {
2558 my ($ds, $drive) = @_;
2559 return if drive_is_cdrom
($drive, 1);
2561 my $volid = $drive->{file
};
2562 return if !$volid || $volid =~ m
|^/|;
2564 my ($path, $owner) = PVE
::Storage
::path
($storecfg, $volid);
2565 return if !$path || !$owner || ($owner != $vmid);
2567 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2568 warn "Could not remove disk '$volid', check manually: $@" if $@;
2571 # also remove unused disk
2572 my $vmdisks = PVE
::Storage
::vdisk_list
($storecfg, undef, $vmid);
2573 PVE
::Storage
::foreach_volid
($vmdisks, sub {
2574 my ($volid, $sid, $volname, $d) = @_;
2575 eval { PVE
::Storage
::vdisk_free
($storecfg, $volid) };
2579 if (defined $replacement_conf) {
2580 PVE
::QemuConfig-
>write_config($vmid, $replacement_conf);
2582 PVE
::QemuConfig-
>destroy_config($vmid);
2586 sub parse_vm_config
{
2587 my ($filename, $raw) = @_;
2589 return undef if !defined($raw);
2592 digest
=> Digest
::SHA
::sha1_hex
($raw),
2597 $filename =~ m
|/qemu-server/(\d
+)\
.conf
$|
2598 || die "got strange filename '$filename'";
2606 my @lines = split(/\n/, $raw);
2607 foreach my $line (@lines) {
2608 next if $line =~ m/^\s*$/;
2610 if ($line =~ m/^\[PENDING\]\s*$/i) {
2611 $section = 'pending';
2612 if (defined($descr)) {
2614 $conf->{description
} = $descr;
2617 $conf = $res->{$section} = {};
2620 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2622 if (defined($descr)) {
2624 $conf->{description
} = $descr;
2627 $conf = $res->{snapshots
}->{$section} = {};
2631 if ($line =~ m/^\#(.*)\s*$/) {
2632 $descr = '' if !defined($descr);
2633 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
2637 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2638 $descr = '' if !defined($descr);
2639 $descr .= PVE
::Tools
::decode_text
($2);
2640 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2641 $conf->{snapstate
} = $1;
2642 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2645 $conf->{$key} = $value;
2646 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2648 if ($section eq 'pending') {
2649 $conf->{delete} = $value; # we parse this later
2651 warn "vm $vmid - propertry 'delete' is only allowed in [PENDING]\n";
2653 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2656 eval { $value = check_type
($key, $value); };
2658 warn "vm $vmid - unable to parse value of '$key' - $@";
2660 $key = 'ide2' if $key eq 'cdrom';
2661 my $fmt = $confdesc->{$key}->{format
};
2662 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2663 my $v = parse_drive
($key, $value);
2664 if (my $volid = filename_to_volume_id
($vmid, $v->{file
}, $v->{media
})) {
2665 $v->{file
} = $volid;
2666 $value = print_drive
($v);
2668 warn "vm $vmid - unable to parse value of '$key'\n";
2673 $conf->{$key} = $value;
2678 if (defined($descr)) {
2680 $conf->{description
} = $descr;
2682 delete $res->{snapstate
}; # just to be sure
2687 sub write_vm_config
{
2688 my ($filename, $conf) = @_;
2690 delete $conf->{snapstate
}; # just to be sure
2692 if ($conf->{cdrom
}) {
2693 die "option ide2 conflicts with cdrom\n" if $conf->{ide2
};
2694 $conf->{ide2
} = $conf->{cdrom
};
2695 delete $conf->{cdrom
};
2698 # we do not use 'smp' any longer
2699 if ($conf->{sockets
}) {
2700 delete $conf->{smp
};
2701 } elsif ($conf->{smp
}) {
2702 $conf->{sockets
} = $conf->{smp
};
2703 delete $conf->{cores
};
2704 delete $conf->{smp
};
2707 my $used_volids = {};
2709 my $cleanup_config = sub {
2710 my ($cref, $pending, $snapname) = @_;
2712 foreach my $key (keys %$cref) {
2713 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2714 $key eq 'snapstate' || $key eq 'pending';
2715 my $value = $cref->{$key};
2716 if ($key eq 'delete') {
2717 die "propertry 'delete' is only allowed in [PENDING]\n"
2719 # fixme: check syntax?
2722 eval { $value = check_type
($key, $value); };
2723 die "unable to parse value of '$key' - $@" if $@;
2725 $cref->{$key} = $value;
2727 if (!$snapname && is_valid_drivename
($key)) {
2728 my $drive = parse_drive
($key, $value);
2729 $used_volids->{$drive->{file
}} = 1 if $drive && $drive->{file
};
2734 &$cleanup_config($conf);
2736 &$cleanup_config($conf->{pending
}, 1);
2738 foreach my $snapname (keys %{$conf->{snapshots
}}) {
2739 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2740 &$cleanup_config($conf->{snapshots
}->{$snapname}, undef, $snapname);
2743 # remove 'unusedX' settings if we re-add a volume
2744 foreach my $key (keys %$conf) {
2745 my $value = $conf->{$key};
2746 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2747 delete $conf->{$key};
2751 my $generate_raw_config = sub {
2752 my ($conf, $pending) = @_;
2756 # add description as comment to top of file
2757 if (defined(my $descr = $conf->{description
})) {
2759 foreach my $cl (split(/\n/, $descr)) {
2760 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
2763 $raw .= "#\n" if $pending;
2767 foreach my $key (sort keys %$conf) {
2768 next if $key eq 'digest' || $key eq 'description' || $key eq 'pending' || $key eq 'snapshots';
2769 $raw .= "$key: $conf->{$key}\n";
2774 my $raw = &$generate_raw_config($conf);
2776 if (scalar(keys %{$conf->{pending
}})){
2777 $raw .= "\n[PENDING]\n";
2778 $raw .= &$generate_raw_config($conf->{pending
}, 1);
2781 foreach my $snapname (sort keys %{$conf->{snapshots
}}) {
2782 $raw .= "\n[$snapname]\n";
2783 $raw .= &$generate_raw_config($conf->{snapshots
}->{$snapname});
2793 # we use static defaults from our JSON schema configuration
2794 foreach my $key (keys %$confdesc) {
2795 if (defined(my $default = $confdesc->{$key}->{default})) {
2796 $res->{$key} = $default;
2804 my $vmlist = PVE
::Cluster
::get_vmlist
();
2806 return $res if !$vmlist || !$vmlist->{ids
};
2807 my $ids = $vmlist->{ids
};
2809 foreach my $vmid (keys %$ids) {
2810 my $d = $ids->{$vmid};
2811 next if !$d->{node
} || $d->{node
} ne $nodename;
2812 next if !$d->{type
} || $d->{type
} ne 'qemu';
2813 $res->{$vmid}->{exists} = 1;
2818 # test if VM uses local resources (to prevent migration)
2819 sub check_local_resources
{
2820 my ($conf, $noerr) = @_;
2824 push @loc_res, "hostusb" if $conf->{hostusb
}; # old syntax
2825 push @loc_res, "hostpci" if $conf->{hostpci
}; # old syntax
2827 push @loc_res, "ivshmem" if $conf->{ivshmem
};
2829 foreach my $k (keys %$conf) {
2830 next if $k =~ m/^usb/ && ($conf->{$k} =~ m/^spice(?![^,])/);
2831 # sockets are safe: they will recreated be on the target side post-migrate
2832 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2833 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2836 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2841 # check if used storages are available on all nodes (use by migrate)
2842 sub check_storage_availability
{
2843 my ($storecfg, $conf, $node) = @_;
2845 foreach_drive
($conf, sub {
2846 my ($ds, $drive) = @_;
2848 my $volid = $drive->{file
};
2851 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2854 # check if storage is available on both nodes
2855 my $scfg = PVE
::Storage
::storage_check_node
($storecfg, $sid);
2856 PVE
::Storage
::storage_check_node
($storecfg, $sid, $node);
2860 # list nodes where all VM images are available (used by has_feature API)
2862 my ($conf, $storecfg) = @_;
2864 my $nodelist = PVE
::Cluster
::get_nodelist
();
2865 my $nodehash = { map { $_ => 1 } @$nodelist };
2866 my $nodename = PVE
::INotify
::nodename
();
2868 foreach_drive
($conf, sub {
2869 my ($ds, $drive) = @_;
2871 my $volid = $drive->{file
};
2874 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2876 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2877 if ($scfg->{disable
}) {
2879 } elsif (my $avail = $scfg->{nodes
}) {
2880 foreach my $node (keys %$nodehash) {
2881 delete $nodehash->{$node} if !$avail->{$node};
2883 } elsif (!$scfg->{shared
}) {
2884 foreach my $node (keys %$nodehash) {
2885 delete $nodehash->{$node} if $node ne $nodename
2894 sub check_local_storage_availability
{
2895 my ($conf, $storecfg) = @_;
2897 my $nodelist = PVE
::Cluster
::get_nodelist
();
2898 my $nodehash = { map { $_ => {} } @$nodelist };
2900 foreach_drive
($conf, sub {
2901 my ($ds, $drive) = @_;
2903 my $volid = $drive->{file
};
2906 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
2908 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
2910 if ($scfg->{disable
}) {
2911 foreach my $node (keys %$nodehash) {
2912 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2914 } elsif (my $avail = $scfg->{nodes
}) {
2915 foreach my $node (keys %$nodehash) {
2916 if (!$avail->{$node}) {
2917 $nodehash->{$node}->{unavailable_storages
}->{$storeid} = 1;
2924 foreach my $node (values %$nodehash) {
2925 if (my $unavail = $node->{unavailable_storages
}) {
2926 $node->{unavailable_storages
} = [ sort keys %$unavail ];
2933 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2935 my ($vmid, $nocheck, $node) = @_;
2937 PVE
::QemuConfig
::assert_config_exists_on_node
($vmid, $node) if !$nocheck;
2938 return PVE
::QemuServer
::Helpers
::vm_running_locally
($vmid);
2943 my $vzlist = config_list
();
2945 my $fd = IO
::Dir-
>new($PVE::QemuServer
::Helpers
::var_run_tmpdir
) || return $vzlist;
2947 while (defined(my $de = $fd->read)) {
2948 next if $de !~ m/^(\d+)\.pid$/;
2950 next if !defined($vzlist->{$vmid});
2951 if (my $pid = check_running
($vmid)) {
2952 $vzlist->{$vmid}->{pid
} = $pid;
2960 my ($storecfg, $conf) = @_;
2962 my $bootdisk = $conf->{bootdisk
};
2963 return undef if !$bootdisk;
2964 return undef if !is_valid_drivename
($bootdisk);
2966 return undef if !$conf->{$bootdisk};
2968 my $drive = parse_drive
($bootdisk, $conf->{$bootdisk});
2969 return undef if !defined($drive);
2971 return undef if drive_is_cdrom
($drive);
2973 my $volid = $drive->{file
};
2974 return undef if !$volid;
2976 return $drive->{size
};
2979 our $vmstatus_return_properties = {
2980 vmid
=> get_standard_option
('pve-vmid'),
2982 description
=> "Qemu process status.",
2984 enum
=> ['stopped', 'running'],
2987 description
=> "Maximum memory in bytes.",
2990 renderer
=> 'bytes',
2993 description
=> "Root disk size in bytes.",
2996 renderer
=> 'bytes',
2999 description
=> "VM name.",
3004 description
=> "Qemu QMP agent status.",
3009 description
=> "PID of running qemu process.",
3014 description
=> "Uptime.",
3017 renderer
=> 'duration',
3020 description
=> "Maximum usable CPUs.",
3025 description
=> "The current config lock, if any.",
3030 description
=> "The current configured tags, 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};
3104 $d->{tags
} = $conf->{tags
} if defined($conf->{tags
});
3109 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
3110 foreach my $dev (keys %$netdev) {
3111 next if $dev !~ m/^tap([1-9]\d*)i/;
3113 my $d = $res->{$vmid};
3116 $d->{netout
} += $netdev->{$dev}->{receive
};
3117 $d->{netin
} += $netdev->{$dev}->{transmit
};
3120 $d->{nics
}->{$dev}->{netout
} = $netdev->{$dev}->{receive
};
3121 $d->{nics
}->{$dev}->{netin
} = $netdev->{$dev}->{transmit
};
3126 my $ctime = gettimeofday
;
3128 foreach my $vmid (keys %$list) {
3130 my $d = $res->{$vmid};
3131 my $pid = $d->{pid
};
3134 my $pstat = PVE
::ProcFSTools
::read_proc_pid_stat
($pid);
3135 next if !$pstat; # not running
3137 my $used = $pstat->{utime} + $pstat->{stime
};
3139 $d->{uptime
} = int(($uptime - $pstat->{starttime
})/$cpuinfo->{user_hz
});
3141 if ($pstat->{vsize
}) {
3142 $d->{mem
} = int(($pstat->{rss
}/$pstat->{vsize
})*$d->{maxmem
});
3145 my $old = $last_proc_pid_stat->{$pid};
3147 $last_proc_pid_stat->{$pid} = {
3155 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz
};
3157 if ($dtime > 1000) {
3158 my $dutime = $used - $old->{used
};
3160 $d->{cpu
} = (($dutime/$dtime)* $cpucount) / $d->{cpus
};
3161 $last_proc_pid_stat->{$pid} = {
3167 $d->{cpu
} = $old->{cpu
};
3171 return $res if !$full;
3173 my $qmpclient = PVE
::QMPClient-
>new();
3175 my $ballooncb = sub {
3176 my ($vmid, $resp) = @_;
3178 my $info = $resp->{'return'};
3179 return if !$info->{max_mem
};
3181 my $d = $res->{$vmid};
3183 # use memory assigned to VM
3184 $d->{maxmem
} = $info->{max_mem
};
3185 $d->{balloon
} = $info->{actual
};
3187 if (defined($info->{total_mem
}) && defined($info->{free_mem
})) {
3188 $d->{mem
} = $info->{total_mem
} - $info->{free_mem
};
3189 $d->{freemem
} = $info->{free_mem
};
3192 $d->{ballooninfo
} = $info;
3195 my $blockstatscb = sub {
3196 my ($vmid, $resp) = @_;
3197 my $data = $resp->{'return'} || [];
3198 my $totalrdbytes = 0;
3199 my $totalwrbytes = 0;
3201 for my $blockstat (@$data) {
3202 $totalrdbytes = $totalrdbytes + $blockstat->{stats
}->{rd_bytes
};
3203 $totalwrbytes = $totalwrbytes + $blockstat->{stats
}->{wr_bytes
};
3205 $blockstat->{device
} =~ s/drive-//;
3206 $res->{$vmid}->{blockstat
}->{$blockstat->{device
}} = $blockstat->{stats
};
3208 $res->{$vmid}->{diskread
} = $totalrdbytes;
3209 $res->{$vmid}->{diskwrite
} = $totalwrbytes;
3212 my $statuscb = sub {
3213 my ($vmid, $resp) = @_;
3215 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
3216 # this fails if ballon driver is not loaded, so this must be
3217 # the last commnand (following command are aborted if this fails).
3218 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
3220 my $status = 'unknown';
3221 if (!defined($status = $resp->{'return'}->{status
})) {
3222 warn "unable to get VM status\n";
3226 $res->{$vmid}->{qmpstatus
} = $resp->{'return'}->{status
};
3229 foreach my $vmid (keys %$list) {
3230 next if $opt_vmid && ($vmid ne $opt_vmid);
3231 next if !$res->{$vmid}->{pid
}; # not running
3232 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
3235 $qmpclient->queue_execute(undef, 2);
3237 foreach my $vmid (keys %$list) {
3238 next if $opt_vmid && ($vmid ne $opt_vmid);
3239 $res->{$vmid}->{qmpstatus
} = $res->{$vmid}->{status
} if !$res->{$vmid}->{qmpstatus
};
3246 my ($conf, $func, @param) = @_;
3248 foreach my $ds (valid_drive_names
()) {
3249 next if !defined($conf->{$ds});
3251 my $drive = parse_drive
($ds, $conf->{$ds});
3254 &$func($ds, $drive, @param);
3259 my ($conf, $func, @param) = @_;
3263 my $test_volid = sub {
3264 my ($volid, $is_cdrom, $replicate, $shared, $snapname, $size) = @_;
3268 $volhash->{$volid}->{cdrom
} //= 1;
3269 $volhash->{$volid}->{cdrom
} = 0 if !$is_cdrom;
3271 $volhash->{$volid}->{replicate
} //= 0;
3272 $volhash->{$volid}->{replicate
} = 1 if $replicate;
3274 $volhash->{$volid}->{shared
} //= 0;
3275 $volhash->{$volid}->{shared
} = 1 if $shared;
3277 $volhash->{$volid}->{referenced_in_config
} //= 0;
3278 $volhash->{$volid}->{referenced_in_config
} = 1 if !defined($snapname);
3280 $volhash->{$volid}->{referenced_in_snapshot
}->{$snapname} = 1
3281 if defined($snapname);
3282 $volhash->{$volid}->{size
} = $size if $size;
3285 foreach_drive
($conf, sub {
3286 my ($ds, $drive) = @_;
3287 $test_volid->($drive->{file
}, drive_is_cdrom
($drive), $drive->{replicate
} // 1, $drive->{shared
}, undef, $drive->{size
});
3290 foreach my $snapname (keys %{$conf->{snapshots
}}) {
3291 my $snap = $conf->{snapshots
}->{$snapname};
3292 $test_volid->($snap->{vmstate
}, 0, 1, $snapname);
3293 foreach_drive
($snap, sub {
3294 my ($ds, $drive) = @_;
3295 $test_volid->($drive->{file
}, drive_is_cdrom
($drive), $drive->{replicate
} // 1, $drive->{shared
}, $snapname);
3299 foreach my $volid (keys %$volhash) {
3300 &$func($volid, $volhash->{$volid}, @param);
3304 sub conf_has_serial
{
3307 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3308 if ($conf->{"serial$i"}) {
3316 sub conf_has_audio
{
3317 my ($conf, $id) = @_;
3320 my $audio = $conf->{"audio$id"};
3321 return undef if !defined($audio);
3323 my $audioproperties = PVE
::JSONSchema
::parse_property_string
($audio_fmt, $audio);
3324 my $audiodriver = $audioproperties->{driver
} // 'spice';
3327 dev
=> $audioproperties->{device
},
3328 dev_id
=> "audiodev$id",
3329 backend
=> $audiodriver,
3330 backend_id
=> "$audiodriver-backend${id}",
3334 sub vga_conf_has_spice
{
3337 my $vgaconf = parse_vga
($vga);
3338 my $vgatype = $vgaconf->{type
};
3339 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
3346 return get_host_arch
() eq $arch;
3351 return $conf->{arch
} // get_host_arch
();
3354 my $default_machines = {
3359 sub get_vm_machine
{
3360 my ($conf, $forcemachine, $arch, $add_pve_version) = @_;
3362 my $machine = $forcemachine || $conf->{machine
};
3364 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
3366 $machine ||= $default_machines->{$arch};
3367 $machine .= "+pve$PVE::QemuServer::Machine::PVE_MACHINE_VERSION" if $add_pve_version;
3373 sub get_ovmf_files
($) {
3376 my $ovmf = $OVMF->{$arch}
3377 or die "no OVMF images known for architecture '$arch'\n";
3383 aarch64
=> '/usr/bin/qemu-system-aarch64',
3384 x86_64
=> '/usr/bin/qemu-system-x86_64',
3386 sub get_command_for_arch
($) {
3388 return '/usr/bin/kvm' if is_native
($arch);
3390 my $cmd = $Arch2Qemu->{$arch}
3391 or die "don't know how to emulate architecture '$arch'\n";
3395 sub get_cpu_options
{
3396 my ($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough) = @_;
3399 my $ostype = $conf->{ostype
};
3401 my $cpu = $kvm ?
"kvm64" : "qemu64";
3402 if ($arch eq 'aarch64') {
3403 $cpu = 'cortex-a57';
3406 if (my $cputype = $conf->{cpu
}) {
3407 my $cpuconf = PVE
::JSONSchema
::parse_property_string
($cpu_fmt, $cputype)
3408 or die "Cannot parse cpu description: $cputype\n";
3409 $cpu = $cpuconf->{cputype
};
3410 $kvm_off = 1 if $cpuconf->{hidden
};
3411 $hv_vendor_id = $cpuconf->{'hv-vendor-id'};
3413 if (defined(my $flags = $cpuconf->{flags
})) {
3414 push @$cpuFlags, split(";", $flags);
3418 push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64' && $arch eq 'x86_64';
3420 push @$cpuFlags , '-x2apic' if $ostype && $ostype eq 'solaris';
3422 push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32';
3424 push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/;
3426 if (min_version
($machine_version, 2, 3) && $arch eq 'x86_64') {
3428 push @$cpuFlags , '+kvm_pv_unhalt' if $kvm;
3429 push @$cpuFlags , '+kvm_pv_eoi' if $kvm;
3432 add_hyperv_enlightenments
($cpuFlags, $winversion, $machine_version, $conf->{bios
}, $gpu_passthrough, $hv_vendor_id) if $kvm;
3434 push @$cpuFlags, 'enforce' if $cpu ne 'host' && $kvm && $arch eq 'x86_64';
3436 push @$cpuFlags, 'kvm=off' if $kvm_off;
3438 if (my $cpu_vendor = $cpu_vendor_list->{$cpu}) {
3439 push @$cpuFlags, "vendor=${cpu_vendor}"
3440 if $cpu_vendor ne 'default';
3441 } elsif ($arch ne 'aarch64') {
3442 die "internal error"; # should not happen
3445 $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags);
3447 return ('-cpu', $cpu);
3450 sub config_to_command
{
3451 my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_;
3454 my $globalFlags = [];
3455 my $machineFlags = [];
3460 my $vernum = 0; # unknown
3461 my $ostype = $conf->{ostype
};
3462 my $winversion = windows_version
($ostype);
3463 my $kvm = $conf->{kvm
};
3465 my $arch = get_vm_arch
($conf);
3466 my $kvm_binary = get_command_for_arch
($arch);
3467 my $kvmver = kvm_user_version
($kvm_binary);
3469 my $add_pve_version = min_version
($kvmver, 4, 1);
3471 my $machine_type = get_vm_machine
($conf, $forcemachine, $arch, $add_pve_version);
3472 my $machine_version = PVE
::QemuServer
::Machine
::extract_version
($machine_type, $kvmver);
3473 $kvm //= 1 if is_native
($arch);
3476 die "KVM virtualisation configured, but not available. Either disable in VM configuration or enable in BIOS.\n"
3477 if !defined kvm_version
();
3480 if ($kvmver =~ m/^(\d+)\.(\d+)$/) {
3481 $vernum = $1*1000000+$2*1000;
3482 } elsif ($kvmver =~ m/^(\d+)\.(\d+)\.(\d+)$/) {
3483 $vernum = $1*1000000+$2*1000+$3;
3486 die "detected old qemu-kvm binary ($kvmver)\n" if $vernum < 15000;
3488 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
3489 my $hotplug_features = parse_hotplug_features
(defined($conf->{hotplug
}) ?
$conf->{hotplug
} : '1');
3490 my $use_old_bios_files = undef;
3491 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
3493 my $cpuunits = defined($conf->{cpuunits
}) ?
3494 $conf->{cpuunits
} : $defaults->{cpuunits
};
3496 push @$cmd, $kvm_binary;
3498 push @$cmd, '-id', $vmid;
3500 my $vmname = $conf->{name
} || "vm$vmid";
3502 push @$cmd, '-name', $vmname;
3506 my $qmpsocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid);
3507 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait";
3508 push @$cmd, '-mon', "chardev=qmp,mode=control";
3510 if (min_version
($machine_version, 2, 12)) {
3511 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3512 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3515 push @$cmd, '-pidfile' , PVE
::QemuServer
::Helpers
::pidfile_name
($vmid);
3517 push @$cmd, '-daemonize';
3519 if ($conf->{smbios1
}) {
3520 my $smbios_conf = parse_smbios1
($conf->{smbios1
});
3521 if ($smbios_conf->{base64
}) {
3522 # Do not pass base64 flag to qemu
3523 delete $smbios_conf->{base64
};
3524 my $smbios_string = "";
3525 foreach my $key (keys %$smbios_conf) {
3527 if ($key eq "uuid") {
3528 $value = $smbios_conf->{uuid
}
3530 $value = decode_base64
($smbios_conf->{$key});
3532 # qemu accepts any binary data, only commas need escaping by double comma
3534 $smbios_string .= "," . $key . "=" . $value if $value;
3536 push @$cmd, '-smbios', "type=1" . $smbios_string;
3538 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3542 if ($conf->{vmgenid
}) {
3543 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid
};
3546 my ($ovmf_code, $ovmf_vars) = get_ovmf_files
($arch);
3547 if ($conf->{bios
} && $conf->{bios
} eq 'ovmf') {
3548 die "uefi base image not found\n" if ! -f
$ovmf_code;
3552 if (my $efidisk = $conf->{efidisk0
}) {
3553 my $d = PVE
::JSONSchema
::parse_property_string
($efidisk_fmt, $efidisk);
3554 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($d->{file
}, 1);
3555 $format = $d->{format
};
3557 $path = PVE
::Storage
::path
($storecfg, $d->{file
});
3558 if (!defined($format)) {
3559 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
3560 $format = qemu_img_format
($scfg, $volname);
3564 die "efidisk format must be specified\n"
3565 if !defined($format);
3568 warn "no efidisk configured! Using temporary efivars disk.\n";
3569 $path = "/tmp/$vmid-ovmf.fd";
3570 PVE
::Tools
::file_copy
($ovmf_vars, $path, -s
$ovmf_vars);
3574 push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$ovmf_code";
3575 push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0,file=$path";
3580 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3581 if (min_version
($machine_version, 4, 0)) {
3582 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3584 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3588 # add usb controllers
3589 my @usbcontrollers = PVE
::QemuServer
::USB
::get_usb_controllers
($conf, $bridges, $arch, $machine_type, $usbdesc->{format
}, $MAX_USB_DEVICES);
3590 push @$devices, @usbcontrollers if @usbcontrollers;
3591 my $vga = parse_vga
($conf->{vga
});
3593 my $qxlnum = vga_conf_has_spice
($conf->{vga
});
3594 $vga->{type
} = 'qxl' if $qxlnum;
3596 if (!$vga->{type
}) {
3597 if ($arch eq 'aarch64') {
3598 $vga->{type
} = 'virtio';
3599 } elsif (min_version
($machine_version, 2, 9)) {
3600 $vga->{type
} = (!$winversion || $winversion >= 6) ?
'std' : 'cirrus';
3602 $vga->{type
} = ($winversion >= 6) ?
'std' : 'cirrus';
3606 # enable absolute mouse coordinates (needed by vnc)
3608 if (defined($conf->{tablet
})) {
3609 $tablet = $conf->{tablet
};
3611 $tablet = $defaults->{tablet
};
3612 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3613 $tablet = 0 if $vga->{type
} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3617 push @$devices, '-device', print_tabletdevice_full
($conf, $arch) if $tablet;
3618 my $kbd = print_keyboarddevice_full
($conf, $arch);
3619 push @$devices, '-device', $kbd if defined($kbd);
3623 my $gpu_passthrough;
3626 for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
3627 my $id = "hostpci$i";
3628 my $d = parse_hostpci
($conf->{$id});
3631 if (my $pcie = $d->{pcie
}) {
3632 die "q35 machine model is not enabled" if !$q35;
3633 # win7 wants to have the pcie devices directly on the pcie bus
3634 # instead of in the root port
3635 if ($winversion == 7) {
3636 $pciaddr = print_pcie_addr
("${id}bus0");
3638 # add more root ports if needed, 4 are present by default
3639 # by pve-q35 cfgs, rest added here on demand.
3641 push @$devices, '-device', print_pcie_root_port
($i);
3643 $pciaddr = print_pcie_addr
($id);
3646 $pciaddr = print_pci_addr
($id, $bridges, $arch, $machine_type);
3650 if ($d->{'x-vga'}) {
3651 $xvga = ',x-vga=on' if !($conf->{bios
} && $conf->{bios
} eq 'ovmf');
3653 $vga->{type
} = 'none' if !defined($conf->{vga
});
3654 $gpu_passthrough = 1;
3657 my $pcidevices = $d->{pciid
};
3658 my $multifunction = 1 if @$pcidevices > 1;
3661 if ($d->{mdev
} && scalar(@$pcidevices) == 1) {
3662 my $pci_id = $pcidevices->[0]->{id
};
3663 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $i);
3664 $sysfspath = "/sys/bus/pci/devices/$pci_id/$uuid";
3665 } elsif ($d->{mdev
}) {
3666 warn "ignoring mediated device '$id' with multifunction device\n";
3670 foreach my $pcidevice (@$pcidevices) {
3671 my $devicestr = "vfio-pci";
3674 $devicestr .= ",sysfsdev=$sysfspath";
3676 $devicestr .= ",host=$pcidevice->{id}";
3679 my $mf_addr = $multifunction ?
".$j" : '';
3680 $devicestr .= ",id=${id}${mf_addr}${pciaddr}${mf_addr}";
3683 $devicestr .= ',rombar=0' if defined($d->{rombar
}) && !$d->{rombar
};
3684 $devicestr .= "$xvga";
3685 $devicestr .= ",multifunction=on" if $multifunction;
3686 $devicestr .= ",romfile=/usr/share/kvm/$d->{romfile}" if $d->{romfile
};
3689 push @$devices, '-device', $devicestr;
3695 my $usb_dev_features = {};
3696 $usb_dev_features->{spice_usb3
} = 1 if min_version
($machine_version, 4, 0);
3698 my @usbdevices = PVE
::QemuServer
::USB
::get_usb_devices
($conf, $usbdesc->{format
}, $MAX_USB_DEVICES, $usb_dev_features);
3699 push @$devices, @usbdevices if @usbdevices;
3701 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3702 if (my $path = $conf->{"serial$i"}) {
3703 if ($path eq 'socket') {
3704 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3705 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server,nowait";
3706 # On aarch64, serial0 is the UART device. Qemu only allows
3707 # connecting UART devices via the '-serial' command line, as
3708 # the device has a fixed slot on the hardware...
3709 if ($arch eq 'aarch64' && $i == 0) {
3710 push @$devices, '-serial', "chardev:serial$i";
3712 push @$devices, '-device', "isa-serial,chardev=serial$i";
3715 die "no such serial device\n" if ! -c
$path;
3716 push @$devices, '-chardev', "tty,id=serial$i,path=$path";
3717 push @$devices, '-device', "isa-serial,chardev=serial$i";
3723 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3724 if (my $path = $conf->{"parallel$i"}) {
3725 die "no such parallel device\n" if ! -c
$path;
3726 my $devtype = $path =~ m!^/dev/usb/lp! ?
'tty' : 'parport';
3727 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3728 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3732 if (my $audio = conf_has_audio
($conf)) {
3734 my $audiopciaddr = print_pci_addr
("audio0", $bridges, $arch, $machine_type);
3736 my $id = $audio->{dev_id
};
3737 if ($audio->{dev
} eq 'AC97') {
3738 push @$devices, '-device', "AC97,id=${id}${audiopciaddr}";
3739 } elsif ($audio->{dev
} =~ /intel\-hda$/) {
3740 push @$devices, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
3741 push @$devices, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0";
3742 push @$devices, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1";
3744 die "unkown audio device '$audio->{dev}', implement me!";
3747 push @$devices, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
3751 $sockets = $conf->{smp
} if $conf->{smp
}; # old style - no longer iused
3752 $sockets = $conf->{sockets
} if $conf->{sockets
};
3754 my $cores = $conf->{cores
} || 1;
3756 my $maxcpus = $sockets * $cores;
3758 my $vcpus = $conf->{vcpus
} ?
$conf->{vcpus
} : $maxcpus;
3760 my $allowed_vcpus = $cpuinfo->{cpus
};
3762 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n"
3763 if ($allowed_vcpus < $maxcpus);
3765 if($hotplug_features->{cpu
} && min_version
($machine_version, 2, 7)) {
3767 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3768 for (my $i = 2; $i <= $vcpus; $i++) {
3769 my $cpustr = print_cpu_device
($conf,$i);
3770 push @$cmd, '-device', $cpustr;
3775 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3777 push @$cmd, '-nodefaults';
3779 my $bootorder = $conf->{boot
} || $confdesc->{boot
}->{default};
3781 my $bootindex_hash = {};
3783 foreach my $o (split(//, $bootorder)) {
3784 $bootindex_hash->{$o} = $i*100;
3788 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3790 push @$cmd, '-no-acpi' if defined($conf->{acpi
}) && $conf->{acpi
} == 0;
3792 push @$cmd, '-no-reboot' if defined($conf->{reboot
}) && $conf->{reboot
} == 0;
3794 if ($vga->{type
} && $vga->{type
} !~ m/^serial\d+$/ && $vga->{type
} ne 'none'){
3795 push @$devices, '-device', print_vga_device
($conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
3796 my $socket = PVE
::QemuServer
::Helpers
::vnc_socket
($vmid);
3797 push @$cmd, '-vnc', "unix:$socket,password";
3799 push @$cmd, '-vga', 'none' if $vga->{type
} eq 'none';
3800 push @$cmd, '-nographic';
3804 my $tdf = defined($conf->{tdf
}) ?
$conf->{tdf
} : $defaults->{tdf
};
3806 my $useLocaltime = $conf->{localtime};
3808 if ($winversion >= 5) { # windows
3809 $useLocaltime = 1 if !defined($conf->{localtime});
3811 # use time drift fix when acpi is enabled
3812 if (!(defined($conf->{acpi
}) && $conf->{acpi
} == 0)) {
3813 $tdf = 1 if !defined($conf->{tdf
});
3817 if ($winversion >= 6) {
3818 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3819 push @$cmd, '-no-hpet';
3822 push @$rtcFlags, 'driftfix=slew' if $tdf;
3825 push @$machineFlags, 'accel=tcg';
3828 if ($machine_type) {
3829 push @$machineFlags, "type=${machine_type}";
3832 if (($conf->{startdate
}) && ($conf->{startdate
} ne 'now')) {
3833 push @$rtcFlags, "base=$conf->{startdate}";
3834 } elsif ($useLocaltime) {
3835 push @$rtcFlags, 'base=localtime';
3838 push @$cmd, get_cpu_options
($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
3840 PVE
::QemuServer
::Memory
::config
($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
3842 push @$cmd, '-S' if $conf->{freeze
};
3844 push @$cmd, '-k', $conf->{keyboard
} if defined($conf->{keyboard
});
3846 my $guest_agent = parse_guest_agent
($conf);
3848 if ($guest_agent->{enabled
}) {
3849 my $qgasocket = PVE
::QemuServer
::Helpers
::qmp_socket
($vmid, 1);
3850 push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0";
3852 if (!$guest_agent->{type
} || $guest_agent->{type
} eq 'virtio') {
3853 my $pciaddr = print_pci_addr
("qga0", $bridges, $arch, $machine_type);
3854 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3855 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3856 } elsif ($guest_agent->{type
} eq 'isa') {
3857 push @$devices, '-device', "isa-serial,chardev=qga0";
3866 for(my $i = 1; $i < $qxlnum; $i++){
3867 push @$devices, '-device', print_vga_device
($conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
3870 # assume other OS works like Linux
3871 my ($ram, $vram) = ("134217728", "67108864");
3872 if ($vga->{memory
}) {
3873 $ram = PVE
::Tools
::convert_size
($qxlnum*4*$vga->{memory
}, 'mb' => 'b');
3874 $vram = PVE
::Tools
::convert_size
($qxlnum*2*$vga->{memory
}, 'mb' => 'b');
3876 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3877 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3881 my $pciaddr = print_pci_addr
("spice", $bridges, $arch, $machine_type);
3883 my $nodename = PVE
::INotify
::nodename
();
3884 my $pfamily = PVE
::Tools
::get_host_address_family
($nodename);
3885 my @nodeaddrs = PVE
::Tools
::getaddrinfo_all
('localhost', family
=> $pfamily);
3886 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3888 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3889 push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
3890 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3892 my $localhost = PVE
::Network
::addr_to_ip
($nodeaddrs[0]->{addr
});
3893 $spice_port = PVE
::Tools
::next_spice_port
($pfamily, $localhost);
3895 my $spice_enhancement = PVE
::JSONSchema
::parse_property_string
($spice_enhancements_fmt, $conf->{spice_enhancements
} // '');
3896 if ($spice_enhancement->{foldersharing
}) {
3897 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3898 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3901 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3902 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}" if $spice_enhancement->{videostreaming
};
3903 push @$devices, '-spice', "$spice_opts";
3906 # enable balloon by default, unless explicitly disabled
3907 if (!defined($conf->{balloon
}) || $conf->{balloon
}) {
3908 $pciaddr = print_pci_addr
("balloon0", $bridges, $arch, $machine_type);
3909 push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr";
3912 if ($conf->{watchdog
}) {
3913 my $wdopts = parse_watchdog
($conf->{watchdog
});
3914 $pciaddr = print_pci_addr
("watchdog", $bridges, $arch, $machine_type);
3915 my $watchdog = $wdopts->{model
} || 'i6300esb';
3916 push @$devices, '-device', "$watchdog$pciaddr";
3917 push @$devices, '-watchdog-action', $wdopts->{action
} if $wdopts->{action
};
3921 my $scsicontroller = {};
3922 my $ahcicontroller = {};
3923 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : $defaults->{scsihw
};
3925 # Add iscsi initiator name if available
3926 if (my $initiator = get_initiator_name
()) {
3927 push @$devices, '-iscsi', "initiator-name=$initiator";
3930 foreach_drive
($conf, sub {
3931 my ($ds, $drive) = @_;
3933 if (PVE
::Storage
::parse_volume_id
($drive->{file
}, 1)) {
3934 push @$vollist, $drive->{file
};
3937 # ignore efidisk here, already added in bios/fw handling code above
3938 return if $drive->{interface
} eq 'efidisk';
3940 $use_virtio = 1 if $ds =~ m/^virtio/;
3942 if (drive_is_cdrom
($drive)) {
3943 if ($bootindex_hash->{d
}) {
3944 $drive->{bootindex
} = $bootindex_hash->{d
};
3945 $bootindex_hash->{d
} += 1;
3948 if ($bootindex_hash->{c
}) {
3949 $drive->{bootindex
} = $bootindex_hash->{c
} if $conf->{bootdisk
} && ($conf->{bootdisk
} eq $ds);
3950 $bootindex_hash->{c
} += 1;
3954 if($drive->{interface
} eq 'virtio'){
3955 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread
};
3958 if ($drive->{interface
} eq 'scsi') {
3960 my ($maxdev, $controller, $controller_prefix) = scsihw_infos
($conf, $drive);
3962 $pciaddr = print_pci_addr
("$controller_prefix$controller", $bridges, $arch, $machine_type);
3963 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ?
"virtio-scsi-pci" : $scsihw;
3966 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{iothread
}){
3967 $iothread .= ",iothread=iothread-$controller_prefix$controller";
3968 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
3969 } elsif ($drive->{iothread
}) {
3970 warn "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n";
3974 if($conf->{scsihw
} && $conf->{scsihw
} eq "virtio-scsi-single" && $drive->{queues
}){
3975 $queues = ",num_queues=$drive->{queues}";
3978 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues" if !$scsicontroller->{$controller};
3979 $scsicontroller->{$controller}=1;
3982 if ($drive->{interface
} eq 'sata') {
3983 my $controller = int($drive->{index} / $MAX_SATA_DISKS);
3984 $pciaddr = print_pci_addr
("ahci$controller", $bridges, $arch, $machine_type);
3985 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
3986 $ahcicontroller->{$controller}=1;
3989 my $drive_cmd = print_drive_full
($storecfg, $vmid, $drive);
3990 push @$devices, '-drive',$drive_cmd;
3991 push @$devices, '-device', print_drivedevice_full
($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
3994 for (my $i = 0; $i < $MAX_NETS; $i++) {
3995 next if !$conf->{"net$i"};
3996 my $d = parse_net
($conf->{"net$i"});
3999 $use_virtio = 1 if $d->{model
} eq 'virtio';
4001 if ($bootindex_hash->{n
}) {
4002 $d->{bootindex
} = $bootindex_hash->{n
};
4003 $bootindex_hash->{n
} += 1;
4006 my $netdevfull = print_netdev_full
($vmid, $conf, $arch, $d, "net$i");
4007 push @$devices, '-netdev', $netdevfull;
4009 my $netdevicefull = print_netdevice_full
($vmid, $conf, $d, "net$i", $bridges, $use_old_bios_files, $arch, $machine_type);
4010 push @$devices, '-device', $netdevicefull;
4013 if ($conf->{ivshmem
}) {
4014 my $ivshmem = PVE
::JSONSchema
::parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
4018 $bus = print_pcie_addr
("ivshmem");
4020 $bus = print_pci_addr
("ivshmem", $bridges, $arch, $machine_type);
4023 my $ivshmem_name = $ivshmem->{name
} // $vmid;
4024 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
4026 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
4027 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path,size=$ivshmem->{size}M";
4032 if (min_version
($machine_version, 2, 3)) {
4037 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
4039 for my $k (sort {$b cmp $a} keys %$bridges) {
4040 $pciaddr = print_pci_addr
("pci.$k", undef, $arch, $machine_type);
4041 unshift @$devices, '-device', "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr" if $k > 0;
4045 push @$cmd, @$devices;
4046 push @$cmd, '-rtc', join(',', @$rtcFlags)
4047 if scalar(@$rtcFlags);
4048 push @$cmd, '-machine', join(',', @$machineFlags)
4049 if scalar(@$machineFlags);
4050 push @$cmd, '-global', join(',', @$globalFlags)
4051 if scalar(@$globalFlags);
4053 if (my $vmstate = $conf->{vmstate
}) {
4054 my $statepath = PVE
::Storage
::path
($storecfg, $vmstate);
4055 push @$vollist, $vmstate;
4056 push @$cmd, '-loadstate', $statepath;
4057 print "activating and using '$vmstate' as vmstate\n";
4061 if ($conf->{args
}) {
4062 my $aa = PVE
::Tools
::split_args
($conf->{args
});
4066 return wantarray ?
($cmd, $vollist, $spice_port) : $cmd;
4072 my $res = mon_cmd
($vmid, 'query-spice');
4074 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
4077 sub vm_devices_list
{
4080 my $res = mon_cmd
($vmid, 'query-pci');
4081 my $devices_to_check = [];
4083 foreach my $pcibus (@$res) {
4084 push @$devices_to_check, @{$pcibus->{devices
}},
4087 while (@$devices_to_check) {
4089 for my $d (@$devices_to_check) {
4090 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
4091 next if !$d->{'pci_bridge'};
4093 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices
}});
4094 push @$to_check, @{$d->{'pci_bridge'}->{devices
}};
4096 $devices_to_check = $to_check;
4099 my $resblock = mon_cmd
($vmid, 'query-block');
4100 foreach my $block (@$resblock) {
4101 if($block->{device
} =~ m/^drive-(\S+)/){
4106 my $resmice = mon_cmd
($vmid, 'query-mice');
4107 foreach my $mice (@$resmice) {
4108 if ($mice->{name
} eq 'QEMU HID Tablet') {
4109 $devices->{tablet
} = 1;
4114 # for usb devices there is no query-usb
4115 # but we can iterate over the entries in
4116 # qom-list path=/machine/peripheral
4117 my $resperipheral = mon_cmd
($vmid, 'qom-list', path
=> '/machine/peripheral');
4118 foreach my $per (@$resperipheral) {
4119 if ($per->{name
} =~ m/^usb\d+$/) {
4120 $devices->{$per->{name
}} = 1;
4128 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4130 my $q35 = PVE
::QemuServer
::Machine
::machine_type_is_q35
($conf);
4132 my $devices_list = vm_devices_list
($vmid);
4133 return 1 if defined($devices_list->{$deviceid});
4135 qemu_add_pci_bridge
($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type); # add PCI bridge if we need it for the device
4137 if ($deviceid eq 'tablet') {
4139 qemu_deviceadd
($vmid, print_tabletdevice_full
($conf, $arch));
4141 } elsif ($deviceid eq 'keyboard') {
4143 qemu_deviceadd
($vmid, print_keyboarddevice_full
($conf, $arch));
4145 } elsif ($deviceid =~ m/^usb(\d+)$/) {
4147 die "usb hotplug currently not reliable\n";
4148 # since we can't reliably hot unplug all added usb devices
4149 # and usb passthrough disables live migration
4150 # we disable usb hotplugging for now
4151 qemu_deviceadd
($vmid, PVE
::QemuServer
::USB
::print_usbdevice_full
($conf, $deviceid, $device));
4153 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4155 qemu_iothread_add
($vmid, $deviceid, $device);
4157 qemu_driveadd
($storecfg, $vmid, $device);
4158 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, $arch, $machine_type);
4160 qemu_deviceadd
($vmid, $devicefull);
4161 eval { qemu_deviceaddverify
($vmid, $deviceid); };
4163 eval { qemu_drivedel
($vmid, $deviceid); };
4168 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4171 my $scsihw = defined($conf->{scsihw
}) ?
$conf->{scsihw
} : "lsi";
4172 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4173 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ?
"virtio-scsi-pci" : $scsihw;
4175 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
4177 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread
}) {
4178 qemu_iothread_add
($vmid, $deviceid, $device);
4179 $devicefull .= ",iothread=iothread-$deviceid";
4182 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues
}) {
4183 $devicefull .= ",num_queues=$device->{queues}";
4186 qemu_deviceadd
($vmid, $devicefull);
4187 qemu_deviceaddverify
($vmid, $deviceid);
4189 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4191 qemu_findorcreatescsihw
($storecfg,$conf, $vmid, $device, $arch, $machine_type);
4192 qemu_driveadd
($storecfg, $vmid, $device);
4194 my $devicefull = print_drivedevice_full
($storecfg, $conf, $vmid, $device, $arch, $machine_type);
4195 eval { qemu_deviceadd
($vmid, $devicefull); };
4197 eval { qemu_drivedel
($vmid, $deviceid); };
4202 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4204 return undef if !qemu_netdevadd
($vmid, $conf, $arch, $device, $deviceid);
4206 my $machine_type = PVE
::QemuServer
::Machine
::qemu_machine_pxe
($vmid, $conf);
4207 my $use_old_bios_files = undef;
4208 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files
($machine_type);
4210 my $netdevicefull = print_netdevice_full
($vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type);
4211 qemu_deviceadd
($vmid, $netdevicefull);
4213 qemu_deviceaddverify
($vmid, $deviceid);
4214 qemu_set_link_status
($vmid, $deviceid, !$device->{link_down
});
4217 eval { qemu_netdevdel
($vmid, $deviceid); };
4222 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
4225 my $pciaddr = print_pci_addr
($deviceid, undef, $arch, $machine_type);
4226 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
4228 qemu_deviceadd
($vmid, $devicefull);
4229 qemu_deviceaddverify
($vmid, $deviceid);
4232 die "can't hotplug device '$deviceid'\n";
4238 # fixme: this should raise exceptions on error!
4239 sub vm_deviceunplug
{
4240 my ($vmid, $conf, $deviceid) = @_;
4242 my $devices_list = vm_devices_list
($vmid);
4243 return 1 if !defined($devices_list->{$deviceid});
4245 die "can't unplug bootdisk" if $conf->{bootdisk
} && $conf->{bootdisk
} eq $deviceid;
4247 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard') {
4249 qemu_devicedel
($vmid, $deviceid);
4251 } elsif ($deviceid =~ m/^usb\d+$/) {
4253 die "usb hotplug currently not reliable\n";
4254 # when unplugging usb devices this way,
4255 # there may be remaining usb controllers/hubs
4256 # so we disable it for now
4257 qemu_devicedel
($vmid, $deviceid);
4258 qemu_devicedelverify
($vmid, $deviceid);
4260 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4262 qemu_devicedel
($vmid, $deviceid);
4263 qemu_devicedelverify
($vmid, $deviceid);
4264 qemu_drivedel
($vmid, $deviceid);
4265 qemu_iothread_del
($conf, $vmid, $deviceid);
4267 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4269 qemu_devicedel
($vmid, $deviceid);
4270 qemu_devicedelverify
($vmid, $deviceid);
4271 qemu_iothread_del
($conf, $vmid, $deviceid);
4273 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4275 qemu_devicedel
($vmid, $deviceid);
4276 qemu_drivedel
($vmid, $deviceid);
4277 qemu_deletescsihw
($conf, $vmid, $deviceid);
4279 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4281 qemu_devicedel
($vmid, $deviceid);
4282 qemu_devicedelverify
($vmid, $deviceid);
4283 qemu_netdevdel
($vmid, $deviceid);
4286 die "can't unplug device '$deviceid'\n";
4292 sub qemu_deviceadd
{
4293 my ($vmid, $devicefull) = @_;
4295 $devicefull = "driver=".$devicefull;
4296 my %options = split(/[=,]/, $devicefull);
4298 mon_cmd
($vmid, "device_add" , %options);
4301 sub qemu_devicedel
{
4302 my ($vmid, $deviceid) = @_;
4304 my $ret = mon_cmd
($vmid, "device_del", id
=> $deviceid);
4307 sub qemu_iothread_add
{
4308 my($vmid, $deviceid, $device) = @_;
4310 if ($device->{iothread
}) {
4311 my $iothreads = vm_iothreads_list
($vmid);
4312 qemu_objectadd
($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4316 sub qemu_iothread_del
{
4317 my($conf, $vmid, $deviceid) = @_;
4319 my $confid = $deviceid;
4320 if ($deviceid =~ m/^(?:virtioscsi|scsihw)(\d+)$/) {
4321 $confid = 'scsi' . $1;
4323 my $device = parse_drive
($confid, $conf->{$confid});
4324 if ($device->{iothread
}) {
4325 my $iothreads = vm_iothreads_list
($vmid);
4326 qemu_objectdel
($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4330 sub qemu_objectadd
{
4331 my($vmid, $objectid, $qomtype) = @_;
4333 mon_cmd
($vmid, "object-add", id
=> $objectid, "qom-type" => $qomtype);
4338 sub qemu_objectdel
{
4339 my($vmid, $objectid) = @_;
4341 mon_cmd
($vmid, "object-del", id
=> $objectid);
4347 my ($storecfg, $vmid, $device) = @_;
4349 my $drive = print_drive_full
($storecfg, $vmid, $device);
4350 $drive =~ s/\\/\\\\/g;
4351 my $ret = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "drive_add auto \"$drive\"");
4353 # If the command succeeds qemu prints: "OK
"
4354 return 1 if $ret =~ m/OK/s;
4356 die "adding drive failed
: $ret\n";
4360 my($vmid, $deviceid) = @_;
4362 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-
$deviceid");
4365 return 1 if $ret eq "";
4367 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4368 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4370 die "deleting drive
$deviceid failed
: $ret\n";
4373 sub qemu_deviceaddverify {
4374 my ($vmid, $deviceid) = @_;
4376 for (my $i = 0; $i <= 5; $i++) {
4377 my $devices_list = vm_devices_list($vmid);
4378 return 1 if defined($devices_list->{$deviceid});
4382 die "error on hotplug device
'$deviceid'\n";
4386 sub qemu_devicedelverify {
4387 my ($vmid, $deviceid) = @_;
4389 # need to verify that the device is correctly removed as device_del
4390 # is async and empty return is not reliable
4392 for (my $i = 0; $i <= 5; $i++) {
4393 my $devices_list = vm_devices_list($vmid);
4394 return 1 if !defined($devices_list->{$deviceid});
4398 die "error on hot-unplugging device
'$deviceid'\n";
4401 sub qemu_findorcreatescsihw {
4402 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4404 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4406 my $scsihwid="$controller_prefix$controller";
4407 my $devices_list = vm_devices_list($vmid);
4409 if(!defined($devices_list->{$scsihwid})) {
4410 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4416 sub qemu_deletescsihw {
4417 my ($conf, $vmid, $opt) = @_;
4419 my $device = parse_drive($opt, $conf->{$opt});
4421 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4422 vm_deviceunplug($vmid, $conf, "virtioscsi
$device->{index}");
4426 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4428 my $devices_list = vm_devices_list($vmid);
4429 foreach my $opt (keys %{$devices_list}) {
4430 if (PVE::QemuServer::is_valid_drivename($opt)) {
4431 my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt});
4432 if($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4438 my $scsihwid="scsihw
$controller";
4440 vm_deviceunplug($vmid, $conf, $scsihwid);
4445 sub qemu_add_pci_bridge {
4446 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4452 print_pci_addr($device, $bridges, $arch, $machine_type);
4454 while (my ($k, $v) = each %$bridges) {
4457 return 1 if !defined($bridgeid) || $bridgeid < 1;
4459 my $bridge = "pci
.$bridgeid";
4460 my $devices_list = vm_devices_list($vmid);
4462 if (!defined($devices_list->{$bridge})) {
4463 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4469 sub qemu_set_link_status {
4470 my ($vmid, $device, $up) = @_;
4472 mon_cmd($vmid, "set_link
", name => $device,
4473 up => $up ? JSON::true : JSON::false);
4476 sub qemu_netdevadd {
4477 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4479 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4480 my %options = split(/[=,]/, $netdev);
4482 mon_cmd($vmid, "netdev_add
", %options);
4486 sub qemu_netdevdel {
4487 my ($vmid, $deviceid) = @_;
4489 mon_cmd($vmid, "netdev_del
", id => $deviceid);
4492 sub qemu_usb_hotplug {
4493 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4497 # remove the old one first
4498 vm_deviceunplug($vmid, $conf, $deviceid);
4500 # check if xhci controller is necessary and available
4501 if ($device->{usb3}) {
4503 my $devicelist = vm_devices_list($vmid);
4505 if (!$devicelist->{xhci}) {
4506 my $pciaddr = print_pci_addr("xhci
", undef, $arch, $machine_type);
4507 qemu_deviceadd($vmid, "nec-usb-xhci
,id
=xhci
$pciaddr");
4510 my $d = parse_usb_device($device->{host});
4511 $d->{usb3} = $device->{usb3};
4514 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d, $arch, $machine_type);
4517 sub qemu_cpu_hotplug {
4518 my ($vmid, $conf, $vcpus) = @_;
4520 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
4523 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4524 $sockets = $conf->{sockets} if $conf->{sockets};
4525 my $cores = $conf->{cores} || 1;
4526 my $maxcpus = $sockets * $cores;
4528 $vcpus = $maxcpus if !$vcpus;
4530 die "you can
't add more vcpus than maxcpus\n"
4531 if $vcpus > $maxcpus;
4533 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4535 if ($vcpus < $currentvcpus) {
4537 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4539 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4540 qemu_devicedel($vmid, "cpu$i");
4542 my $currentrunningvcpus = undef;
4544 $currentrunningvcpus = mon_cmd($vmid, "query-cpus");
4545 last if scalar(@{$currentrunningvcpus}) == $i-1;
4546 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4550 #update conf after each succesfull cpu unplug
4551 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4552 PVE::QemuConfig->write_config($vmid, $conf);
4555 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4561 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus");
4562 die "vcpus in running vm does not match its configuration\n"
4563 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4565 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4567 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4568 my $cpustr = print_cpu_device($conf, $i);
4569 qemu_deviceadd($vmid, $cpustr);
4572 my $currentrunningvcpus = undef;
4574 $currentrunningvcpus = mon_cmd($vmid, "query-cpus");
4575 last if scalar(@{$currentrunningvcpus}) == $i;
4576 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4580 #update conf after each succesfull cpu hotplug
4581 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4582 PVE::QemuConfig->write_config($vmid, $conf);
4586 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4587 mon_cmd($vmid, "cpu-add", id => int($i));
4592 sub qemu_block_set_io_throttle {
4593 my ($vmid, $deviceid,
4594 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4595 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4596 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4597 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4599 return if !check_running($vmid) ;
4601 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4603 bps_rd => int($bps_rd),
4604 bps_wr => int($bps_wr),
4606 iops_rd => int($iops_rd),
4607 iops_wr => int($iops_wr),
4608 bps_max => int($bps_max),
4609 bps_rd_max => int($bps_rd_max),
4610 bps_wr_max => int($bps_wr_max),
4611 iops_max => int($iops_max),
4612 iops_rd_max => int($iops_rd_max),
4613 iops_wr_max => int($iops_wr_max),
4614 bps_max_length => int($bps_max_length),
4615 bps_rd_max_length => int($bps_rd_max_length),
4616 bps_wr_max_length => int($bps_wr_max_length),
4617 iops_max_length => int($iops_max_length),
4618 iops_rd_max_length => int($iops_rd_max_length),
4619 iops_wr_max_length => int($iops_wr_max_length),
4624 # old code, only used to shutdown old VM after update
4626 my ($fh, $timeout) = @_;
4628 my $sel = new IO::Select;
4635 while (scalar (@ready = $sel->can_read($timeout))) {
4637 if ($count = $fh->sysread($buf, 8192)) {
4638 if ($buf =~ /^(.*)\(qemu\) $/s) {
4645 if (!defined($count)) {
4652 die "monitor read timeout\n" if !scalar(@ready);
4657 sub qemu_block_resize {
4658 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4660 my $running = check_running($vmid);
4662 $size = 0 if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4664 return if !$running;
4666 mon_cmd($vmid, "block_resize", device => $deviceid, size => int($size));
4670 sub qemu_volume_snapshot {
4671 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4673 my $running = check_running($vmid);
4675 if ($running && do_snapshots_with_qemu($storecfg, $volid)){
4676 mon_cmd($vmid, 'blockdev-snapshot-internal-sync
', device => $deviceid, name => $snap);
4678 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4682 sub qemu_volume_snapshot_delete {
4683 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4685 my $running = check_running($vmid);
4690 my $conf = PVE::QemuConfig->load_config($vmid);
4691 foreach_drive($conf, sub {
4692 my ($ds, $drive) = @_;
4693 $running = 1 if $drive->{file} eq $volid;
4697 if ($running && do_snapshots_with_qemu($storecfg, $volid)){
4698 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync
', device => $deviceid, name => $snap);
4700 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4704 sub set_migration_caps {
4710 "auto-converge" => 1,
4712 "x-rdma-pin-all" => 0,
4717 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4719 for my $supported_capability (@$supported_capabilities) {
4721 capability => $supported_capability->{capability},
4722 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4726 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4729 my $fast_plug_option = {
4737 'vmstatestorage
' => 1,
4742 # hotplug changes in [PENDING]
4743 # $selection hash can be used to only apply specified options, for
4744 # example: { cores => 1 } (only apply changed 'cores
')
4745 # $errors ref is used to return error messages
4746 sub vmconfig_hotplug_pending {
4747 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4749 my $defaults = load_defaults();
4750 my $arch = get_vm_arch($conf);
4751 my $machine_type = get_vm_machine($conf, undef, $arch);
4753 # commit values which do not have any impact on running VM first
4754 # Note: those option cannot raise errors, we we do not care about
4755 # $selection and always apply them.
4757 my $add_error = sub {
4758 my ($opt, $msg) = @_;
4759 $errors->{$opt} = "hotplug problem - $msg";
4763 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4764 if ($fast_plug_option->{$opt}) {
4765 $conf->{$opt} = $conf->{pending}->{$opt};
4766 delete $conf->{pending}->{$opt};
4772 PVE::QemuConfig->write_config($vmid, $conf);
4773 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
4776 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
4778 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4779 foreach my $opt (sort keys %$pending_delete_hash) {
4780 next if $selection && !$selection->{$opt};
4781 my $force = $pending_delete_hash->{$opt}->{force};
4783 if ($opt eq 'hotplug
') {
4784 die "skip\n" if ($conf->{hotplug} =~ /memory/);
4785 } elsif ($opt eq 'tablet
') {
4786 die "skip\n" if !$hotplug_features->{usb};
4787 if ($defaults->{tablet}) {
4788 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4789 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4790 if $arch eq 'aarch64
';
4792 vm_deviceunplug($vmid, $conf, 'tablet
');
4793 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4795 } elsif ($opt =~ m/^usb\d+/) {
4797 # since we cannot reliably hot unplug usb devices
4798 # we are disabling it
4799 die "skip\n" if !$hotplug_features->{usb} || $conf->{$opt} =~ m/spice/i;
4800 vm_deviceunplug($vmid, $conf, $opt);
4801 } elsif ($opt eq 'vcpus
') {
4802 die "skip\n" if !$hotplug_features->{cpu};
4803 qemu_cpu_hotplug($vmid, $conf, undef);
4804 } elsif ($opt eq 'balloon
') {
4805 # enable balloon device is not hotpluggable
4806 die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
4807 # here we reset the ballooning value to memory
4808 my $balloon = $conf->{memory} || $defaults->{memory};
4809 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4810 } elsif ($fast_plug_option->{$opt}) {
4812 } elsif ($opt =~ m/^net(\d+)$/) {
4813 die "skip\n" if !$hotplug_features->{network};
4814 vm_deviceunplug($vmid, $conf, $opt);
4815 } elsif (is_valid_drivename($opt)) {
4816 die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
4817 vm_deviceunplug($vmid, $conf, $opt);
4818 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4819 } elsif ($opt =~ m/^memory$/) {
4820 die "skip\n" if !$hotplug_features->{memory};
4821 PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
4822 } elsif ($opt eq 'cpuunits
') {
4823 cgroups_write("cpu", $vmid, "cpu.shares", $defaults->{cpuunits});
4824 } elsif ($opt eq 'cpulimit
') {
4825 cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", -1);
4831 &$add_error($opt, $err) if $err ne "skip\n";
4833 # save new config if hotplug was successful
4834 delete $conf->{$opt};
4835 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4836 PVE::QemuConfig->write_config($vmid, $conf);
4837 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
4841 my ($apply_pending_cloudinit, $apply_pending_cloudinit_done);
4842 $apply_pending_cloudinit = sub {
4843 return if $apply_pending_cloudinit_done; # once is enough
4844 $apply_pending_cloudinit_done = 1; # once is enough
4846 my ($key, $value) = @_;
4848 my @cloudinit_opts = keys %$confdesc_cloudinit;
4849 foreach my $opt (keys %{$conf->{pending}}) {
4850 next if !grep { $_ eq $opt } @cloudinit_opts;
4851 $conf->{$opt} = delete $conf->{pending}->{$opt};
4854 my $new_conf = { %$conf };
4855 $new_conf->{$key} = $value;
4856 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($new_conf, $vmid);
4859 foreach my $opt (keys %{$conf->{pending}}) {
4860 next if $selection && !$selection->{$opt};
4861 my $value = $conf->{pending}->{$opt};
4863 if ($opt eq 'hotplug
') {
4864 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/);
4865 } elsif ($opt eq 'tablet
') {
4866 die "skip\n" if !$hotplug_features->{usb};
4868 vm_deviceplug($storecfg, $conf, $vmid, 'tablet
', $arch, $machine_type);
4869 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard
', $arch, $machine_type)
4870 if $arch eq 'aarch64
';
4871 } elsif ($value == 0) {
4872 vm_deviceunplug($vmid, $conf, 'tablet
');
4873 vm_deviceunplug($vmid, $conf, 'keyboard
') if $arch eq 'aarch64
';
4875 } elsif ($opt =~ m/^usb\d+$/) {
4877 # since we cannot reliably hot unplug usb devices
4878 # we are disabling it
4879 die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
4880 my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format}, $value) };
4881 die "skip\n" if !$d;
4882 qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d, $arch, $machine_type);
4883 } elsif ($opt eq 'vcpus
') {
4884 die "skip\n" if !$hotplug_features->{cpu};
4885 qemu_cpu_hotplug($vmid, $conf, $value);
4886 } elsif ($opt eq 'balloon
') {
4887 # enable/disable balloning device is not hotpluggable
4888 my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon});
4889 my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon});
4890 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
4892 # allow manual ballooning if shares is set to zero
4893 if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
4894 my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
4895 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4897 } elsif ($opt =~ m/^net(\d+)$/) {
4898 # some changes can be done without hotplug
4899 vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
4900 $vmid, $opt, $value, $arch, $machine_type);
4901 } elsif (is_valid_drivename($opt)) {
4902 die "skip\n" if $opt eq 'efidisk0
';
4903 # some changes can be done without hotplug
4904 my $drive = parse_drive($opt, $value);
4905 if (drive_is_cloudinit($drive)) {
4906 &$apply_pending_cloudinit($opt, $value);
4908 vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
4909 $vmid, $opt, $value, 1, $arch, $machine_type);
4910 } elsif ($opt =~ m/^memory$/) { #dimms
4911 die "skip\n" if !$hotplug_features->{memory};
4912 $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
4913 } elsif ($opt eq 'cpuunits
') {
4914 cgroups_write("cpu", $vmid, "cpu.shares", $conf->{pending}->{$opt});
4915 } elsif ($opt eq 'cpulimit
') {
4916 my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
4917 cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", $cpulimit);
4919 die "skip\n"; # skip non-hot-pluggable options
4923 &$add_error($opt, $err) if $err ne "skip\n";
4925 # save new config if hotplug was successful
4926 $conf->{$opt} = $value;
4927 delete $conf->{pending}->{$opt};
4928 PVE::QemuConfig->write_config($vmid, $conf);
4929 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
4934 sub try_deallocate_drive {
4935 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
4937 if (($force || $key =~ /^unused/) && !drive_is_cdrom($drive, 1)) {
4938 my $volid = $drive->{file};
4939 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
4940 my $sid = PVE::Storage::parse_volume_id($volid);
4941 $rpcenv->check($authuser, "/storage/$sid", ['Datastore
.AllocateSpace
']);
4943 # check if the disk is really unused
4944 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
4945 if is_volume_in_use($storecfg, $conf, $key, $volid);
4946 PVE::Storage::vdisk_free($storecfg, $volid);
4949 # If vm is not owner of this disk remove from config
4957 sub vmconfig_delete_or_detach_drive {
4958 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
4960 my $drive = parse_drive($opt, $conf->{$opt});
4962 my $rpcenv = PVE::RPCEnvironment::get();
4963 my $authuser = $rpcenv->get_user();
4966 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM
.Config
.Disk
']);
4967 try_deallocate_drive($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
4969 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $drive);
4975 sub vmconfig_apply_pending {
4976 my ($vmid, $conf, $storecfg) = @_;
4980 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4981 foreach my $opt (sort keys %$pending_delete_hash) {
4982 die "internal error" if $opt =~ m/^unused/;
4983 my $force = $pending_delete_hash->{$opt}->{force};
4984 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
4985 if (!defined($conf->{$opt})) {
4986 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4987 PVE::QemuConfig->write_config($vmid, $conf);
4988 } elsif (is_valid_drivename($opt)) {
4989 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4990 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4991 delete $conf->{$opt};
4992 PVE::QemuConfig->write_config($vmid, $conf);
4994 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4995 delete $conf->{$opt};
4996 PVE::QemuConfig->write_config($vmid, $conf);
5000 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
5002 foreach my $opt (keys %{$conf->{pending}}) { # add/change
5003 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
5005 if (defined($conf->{$opt}) && ($conf->{$opt} eq $conf->{pending}->{$opt})) {
5006 # skip if nothing changed
5007 } elsif (is_valid_drivename($opt)) {
5008 vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
5009 if defined($conf->{$opt});
5010 $conf->{$opt} = $conf->{pending}->{$opt};
5012 $conf->{$opt} = $conf->{pending}->{$opt};
5015 delete $conf->{pending}->{$opt};
5016 PVE::QemuConfig->write_config($vmid, $conf);
5020 my $safe_num_ne = sub {
5023 return 0 if !defined($a) && !defined($b);
5024 return 1 if !defined($a);
5025 return 1 if !defined($b);
5030 my $safe_string_ne = sub {
5033 return 0 if !defined($a) && !defined($b);
5034 return 1 if !defined($a);
5035 return 1 if !defined($b);
5040 sub vmconfig_update_net {
5041 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5043 my $newnet = parse_net($value);
5045 if ($conf->{$opt}) {
5046 my $oldnet = parse_net($conf->{$opt});
5048 if (&$safe_string_ne($oldnet->{model}, $newnet->{model}) ||
5049 &$safe_string_ne($oldnet->{macaddr}, $newnet->{macaddr}) ||
5050 &$safe_num_ne($oldnet->{queues}, $newnet->{queues}) ||
5051 !($newnet->{bridge} && $oldnet->{bridge})) { # bridge/nat mode change
5053 # for non online change, we try to hot-unplug
5054 die "skip\n" if !$hotplug;
5055 vm_deviceunplug($vmid, $conf, $opt);
5058 die "internal error" if $opt !~ m/net(\d+)/;
5059 my $iface = "tap${vmid}i$1";
5061 if (&$safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
5062 &$safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
5063 &$safe_string_ne($oldnet->{trunks}, $newnet->{trunks}) ||
5064 &$safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
5065 PVE::Network::tap_unplug($iface);
5066 PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
5067 } elsif (&$safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
5068 # Rate can be applied on its own but any change above needs to
5069 # include the rate in tap_plug since OVS resets everything.
5070 PVE::Network::tap_rate_limit($iface, $newnet->{rate});
5073 if (&$safe_string_ne($oldnet->{link_down}, $newnet->{link_down})) {
5074 qemu_set_link_status($vmid, $opt, !$newnet->{link_down});
5082 vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
5088 sub vmconfig_update_disk {
5089 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $force, $arch, $machine_type) = @_;
5091 # fixme: do we need force?
5093 my $drive = parse_drive($opt, $value);
5095 if ($conf->{$opt}) {
5097 if (my $old_drive = parse_drive($opt, $conf->{$opt})) {
5099 my $media = $drive->{media} || 'disk
';
5100 my $oldmedia = $old_drive->{media} || 'disk
';
5101 die "unable to change media type\n" if $media ne $oldmedia;
5103 if (!drive_is_cdrom($old_drive)) {
5105 if ($drive->{file} ne $old_drive->{file}) {
5107 die "skip\n" if !$hotplug;
5109 # unplug and register as unused
5110 vm_deviceunplug($vmid, $conf, $opt);
5111 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive)
5114 # update existing disk
5116 # skip non hotpluggable value
5117 if (&$safe_string_ne($drive->{discard}, $old_drive->{discard}) ||
5118 &$safe_string_ne($drive->{iothread}, $old_drive->{iothread}) ||
5119 &$safe_string_ne($drive->{queues}, $old_drive->{queues}) ||
5120 &$safe_string_ne($drive->{cache}, $old_drive->{cache})) {
5125 if (&$safe_num_ne($drive->{mbps}, $old_drive->{mbps}) ||
5126 &$safe_num_ne($drive->{mbps_rd}, $old_drive->{mbps_rd}) ||
5127 &$safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) ||
5128 &$safe_num_ne($drive->{iops}, $old_drive->{iops}) ||
5129 &$safe_num_ne($drive->{iops_rd}, $old_drive->{iops_rd}) ||
5130 &$safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr}) ||
5131 &$safe_num_ne($drive->{mbps_max}, $old_drive->{mbps_max}) ||
5132 &$safe_num_ne($drive->{mbps_rd_max}, $old_drive->{mbps_rd_max}) ||
5133 &$safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) ||
5134 &$safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) ||
5135 &$safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) ||
5136 &$safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max}) ||
5137 &$safe_num_ne($drive->{bps_max_length}, $old_drive->{bps_max_length}) ||
5138 &$safe_num_ne($drive->{bps_rd_max_length}, $old_drive->{bps_rd_max_length}) ||
5139 &$safe_num_ne($drive->{bps_wr_max_length}, $old_drive->{bps_wr_max_length}) ||
5140 &$safe_num_ne($drive->{iops_max_length}, $old_drive->{iops_max_length}) ||
5141 &$safe_num_ne($drive->{iops_rd_max_length}, $old_drive->{iops_rd_max_length}) ||
5142 &$safe_num_ne($drive->{iops_wr_max_length}, $old_drive->{iops_wr_max_length})) {
5144 qemu_block_set_io_throttle($vmid,"drive-$opt",
5145 ($drive->{mbps} || 0)*1024*1024,
5146 ($drive->{mbps_rd} || 0)*1024*1024,
5147 ($drive->{mbps_wr} || 0)*1024*1024,
5148 $drive->{iops} || 0,
5149 $drive->{iops_rd} || 0,
5150 $drive->{iops_wr} || 0,
5151 ($drive->{mbps_max} || 0)*1024*1024,
5152 ($drive->{mbps_rd_max} || 0)*1024*1024,
5153 ($drive->{mbps_wr_max} || 0)*1024*1024,
5154 $drive->{iops_max} || 0,
5155 $drive->{iops_rd_max} || 0,
5156 $drive->{iops_wr_max} || 0,
5157 $drive->{bps_max_length} || 1,
5158 $drive->{bps_rd_max_length} || 1,
5159 $drive->{bps_wr_max_length} || 1,
5160 $drive->{iops_max_length} || 1,
5161 $drive->{iops_rd_max_length} || 1,
5162 $drive->{iops_wr_max_length} || 1);
5171 if ($drive->{file} eq 'none
') {
5172 mon_cmd($vmid, "eject",force => JSON::true,device => "drive-$opt");
5173 if (drive_is_cloudinit($old_drive)) {
5174 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive);
5177 my $path = get_iso_path($storecfg, $vmid, $drive->{file});
5178 mon_cmd($vmid, "eject", force => JSON::true,device => "drive-$opt"); # force eject if locked
5179 mon_cmd($vmid, "change", device => "drive-$opt",target => "$path") if $path;
5187 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
5189 PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
5190 vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
5194 my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused,
5195 $forcemachine, $spice_ticket, $migration_network, $migration_type, $targetstorage) = @_;
5197 PVE::QemuConfig->lock_config($vmid, sub {
5198 my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom);
5200 die "you can't start a vm
if it
's a template\n" if PVE::QemuConfig->is_template($conf);
5202 my $is_suspended = PVE::QemuConfig->has_lock($conf, 'suspended
');
5204 PVE::QemuConfig->check_lock($conf)
5205 if !($skiplock || $is_suspended);
5207 die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
5209 # clean up leftover reboot request files
5210 eval { clear_reboot_request($vmid); };
5213 if (!$statefile && scalar(keys %{$conf->{pending}})) {
5214 vmconfig_apply_pending($vmid, $conf, $storecfg);
5215 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
5218 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
5220 my $defaults = load_defaults();
5222 # set environment variable useful inside network script
5223 $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
5225 my $local_volumes = {};
5227 if ($targetstorage) {
5228 foreach_drive($conf, sub {
5229 my ($ds, $drive) = @_;
5231 return if drive_is_cdrom($drive);
5233 my $volid = $drive->{file};
5237 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
5239 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5240 return if $scfg->{shared};
5241 $local_volumes->{$ds} = [$volid, $storeid, $volname];
5246 foreach my $opt (sort keys %$local_volumes) {
5248 my ($volid, $storeid, $volname) = @{$local_volumes->{$opt}};
5249 my $drive = parse_drive($opt, $conf->{$opt});
5251 #if remote storage is specified, use default format
5252 if ($targetstorage && $targetstorage ne "1") {
5253 $storeid = $targetstorage;
5254 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
5255 $format = $defFormat;
5257 #else we use same format than original
5258 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5259 $format = qemu_img_format($scfg, $volid);
5262 my $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $format, undef, ($drive->{size}/1024));
5263 my $newdrive = $drive;
5264 $newdrive->{format} = $format;
5265 $newdrive->{file} = $newvolid;
5266 my $drivestr = print_drive($newdrive);
5267 $local_volumes->{$opt} = $drivestr;
5268 #pass drive to conf for command line
5269 $conf->{$opt} = $drivestr;
5273 PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-start
', 1);
5275 if ($is_suspended) {
5276 # enforce machine type on suspended vm to ensure HW compatibility
5277 $forcemachine = $conf->{runningmachine};
5278 print "Resuming suspended VM\n";
5281 my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
5284 my $get_migration_ip = sub {
5285 my ($cidr, $nodename) = @_;
5287 return $migration_ip if defined($migration_ip);
5289 if (!defined($cidr)) {
5290 my $dc_conf = PVE::Cluster::cfs_read_file('datacenter
.cfg
');
5291 $cidr = $dc_conf->{migration}->{network};
5294 if (defined($cidr)) {
5295 my $ips = PVE::Network::get_local_ip_from_cidr($cidr);
5297 die "could not get IP: no address configured on local " .
5298 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5300 die "could not get IP: multiple addresses configured on local " .
5301 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5303 $migration_ip = @$ips[0];
5306 $migration_ip = PVE::Cluster::remote_node_ip($nodename, 1)
5307 if !defined($migration_ip);
5309 return $migration_ip;
5314 if ($statefile eq 'tcp
') {
5315 my $localip = "localhost";
5316 my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter
.cfg
');
5317 my $nodename = PVE::INotify::nodename();
5319 if (!defined($migration_type)) {
5320 if (defined($datacenterconf->{migration}->{type})) {
5321 $migration_type = $datacenterconf->{migration}->{type};
5323 $migration_type = 'secure
';
5327 if ($migration_type eq 'insecure
') {
5328 $localip = $get_migration_ip->($migration_network, $nodename);
5329 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5332 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5333 my $migrate_port = PVE::Tools::next_migrate_port($pfamily);
5334 $migrate_uri = "tcp:${localip}:${migrate_port}";
5335 push @$cmd, '-incoming
', $migrate_uri;
5338 } elsif ($statefile eq 'unix
') {
5339 # should be default for secure migrations as a ssh TCP forward
5340 # tunnel is not deterministic reliable ready and fails regurarly
5341 # to set up in time, so use UNIX socket forwards
5342 my $socket_addr = "/run/qemu-server/$vmid.migrate";
5343 unlink $socket_addr;
5345 $migrate_uri = "unix:$socket_addr";
5347 push @$cmd, '-incoming
', $migrate_uri;
5350 } elsif (-e $statefile) {
5351 push @$cmd, '-loadstate
', $statefile;
5353 my $statepath = PVE::Storage::path($storecfg, $statefile);
5354 push @$vollist, $statefile;
5355 push @$cmd, '-loadstate
', $statepath;
5362 for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
5363 my $d = parse_hostpci($conf->{"hostpci$i"});
5365 my $pcidevices = $d->{pciid};
5366 foreach my $pcidevice (@$pcidevices) {
5367 my $pciid = $pcidevice->{id};
5369 my $info = PVE::SysFSTools::pci_device_info("$pciid");
5370 die "IOMMU not present\n" if !PVE::SysFSTools::check_iommu_support();
5371 die "no pci device info for device '$pciid'\n" if !$info;
5374 my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
5375 PVE::SysFSTools::pci_create_mdev_device($pciid, $uuid, $d->{mdev});
5377 die "can't unbind
/bind pci group to vfio
'$pciid'\n"
5378 if !PVE::SysFSTools::pci_dev_group_bind_to_vfio($pciid);
5379 die "can
't reset pci device '$pciid'\n"
5380 if $info->{has_fl_reset} and !PVE::SysFSTools::pci_dev_reset($info);
5385 PVE::Storage::activate_volumes($storecfg, $vollist);
5388 run_command(['/bin/systemctl
', 'stop
', "$vmid.scope"],
5389 outfunc => sub {}, errfunc => sub {});
5391 # Issues with the above 'stop
' not being fully completed are extremely rare, a very low
5392 # timeout should be more than enough here...
5393 PVE::Systemd::wait_for_unit_removed("$vmid.scope", 5);
5395 my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
5396 : $defaults->{cpuunits};
5398 my $start_timeout = ($conf->{hugepages} || $is_suspended) ? 300 : 30;
5399 my %run_params = (timeout => $statefile ? undef : $start_timeout, umask => 0077);
5402 Slice => 'qemu
.slice
',
5404 CPUShares => $cpuunits
5407 if (my $cpulimit = $conf->{cpulimit}) {
5408 $properties{CPUQuota} = int($cpulimit * 100);
5410 $properties{timeout} = 10 if $statefile; # setting up the scope shoul be quick
5412 my $run_qemu = sub {
5413 PVE::Tools::run_fork sub {
5414 PVE::Systemd::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %properties);
5415 run_command($cmd, %run_params);
5419 if ($conf->{hugepages}) {
5422 my $hugepages_topology = PVE::QemuServer::Memory::hugepages_topology($conf);
5423 my $hugepages_host_topology = PVE::QemuServer::Memory::hugepages_host_topology();
5425 PVE::QemuServer::Memory::hugepages_mount();
5426 PVE::QemuServer::Memory::hugepages_allocate($hugepages_topology, $hugepages_host_topology);
5428 eval { $run_qemu->() };
5430 PVE::QemuServer::Memory::hugepages_reset($hugepages_host_topology);
5434 PVE::QemuServer::Memory::hugepages_pre_deallocate($hugepages_topology);
5436 eval { PVE::QemuServer::Memory::hugepages_update_locked($code); };
5439 eval { $run_qemu->() };
5443 # deactivate volumes if start fails
5444 eval { PVE::Storage::deactivate_volumes($storecfg, $vollist); };
5445 die "start failed: $err";
5448 print "migration listens on $migrate_uri\n" if $migrate_uri;
5450 if ($statefile && $statefile ne 'tcp
' && $statefile ne 'unix
') {
5451 eval { mon_cmd($vmid, "cont"); };
5455 #start nbd server for storage migration
5456 if ($targetstorage) {
5457 my $nodename = PVE::INotify::nodename();
5458 my $localip = $get_migration_ip->($migration_network, $nodename);
5459 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5460 my $storage_migrate_port = PVE::Tools::next_migrate_port($pfamily);
5462 mon_cmd($vmid, "nbd-server-start", addr => { type => 'inet
', data => { host => "${localip}", port => "${storage_migrate_port}" } } );
5464 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5466 foreach my $opt (sort keys %$local_volumes) {
5467 my $volid = $local_volumes->{$opt};
5468 mon_cmd($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
5469 my $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}:exportname=drive-$opt";
5470 print "storage migration listens on $migrate_storage_uri volume:$volid\n";
5474 if ($migratedfrom) {
5476 set_migration_caps($vmid);
5481 print "spice listens on port $spice_port\n";
5482 if ($spice_ticket) {
5483 mon_cmd($vmid, "set_password", protocol => 'spice
', password => $spice_ticket);
5484 mon_cmd($vmid, "expire_password", protocol => 'spice
', time => "+30");
5489 mon_cmd($vmid, "balloon", value => $conf->{balloon}*1024*1024)
5490 if !$statefile && $conf->{balloon};
5492 foreach my $opt (keys %$conf) {
5493 next if $opt !~ m/^net\d+$/;
5494 my $nicconf = parse_net($conf->{$opt});
5495 qemu_set_link_status($vmid, $opt, 0) if $nicconf->{link_down};
5499 mon_cmd($vmid, 'qom-set
',
5500 path => "machine/peripheral/balloon0",
5501 property => "guest-stats-polling-interval",
5502 value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
5504 if ($is_suspended) {
5505 print "Resumed VM, removing state\n";
5506 if (my $vmstate = $conf->{vmstate}) {
5507 PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
5508 PVE::Storage::vdisk_free($storecfg, $vmstate);
5510 delete $conf->@{qw(lock vmstate runningmachine)};
5511 PVE
::QemuConfig-
>write_config($vmid, $conf);
5514 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'post-start');
5518 sub vm_commandline
{
5519 my ($storecfg, $vmid, $snapname) = @_;
5521 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5525 my $snapshot = $conf->{snapshots
}->{$snapname};
5526 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
5528 # check for a 'runningmachine' in snapshot
5529 $forcemachine = $snapshot->{runningmachine
} if $snapshot->{runningmachine
};
5531 $snapshot->{digest
} = $conf->{digest
}; # keep file digest for API
5536 my $defaults = load_defaults
();
5538 my $cmd = config_to_command
($storecfg, $vmid, $conf, $defaults, $forcemachine);
5540 return PVE
::Tools
::cmd2string
($cmd);
5544 my ($vmid, $skiplock) = @_;
5546 PVE
::QemuConfig-
>lock_config($vmid, sub {
5548 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5550 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5552 mon_cmd
($vmid, "system_reset");
5556 sub get_vm_volumes
{
5560 foreach_volid
($conf, sub {
5561 my ($volid, $attr) = @_;
5563 return if $volid =~ m
|^/|;
5565 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
5568 push @$vollist, $volid;
5574 sub vm_stop_cleanup
{
5575 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
5580 my $vollist = get_vm_volumes
($conf);
5581 PVE
::Storage
::deactivate_volumes
($storecfg, $vollist);
5584 foreach my $ext (qw(mon qmp pid vnc qga)) {
5585 unlink "/var/run/qemu-server/${vmid}.$ext";
5588 if ($conf->{ivshmem
}) {
5589 my $ivshmem = PVE
::JSONSchema
::parse_property_string
($ivshmem_fmt, $conf->{ivshmem
});
5590 # just delete it for now, VMs which have this already open do not
5591 # are affected, but new VMs will get a separated one. If this
5592 # becomes an issue we either add some sort of ref-counting or just
5593 # add a "don't delete on stop" flag to the ivshmem format.
5594 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name
} // $vmid);
5597 foreach my $key (keys %$conf) {
5598 next if $key !~ m/^hostpci(\d+)$/;
5599 my $hostpciindex = $1;
5600 my $d = parse_hostpci
($conf->{$key});
5601 my $uuid = PVE
::SysFSTools
::generate_mdev_uuid
($vmid, $hostpciindex);
5603 foreach my $pci (@{$d->{pciid
}}) {
5604 my $pciid = $pci->{id
};
5605 PVE
::SysFSTools
::pci_cleanup_mdev_device
($pciid, $uuid);
5609 vmconfig_apply_pending
($vmid, $conf, $storecfg) if $apply_pending_changes;
5611 warn $@ if $@; # avoid errors - just warn
5614 # call only in locked context
5616 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
5618 my $pid = check_running
($vmid, $nocheck);
5623 $conf = PVE
::QemuConfig-
>load_config($vmid);
5624 PVE
::QemuConfig-
>check_lock($conf) if !$skiplock;
5625 if (!defined($timeout) && $shutdown && $conf->{startup
}) {
5626 my $opts = PVE
::JSONSchema
::pve_parse_startup_order
($conf->{startup
});
5627 $timeout = $opts->{down
} if $opts->{down
};
5629 PVE
::GuestHelpers
::exec_hookscript
($conf, $vmid, 'pre-stop');
5634 if (defined($conf) && parse_guest_agent
($conf)->{enabled
}) {
5635 mon_cmd
($vmid, "guest-shutdown", timeout
=> $timeout);
5637 mon_cmd
($vmid, "system_powerdown");
5640 mon_cmd
($vmid, "quit");
5646 $timeout = 60 if !defined($timeout);
5649 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
5654 if ($count >= $timeout) {
5656 warn "VM still running - terminating now with SIGTERM\n";
5659 die "VM quit/powerdown failed - got timeout\n";
5662 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5667 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
5670 die "VM quit/powerdown failed\n";
5678 while (($count < $timeout) && check_running
($vmid, $nocheck)) {
5683 if ($count >= $timeout) {
5684 warn "VM still running - terminating now with SIGKILL\n";
5689 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5692 # Note: use $nocheck to skip tests if VM configuration file exists.
5693 # We need that when migration VMs to other nodes (files already moved)
5694 # Note: we set $keepActive in vzdump stop mode - volumes need to stay active
5696 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
5698 $force = 1 if !defined($force) && !$shutdown;
5701 my $pid = check_running
($vmid, $nocheck, $migratedfrom);
5702 kill 15, $pid if $pid;
5703 my $conf = PVE
::QemuConfig-
>load_config($vmid, $migratedfrom);
5704 vm_stop_cleanup
($storecfg, $vmid, $conf, $keepActive, 0);
5708 PVE
::QemuConfig-
>lock_config($vmid, sub {
5709 _do_vm_stop
($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
5714 my ($vmid, $timeout) = @_;
5716 PVE
::QemuConfig-
>lock_config($vmid, sub {
5719 # only reboot if running, as qmeventd starts it again on a stop event
5720 return if !check_running
($vmid);
5722 create_reboot_request
($vmid);
5724 my $storecfg = PVE
::Storage
::config
();
5725 _do_vm_stop
($storecfg, $vmid, undef, undef, $timeout, 1);
5729 # avoid that the next normal shutdown will be confused for a reboot
5730 clear_reboot_request
($vmid);
5737 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
5744 PVE
::QemuConfig-
>lock_config($vmid, sub {
5746 $conf = PVE
::QemuConfig-
>load_config($vmid);
5748 my $is_backing_up = PVE
::QemuConfig-
>has_lock($conf, 'backup');
5749 PVE
::QemuConfig-
>check_lock($conf)
5750 if !($skiplock || $is_backing_up);
5752 die "cannot suspend to disk during backup\n"
5753 if $is_backing_up && $includestate;
5755 if ($includestate) {
5756 $conf->{lock} = 'suspending';
5757 my $date = strftime
("%Y-%m-%d", localtime(time()));
5758 $storecfg = PVE
::Storage
::config
();
5759 $vmstate = PVE
::QemuConfig-
>__snapshot_save_vmstate($vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
5760 $path = PVE
::Storage
::path
($storecfg, $vmstate);
5761 PVE
::QemuConfig-
>write_config($vmid, $conf);
5763 mon_cmd
($vmid, "stop");
5767 if ($includestate) {
5769 PVE
::Storage
::activate_volumes
($storecfg, [$vmstate]);
5772 mon_cmd
($vmid, "savevm-start", statefile
=> $path);
5774 my $state = mon_cmd
($vmid, "query-savevm");
5775 if (!$state->{status
}) {
5776 die "savevm not active\n";
5777 } elsif ($state->{status
} eq 'active') {
5780 } elsif ($state->{status
} eq 'completed') {
5781 print "State saved, quitting\n";
5783 } elsif ($state->{status
} eq 'failed' && $state->{error
}) {
5784 die "query-savevm failed with error '$state->{error}'\n"
5786 die "query-savevm returned status '$state->{status}'\n";
5792 PVE
::QemuConfig-
>lock_config($vmid, sub {
5793 $conf = PVE
::QemuConfig-
>load_config($vmid);
5795 # cleanup, but leave suspending lock, to indicate something went wrong
5797 mon_cmd
($vmid, "savevm-end");
5798 PVE
::Storage
::deactivate_volumes
($storecfg, [$vmstate]);
5799 PVE
::Storage
::vdisk_free
($storecfg, $vmstate);
5800 delete $conf->@{qw(vmstate runningmachine)};
5801 PVE
::QemuConfig-
>write_config($vmid, $conf);
5807 die "lock changed unexpectedly\n"
5808 if !PVE
::QemuConfig-
>has_lock($conf, 'suspending');
5810 mon_cmd
($vmid, "quit");
5811 $conf->{lock} = 'suspended';
5812 PVE
::QemuConfig-
>write_config($vmid, $conf);
5818 my ($vmid, $skiplock, $nocheck) = @_;
5820 PVE
::QemuConfig-
>lock_config($vmid, sub {
5821 my $res = mon_cmd
($vmid, 'query-status');
5822 my $resume_cmd = 'cont';
5824 if ($res->{status
} && $res->{status
} eq 'suspended') {
5825 $resume_cmd = 'system_wakeup';
5830 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5832 PVE
::QemuConfig-
>check_lock($conf)
5833 if !($skiplock || PVE
::QemuConfig-
>has_lock($conf, 'backup'));
5836 mon_cmd
($vmid, $resume_cmd);
5841 my ($vmid, $skiplock, $key) = @_;
5843 PVE
::QemuConfig-
>lock_config($vmid, sub {
5845 my $conf = PVE
::QemuConfig-
>load_config($vmid);
5847 # there is no qmp command, so we use the human monitor command
5848 my $res = PVE
::QemuServer
::Monitor
::hmp_cmd
($vmid, "sendkey $key");
5849 die $res if $res ne '';
5853 # vzdump restore implementaion
5855 sub tar_archive_read_firstfile
{
5856 my $archive = shift;
5858 die "ERROR: file '$archive' does not exist\n" if ! -f
$archive;
5860 # try to detect archive type first
5861 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
5862 die "unable to open file '$archive'\n";
5863 my $firstfile = <$fh>;
5867 die "ERROR: archive contaions no data\n" if !$firstfile;
5873 sub tar_restore_cleanup
{
5874 my ($storecfg, $statfile) = @_;
5876 print STDERR
"starting cleanup\n";
5878 if (my $fd = IO
::File-
>new($statfile, "r")) {
5879 while (defined(my $line = <$fd>)) {
5880 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
5883 if ($volid =~ m
|^/|) {
5884 unlink $volid || die 'unlink failed\n';
5886 PVE
::Storage
::vdisk_free
($storecfg, $volid);
5888 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
5890 print STDERR
"unable to cleanup '$volid' - $@" if $@;
5892 print STDERR
"unable to parse line in statfile - $line";
5899 sub restore_archive
{
5900 my ($archive, $vmid, $user, $opts) = @_;
5902 my $format = $opts->{format
};
5905 if ($archive =~ m/\.tgz$/ || $archive =~ m/\.tar\.gz$/) {
5906 $format = 'tar' if !$format;
5908 } elsif ($archive =~ m/\.tar$/) {
5909 $format = 'tar' if !$format;
5910 } elsif ($archive =~ m/.tar.lzo$/) {
5911 $format = 'tar' if !$format;
5913 } elsif ($archive =~ m/\.vma$/) {
5914 $format = 'vma' if !$format;
5915 } elsif ($archive =~ m/\.vma\.gz$/) {
5916 $format = 'vma' if !$format;
5918 } elsif ($archive =~ m/\.vma\.lzo$/) {
5919 $format = 'vma' if !$format;
5922 $format = 'vma' if !$format; # default
5925 # try to detect archive format
5926 if ($format eq 'tar') {
5927 return restore_tar_archive
($archive, $vmid, $user, $opts);
5929 return restore_vma_archive
($archive, $vmid, $user, $opts, $comp);
5933 sub restore_update_config_line
{
5934 my ($outfd, $cookie, $vmid, $map, $line, $unique) = @_;
5936 return if $line =~ m/^\#qmdump\#/;
5937 return if $line =~ m/^\#vzdump\#/;
5938 return if $line =~ m/^lock:/;
5939 return if $line =~ m/^unused\d+:/;
5940 return if $line =~ m/^parent:/;
5942 my $dc = PVE
::Cluster
::cfs_read_file
('datacenter.cfg');
5943 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
5944 # try to convert old 1.X settings
5945 my ($id, $ind, $ethcfg) = ($1, $2, $3);
5946 foreach my $devconfig (PVE
::Tools
::split_list
($ethcfg)) {
5947 my ($model, $macaddr) = split(/\=/, $devconfig);
5948 $macaddr = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if !$macaddr || $unique;
5951 bridge
=> "vmbr$ind",
5952 macaddr
=> $macaddr,
5954 my $netstr = print_net
($net);
5956 print $outfd "net$cookie->{netcount}: $netstr\n";
5957 $cookie->{netcount
}++;
5959 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
5960 my ($id, $netstr) = ($1, $2);
5961 my $net = parse_net
($netstr);
5962 $net->{macaddr
} = PVE
::Tools
::random_ether_addr
($dc->{mac_prefix
}) if $net->{macaddr
};
5963 $netstr = print_net
($net);
5964 print $outfd "$id: $netstr\n";
5965 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk)\d+):\s*(\S+)\s*$/) {
5968 my $di = parse_drive
($virtdev, $value);
5969 if (defined($di->{backup
}) && !$di->{backup
}) {
5970 print $outfd "#$line";
5971 } elsif ($map->{$virtdev}) {
5972 delete $di->{format
}; # format can change on restore
5973 $di->{file
} = $map->{$virtdev};
5974 $value = print_drive
($di);
5975 print $outfd "$virtdev: $value\n";
5979 } elsif (($line =~ m/^vmgenid: (.*)/)) {
5981 if ($vmgenid ne '0') {
5982 # always generate a new vmgenid if there was a valid one setup
5983 $vmgenid = generate_uuid
();
5985 print $outfd "vmgenid: $vmgenid\n";
5986 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
5987 my ($uuid, $uuid_str);
5988 UUID
::generate
($uuid);
5989 UUID
::unparse
($uuid, $uuid_str);
5990 my $smbios1 = parse_smbios1
($2);
5991 $smbios1->{uuid
} = $uuid_str;
5992 print $outfd $1.print_smbios1
($smbios1)."\n";
5999 my ($cfg, $vmid) = @_;
6001 my $info = PVE
::Storage
::vdisk_list
($cfg, undef, $vmid);
6003 my $volid_hash = {};
6004 foreach my $storeid (keys %$info) {
6005 foreach my $item (@{$info->{$storeid}}) {
6006 next if !($item->{volid
} && $item->{size
});
6007 $item->{path
} = PVE
::Storage
::path
($cfg, $item->{volid
});
6008 $volid_hash->{$item->{volid
}} = $item;
6015 sub is_volume_in_use
{
6016 my ($storecfg, $conf, $skip_drive, $volid) = @_;
6018 my $path = PVE
::Storage
::path
($storecfg, $volid);
6020 my $scan_config = sub {
6021 my ($cref, $snapname) = @_;
6023 foreach my $key (keys %$cref) {
6024 my $value = $cref->{$key};
6025 if (is_valid_drivename
($key)) {
6026 next if $skip_drive && $key eq $skip_drive;
6027 my $drive = parse_drive
($key, $value);
6028 next if !$drive || !$drive->{file
} || drive_is_cdrom
($drive);
6029 return 1 if $volid eq $drive->{file
};
6030 if ($drive->{file
} =~ m!^/!) {
6031 return 1 if $drive->{file
} eq $path;
6033 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
}, 1);
6035 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid, 1);
6037 return 1 if $path eq PVE
::Storage
::path
($storecfg, $drive->{file
}, $snapname);
6045 return 1 if &$scan_config($conf);
6049 foreach my $snapname (keys %{$conf->{snapshots
}}) {
6050 return 1 if &$scan_config($conf->{snapshots
}->{$snapname}, $snapname);
6056 sub update_disksize
{
6057 my ($vmid, $conf, $volid_hash) = @_;
6060 my $prefix = "VM $vmid:";
6062 # used and unused disks
6063 my $referenced = {};
6065 # Note: it is allowed to define multiple storages with same path (alias), so
6066 # we need to check both 'volid' and real 'path' (two different volid can point
6067 # to the same path).
6069 my $referencedpath = {};
6072 foreach my $opt (keys %$conf) {
6073 if (is_valid_drivename
($opt)) {
6074 my $drive = parse_drive
($opt, $conf->{$opt});
6075 my $volid = $drive->{file
};
6078 $referenced->{$volid} = 1;
6079 if ($volid_hash->{$volid} &&
6080 (my $path = $volid_hash->{$volid}->{path
})) {
6081 $referencedpath->{$path} = 1;
6084 next if drive_is_cdrom
($drive);
6085 next if !$volid_hash->{$volid};
6087 $drive->{size
} = $volid_hash->{$volid}->{size
};
6088 my $new = print_drive
($drive);
6089 if ($new ne $conf->{$opt}) {
6091 $conf->{$opt} = $new;
6092 print "$prefix update disk '$opt' information.\n";
6097 # remove 'unusedX' entry if volume is used
6098 foreach my $opt (keys %$conf) {
6099 next if $opt !~ m/^unused\d+$/;
6100 my $volid = $conf->{$opt};
6101 my $path = $volid_hash->{$volid}->{path
} if $volid_hash->{$volid};
6102 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6103 print "$prefix remove entry '$opt', its volume '$volid' is in use.\n";
6105 delete $conf->{$opt};
6108 $referenced->{$volid} = 1;
6109 $referencedpath->{$path} = 1 if $path;
6112 foreach my $volid (sort keys %$volid_hash) {
6113 next if $volid =~ m/vm-$vmid-state-/;
6114 next if $referenced->{$volid};
6115 my $path = $volid_hash->{$volid}->{path
};
6116 next if !$path; # just to be sure
6117 next if $referencedpath->{$path};
6119 my $key = PVE
::QemuConfig-
>add_unused_volume($conf, $volid);
6120 print "$prefix add unreferenced volume '$volid' as '$key' to config.\n";
6121 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6128 my ($vmid, $nolock, $dryrun) = @_;
6130 my $cfg = PVE
::Storage
::config
();
6132 # FIXME: Remove once our RBD plugin can handle CT and VM on a single storage
6133 # see: https://pve.proxmox.com/pipermail/pve-devel/2018-July/032900.html
6134 foreach my $stor (keys %{$cfg->{ids
}}) {
6135 delete($cfg->{ids
}->{$stor}) if ! $cfg->{ids
}->{$stor}->{content
}->{images
};
6138 print "rescan volumes...\n";
6139 my $volid_hash = scan_volids
($cfg, $vmid);
6141 my $updatefn = sub {
6144 my $conf = PVE
::QemuConfig-
>load_config($vmid);
6146 PVE
::QemuConfig-
>check_lock($conf);
6149 foreach my $volid (keys %$volid_hash) {
6150 my $info = $volid_hash->{$volid};
6151 $vm_volids->{$volid} = $info if $info->{vmid
} && $info->{vmid
} == $vmid;
6154 my $changes = update_disksize
($vmid, $conf, $vm_volids);
6156 PVE
::QemuConfig-
>write_config($vmid, $conf) if $changes && !$dryrun;
6159 if (defined($vmid)) {
6163 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6166 my $vmlist = config_list
();
6167 foreach my $vmid (keys %$vmlist) {
6171 PVE
::QemuConfig-
>lock_config($vmid, $updatefn, $vmid);
6177 sub restore_vma_archive
{
6178 my ($archive, $vmid, $user, $opts, $comp) = @_;
6180 my $readfrom = $archive;
6182 my $cfg = PVE
::Storage
::config
();
6184 my $bwlimit = $opts->{bwlimit
};
6186 my $dbg_cmdstring = '';
6187 my $add_pipe = sub {
6189 push @$commands, $cmd;
6190 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
6191 $dbg_cmdstring .= PVE
::Tools
::cmd2string
($cmd);
6196 if ($archive eq '-') {
6199 # If we use a backup from a PVE defined storage we also consider that
6200 # storage's rate limit:
6201 my (undef, $volid) = PVE
::Storage
::path_to_volume_id
($cfg, $archive);
6202 if (defined($volid)) {
6203 my ($sid, undef) = PVE
::Storage
::parse_volume_id
($volid);
6204 my $readlimit = PVE
::Storage
::get_bandwidth_limit
('restore', [$sid], $bwlimit);
6206 print STDERR
"applying read rate limit: $readlimit\n";
6207 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
6208 $add_pipe->($cstream);
6215 if ($comp eq 'gzip') {
6216 $cmd = ['zcat', $readfrom];
6217 } elsif ($comp eq 'lzop') {
6218 $cmd = ['lzop', '-d', '-c', $readfrom];
6220 die "unknown compression method '$comp'\n";
6225 my $tmpdir = "/var/tmp/vzdumptmp$$";
6228 # disable interrupts (always do cleanups)
6232 local $SIG{HUP
} = sub { warn "got interrupt - ignored\n"; };
6234 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
6235 POSIX
::mkfifo
($mapfifo, 0600);
6238 my $openfifo = sub {
6239 open($fifofh, '>', $mapfifo) || die $!;
6242 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
6249 my $rpcenv = PVE
::RPCEnvironment
::get
();
6251 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6252 my $tmpfn = "$conffile.$$.tmp";
6254 # Note: $oldconf is undef if VM does not exists
6255 my $cfs_path = PVE
::QemuConfig-
>cfs_config_path($vmid);
6256 my $oldconf = PVE
::Cluster
::cfs_read_file
($cfs_path);
6260 my $print_devmap = sub {
6261 my $virtdev_hash = {};
6263 my $cfgfn = "$tmpdir/qemu-server.conf";
6265 # we can read the config - that is already extracted
6266 my $fh = IO
::File-
>new($cfgfn, "r") ||
6267 "unable to read qemu-server.conf - $!\n";
6269 my $fwcfgfn = "$tmpdir/qemu-server.fw";
6271 my $pve_firewall_dir = '/etc/pve/firewall';
6272 mkdir $pve_firewall_dir; # make sure the dir exists
6273 PVE
::Tools
::file_copy
($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
6276 while (defined(my $line = <$fh>)) {
6277 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
6278 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
6279 die "archive does not contain data for drive '$virtdev'\n"
6280 if !$devinfo->{$devname};
6281 if (defined($opts->{storage
})) {
6282 $storeid = $opts->{storage
} || 'local';
6283 } elsif (!$storeid) {
6286 $format = 'raw' if !$format;
6287 $devinfo->{$devname}->{devname
} = $devname;
6288 $devinfo->{$devname}->{virtdev
} = $virtdev;
6289 $devinfo->{$devname}->{format
} = $format;
6290 $devinfo->{$devname}->{storeid
} = $storeid;
6292 # check permission on storage
6293 my $pool = $opts->{pool
}; # todo: do we need that?
6294 if ($user ne 'root@pam') {
6295 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
6298 $storage_limits{$storeid} = $bwlimit;
6300 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
6301 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
6303 my $drive = parse_drive
($virtdev, $2);
6304 if (drive_is_cloudinit
($drive)) {
6305 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
6306 my $scfg = PVE
::Storage
::storage_config
($cfg, $storeid);
6307 my $format = qemu_img_format
($scfg, $volname); # has 'raw' fallback
6311 storeid
=> $opts->{storage
} // $storeid,
6312 size
=> PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
,
6313 file
=> $drive->{file
}, # to make drive_is_cloudinit check possible
6314 name
=> "vm-$vmid-cloudinit",
6317 $virtdev_hash->{$virtdev} = $d;
6322 foreach my $key (keys %storage_limits) {
6323 my $limit = PVE
::Storage
::get_bandwidth_limit
('restore', [$key], $bwlimit);
6325 print STDERR
"rate limit for storage $key: $limit KiB/s\n";
6326 $storage_limits{$key} = $limit * 1024;
6329 foreach my $devname (keys %$devinfo) {
6330 die "found no device mapping information for device '$devname'\n"
6331 if !$devinfo->{$devname}->{virtdev
};
6334 # create empty/temp config
6336 PVE
::Tools
::file_set_contents
($conffile, "memory: 128\n");
6337 foreach_drive
($oldconf, sub {
6338 my ($ds, $drive) = @_;
6340 return if drive_is_cdrom
($drive, 1);
6342 my $volid = $drive->{file
};
6343 return if !$volid || $volid =~ m
|^/|;
6345 my ($path, $owner) = PVE
::Storage
::path
($cfg, $volid);
6346 return if !$path || !$owner || ($owner != $vmid);
6348 # Note: only delete disk we want to restore
6349 # other volumes will become unused
6350 if ($virtdev_hash->{$ds}) {
6351 eval { PVE
::Storage
::vdisk_free
($cfg, $volid); };
6358 # delete vmstate files, after the restore we have no snapshots anymore
6359 foreach my $snapname (keys %{$oldconf->{snapshots
}}) {
6360 my $snap = $oldconf->{snapshots
}->{$snapname};
6361 if ($snap->{vmstate
}) {
6362 eval { PVE
::Storage
::vdisk_free
($cfg, $snap->{vmstate
}); };
6371 foreach my $virtdev (sort keys %$virtdev_hash) {
6372 my $d = $virtdev_hash->{$virtdev};
6373 my $alloc_size = int(($d->{size
} + 1024 - 1)/1024);
6374 my $storeid = $d->{storeid
};
6375 my $scfg = PVE
::Storage
::storage_config
($cfg, $storeid);
6378 if (my $limit = $storage_limits{$storeid}) {
6379 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
6382 # test if requested format is supported
6383 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($cfg, $storeid);
6384 my $supported = grep { $_ eq $d->{format
} } @$validFormats;
6385 $d->{format
} = $defFormat if !$supported;
6388 if ($d->{is_cloudinit
}) {
6390 $name .= ".$d->{format}" if $d->{format
} ne 'raw';
6393 my $volid = PVE
::Storage
::vdisk_alloc
($cfg, $storeid, $vmid, $d->{format
}, $name, $alloc_size);
6394 print STDERR
"new volume ID is '$volid'\n";
6395 $d->{volid
} = $volid;
6397 PVE
::Storage
::activate_volumes
($cfg, [$volid]);
6399 my $write_zeros = 1;
6400 if (PVE
::Storage
::volume_has_feature
($cfg, 'sparseinit', $volid)) {
6404 if (!$d->{is_cloudinit
}) {
6405 my $path = PVE
::Storage
::path
($cfg, $volid);
6407 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
6409 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
6411 $map->{$virtdev} = $volid;
6414 $fh->seek(0, 0) || die "seek failed - $!\n";
6416 my $outfd = new IO
::File
($tmpfn, "w") ||
6417 die "unable to write config for VM $vmid\n";
6419 my $cookie = { netcount
=> 0 };
6420 while (defined(my $line = <$fh>)) {
6421 restore_update_config_line
($outfd, $cookie, $vmid, $map, $line, $opts->{unique
});
6434 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6435 local $SIG{ALRM
} = sub { die "got timeout\n"; };
6437 $oldtimeout = alarm($timeout);
6444 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
6445 my ($dev_id, $size, $devname) = ($1, $2, $3);
6446 $devinfo->{$devname} = { size
=> $size, dev_id
=> $dev_id };
6447 } elsif ($line =~ m/^CTIME: /) {
6448 # we correctly received the vma config, so we can disable
6449 # the timeout now for disk allocation (set to 10 minutes, so
6450 # that we always timeout if something goes wrong)
6453 print $fifofh "done\n";
6454 my $tmp = $oldtimeout || 0;
6455 $oldtimeout = undef;
6461 print "restore vma archive: $dbg_cmdstring\n";
6462 run_command
($commands, input
=> $input, outfunc
=> $parser, afterfork
=> $openfifo);
6466 alarm($oldtimeout) if $oldtimeout;
6469 foreach my $devname (keys %$devinfo) {
6470 my $volid = $devinfo->{$devname}->{volid
};
6471 push @$vollist, $volid if $volid;
6474 PVE
::Storage
::deactivate_volumes
($cfg, $vollist);
6482 foreach my $devname (keys %$devinfo) {
6483 my $volid = $devinfo->{$devname}->{volid
};
6486 if ($volid =~ m
|^/|) {
6487 unlink $volid || die 'unlink failed\n';
6489 PVE
::Storage
::vdisk_free
($cfg, $volid);
6491 print STDERR
"temporary volume '$volid' sucessfuly removed\n";
6493 print STDERR
"unable to cleanup '$volid' - $@" if $@;
6500 rename($tmpfn, $conffile) ||
6501 die "unable to commit configuration file '$conffile'\n";
6503 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6505 eval { rescan
($vmid, 1); };
6509 sub restore_tar_archive
{
6510 my ($archive, $vmid, $user, $opts) = @_;
6512 if ($archive ne '-') {
6513 my $firstfile = tar_archive_read_firstfile
($archive);
6514 die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n"
6515 if $firstfile ne 'qemu-server.conf';
6518 my $storecfg = PVE
::Storage
::config
();
6520 # avoid zombie disks when restoring over an existing VM -> cleanup first
6521 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
6522 # skiplock=1 because qmrestore has set the 'create' lock itself already
6523 my $vmcfgfn = PVE
::QemuConfig-
>config_file($vmid);
6524 destroy_vm
($storecfg, $vmid, 1, { lock => 'restore' }) if -f
$vmcfgfn;
6526 my $tocmd = "/usr/lib/qemu-server/qmextract";
6528 $tocmd .= " --storage " . PVE
::Tools
::shellquote
($opts->{storage
}) if $opts->{storage
};
6529 $tocmd .= " --pool " . PVE
::Tools
::shellquote
($opts->{pool
}) if $opts->{pool
};
6530 $tocmd .= ' --prealloc' if $opts->{prealloc
};
6531 $tocmd .= ' --info' if $opts->{info
};
6533 # tar option "xf" does not autodetect compression when read from STDIN,
6534 # so we pipe to zcat
6535 my $cmd = "zcat -f|tar xf " . PVE
::Tools
::shellquote
($archive) . " " .
6536 PVE
::Tools
::shellquote
("--to-command=$tocmd");
6538 my $tmpdir = "/var/tmp/vzdumptmp$$";
6541 local $ENV{VZDUMP_TMPDIR
} = $tmpdir;
6542 local $ENV{VZDUMP_VMID
} = $vmid;
6543 local $ENV{VZDUMP_USER
} = $user;
6545 my $conffile = PVE
::QemuConfig-
>config_file($vmid);
6546 my $tmpfn = "$conffile.$$.tmp";
6548 # disable interrupts (always do cleanups)
6552 local $SIG{HUP
} = sub { print STDERR
"got interrupt - ignored\n"; };
6560 local $SIG{PIPE
} = sub { die "interrupted by signal\n"; };
6562 if ($archive eq '-') {
6563 print "extracting archive from STDIN\n";
6564 run_command
($cmd, input
=> "<&STDIN");
6566 print "extracting archive '$archive'\n";
6570 return if $opts->{info
};
6574 my $statfile = "$tmpdir/qmrestore.stat";
6575 if (my $fd = IO
::File-
>new($statfile, "r")) {
6576 while (defined (my $line = <$fd>)) {
6577 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6578 $map->{$1} = $2 if $1;
6580 print STDERR
"unable to parse line in statfile - $line\n";
6586 my $confsrc = "$tmpdir/qemu-server.conf";
6588 my $srcfd = new IO
::File
($confsrc, "r") ||
6589 die "unable to open file '$confsrc'\n";
6591 my $outfd = new IO
::File
($tmpfn, "w") ||
6592 die "unable to write config for VM $vmid\n";
6594 my $cookie = { netcount
=> 0 };
6595 while (defined (my $line = <$srcfd>)) {
6596 restore_update_config_line
($outfd, $cookie, $vmid, $map, $line, $opts->{unique
});
6604 tar_restore_cleanup
($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info
};
6610 rename $tmpfn, $conffile ||
6611 die "unable to commit configuration file '$conffile'\n";
6613 PVE
::Cluster
::cfs_update
(); # make sure we read new file
6615 eval { rescan
($vmid, 1); };
6619 sub foreach_storage_used_by_vm
{
6620 my ($conf, $func) = @_;
6624 foreach_drive
($conf, sub {
6625 my ($ds, $drive) = @_;
6626 return if drive_is_cdrom
($drive);
6628 my $volid = $drive->{file
};
6630 my ($sid, $volname) = PVE
::Storage
::parse_volume_id
($volid, 1);
6631 $sidhash->{$sid} = $sid if $sid;
6634 foreach my $sid (sort keys %$sidhash) {
6639 my $qemu_snap_storage = {
6642 sub do_snapshots_with_qemu
{
6643 my ($storecfg, $volid) = @_;
6645 my $storage_name = PVE
::Storage
::parse_volume_id
($volid);
6646 my $scfg = $storecfg->{ids
}->{$storage_name};
6648 if ($qemu_snap_storage->{$scfg->{type
}} && !$scfg->{krbd
}){
6652 if ($volid =~ m/\.(qcow2|qed)$/){
6659 sub qga_check_running
{
6660 my ($vmid, $nowarn) = @_;
6662 eval { mon_cmd
($vmid, "guest-ping", timeout
=> 3); };
6664 warn "Qemu Guest Agent is not running - $@" if !$nowarn;
6670 sub template_create
{
6671 my ($vmid, $conf, $disk) = @_;
6673 my $storecfg = PVE
::Storage
::config
();
6675 foreach_drive
($conf, sub {
6676 my ($ds, $drive) = @_;
6678 return if drive_is_cdrom
($drive);
6679 return if $disk && $ds ne $disk;
6681 my $volid = $drive->{file
};
6682 return if !PVE
::Storage
::volume_has_feature
($storecfg, 'template', $volid);
6684 my $voliddst = PVE
::Storage
::vdisk_create_base
($storecfg, $volid);
6685 $drive->{file
} = $voliddst;
6686 $conf->{$ds} = print_drive
($drive);
6687 PVE
::QemuConfig-
>write_config($vmid, $conf);
6691 sub convert_iscsi_path
{
6694 if ($path =~ m
|^iscsi
://([^/]+)/([^/]+)/(.+)$|) {
6699 my $initiator_name = get_initiator_name
();
6701 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
6702 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
6705 die "cannot convert iscsi path '$path', unkown format\n";
6708 sub qemu_img_convert
{
6709 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
6711 my $storecfg = PVE
::Storage
::config
();
6712 my ($src_storeid, $src_volname) = PVE
::Storage
::parse_volume_id
($src_volid, 1);
6713 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid, 1);
6715 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
6719 my $src_is_iscsi = 0;
6723 PVE
::Storage
::activate_volumes
($storecfg, [$src_volid], $snapname);
6724 my $src_scfg = PVE
::Storage
::storage_config
($storecfg, $src_storeid);
6725 $src_format = qemu_img_format
($src_scfg, $src_volname);
6726 $src_path = PVE
::Storage
::path
($storecfg, $src_volid, $snapname);
6727 $src_is_iscsi = ($src_path =~ m
|^iscsi
://|);
6728 $cachemode = 'none' if $src_scfg->{type
} eq 'zfspool';
6729 } elsif (-f
$src_volid) {
6730 $src_path = $src_volid;
6731 if ($src_path =~ m/\.($QEMU_FORMAT_RE)$/) {
6736 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
6738 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
6739 my $dst_format = qemu_img_format
($dst_scfg, $dst_volname);
6740 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
6741 my $dst_is_iscsi = ($dst_path =~ m
|^iscsi
://|);
6744 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
6745 push @$cmd, '-l', "snapshot.name=$snapname"
6746 if $snapname && $src_format && $src_format eq "qcow2";
6747 push @$cmd, '-t', 'none' if $dst_scfg->{type
} eq 'zfspool';
6748 push @$cmd, '-T', $cachemode if defined($cachemode);
6750 if ($src_is_iscsi) {
6751 push @$cmd, '--image-opts';
6752 $src_path = convert_iscsi_path
($src_path);
6753 } elsif ($src_format) {
6754 push @$cmd, '-f', $src_format;
6757 if ($dst_is_iscsi) {
6758 push @$cmd, '--target-image-opts';
6759 $dst_path = convert_iscsi_path
($dst_path);
6761 push @$cmd, '-O', $dst_format;
6764 push @$cmd, $src_path;
6766 if (!$dst_is_iscsi && $is_zero_initialized) {
6767 push @$cmd, "zeroinit:$dst_path";
6769 push @$cmd, $dst_path;
6774 if($line =~ m/\((\S+)\/100\
%\)/){
6776 my $transferred = int($size * $percent / 100);
6777 my $remaining = $size - $transferred;
6779 print "transferred: $transferred bytes remaining: $remaining bytes total: $size bytes progression: $percent %\n";
6784 eval { run_command
($cmd, timeout
=> undef, outfunc
=> $parser); };
6786 die "copy failed: $err" if $err;
6789 sub qemu_img_format
{
6790 my ($scfg, $volname) = @_;
6792 if ($scfg->{path
} && $volname =~ m/\.($QEMU_FORMAT_RE)$/) {
6799 sub qemu_drive_mirror
{
6800 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $skipcomplete, $qga, $bwlimit) = @_;
6802 $jobs = {} if !$jobs;
6806 $jobs->{"drive-$drive"} = {};
6808 if ($dst_volid =~ /^nbd:/) {
6809 $qemu_target = $dst_volid;
6812 my $storecfg = PVE
::Storage
::config
();
6813 my ($dst_storeid, $dst_volname) = PVE
::Storage
::parse_volume_id
($dst_volid);
6815 my $dst_scfg = PVE
::Storage
::storage_config
($storecfg, $dst_storeid);
6817 $format = qemu_img_format
($dst_scfg, $dst_volname);
6819 my $dst_path = PVE
::Storage
::path
($storecfg, $dst_volid);
6821 $qemu_target = $is_zero_initialized ?
"zeroinit:$dst_path" : $dst_path;
6824 my $opts = { timeout
=> 10, device
=> "drive-$drive", mode
=> "existing", sync
=> "full", target
=> $qemu_target };
6825 $opts->{format
} = $format if $format;
6827 if (defined($bwlimit)) {
6828 $opts->{speed
} = $bwlimit * 1024;
6829 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
6831 print "drive mirror is starting for drive-$drive\n";
6834 # if a job already runs for this device we get an error, catch it for cleanup
6835 eval { mon_cmd
($vmid, "drive-mirror", %$opts); };
6837 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
6839 die "mirroring error: $err\n";
6842 qemu_drive_mirror_monitor
($vmid, $vmiddst, $jobs, $skipcomplete, $qga);
6845 sub qemu_drive_mirror_monitor
{
6846 my ($vmid, $vmiddst, $jobs, $skipcomplete, $qga) = @_;
6849 my $err_complete = 0;
6852 die "storage migration timed out\n" if $err_complete > 300;
6854 my $stats = mon_cmd
($vmid, "query-block-jobs");
6856 my $running_mirror_jobs = {};
6857 foreach my $stat (@$stats) {
6858 next if $stat->{type
} ne 'mirror';
6859 $running_mirror_jobs->{$stat->{device
}} = $stat;
6862 my $readycounter = 0;
6864 foreach my $job (keys %$jobs) {
6866 if(defined($jobs->{$job}->{complete
}) && !defined($running_mirror_jobs->{$job})) {
6867 print "$job : finished\n";
6868 delete $jobs->{$job};
6872 die "$job: mirroring has been cancelled\n" if !defined($running_mirror_jobs->{$job});
6874 my $busy = $running_mirror_jobs->{$job}->{busy
};
6875 my $ready = $running_mirror_jobs->{$job}->{ready
};
6876 if (my $total = $running_mirror_jobs->{$job}->{len
}) {
6877 my $transferred = $running_mirror_jobs->{$job}->{offset
} || 0;
6878 my $remaining = $total - $transferred;
6879 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
6881 print "$job: transferred: $transferred bytes remaining: $remaining bytes total: $total bytes progression: $percent % busy: $busy ready: $ready \n";
6884 $readycounter++ if $running_mirror_jobs->{$job}->{ready
};
6887 last if scalar(keys %$jobs) == 0;
6889 if ($readycounter == scalar(keys %$jobs)) {
6890 print "all mirroring jobs are ready \n";
6891 last if $skipcomplete; #do the complete later
6893 if ($vmiddst && $vmiddst != $vmid) {
6894 my $agent_running = $qga && qga_check_running
($vmid);
6895 if ($agent_running) {
6896 print "freeze filesystem\n";
6897 eval { mon_cmd
($vmid, "guest-fsfreeze-freeze"); };
6899 print "suspend vm\n";
6900 eval { PVE
::QemuServer
::vm_suspend
($vmid, 1); };
6903 # if we clone a disk for a new target vm, we don't switch the disk
6904 PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs);
6906 if ($agent_running) {
6907 print "unfreeze filesystem\n";
6908 eval { mon_cmd
($vmid, "guest-fsfreeze-thaw"); };
6910 print "resume vm\n";
6911 eval { PVE
::QemuServer
::vm_resume
($vmid, 1, 1); };
6917 foreach my $job (keys %$jobs) {
6918 # try to switch the disk if source and destination are on the same guest
6919 print "$job: Completing block job...\n";
6921 eval { mon_cmd
($vmid, "block-job-complete", device
=> $job) };
6922 if ($@ =~ m/cannot be completed/) {
6923 print "$job: Block job cannot be completed, try again.\n";
6926 print "$job: Completed successfully.\n";
6927 $jobs->{$job}->{complete
} = 1;
6938 eval { PVE
::QemuServer
::qemu_blockjobs_cancel
($vmid, $jobs) };
6939 die "mirroring error: $err";
6944 sub qemu_blockjobs_cancel
{
6945 my ($vmid, $jobs) = @_;
6947 foreach my $job (keys %$jobs) {
6948 print "$job: Cancelling block job\n";
6949 eval { mon_cmd
($vmid, "block-job-cancel", device
=> $job); };
6950 $jobs->{$job}->{cancel
} = 1;
6954 my $stats = mon_cmd
($vmid, "query-block-jobs");
6956 my $running_jobs = {};
6957 foreach my $stat (@$stats) {
6958 $running_jobs->{$stat->{device
}} = $stat;
6961 foreach my $job (keys %$jobs) {
6963 if (defined($jobs->{$job}->{cancel
}) && !defined($running_jobs->{$job})) {
6964 print "$job: Done.\n";
6965 delete $jobs->{$job};
6969 last if scalar(keys %$jobs) == 0;
6976 my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
6977 $newvmid, $storage, $format, $full, $newvollist, $jobs, $skipcomplete, $qga, $bwlimit) = @_;
6982 print "create linked clone of drive $drivename ($drive->{file})\n";
6983 $newvolid = PVE
::Storage
::vdisk_clone
($storecfg, $drive->{file
}, $newvmid, $snapname);
6984 push @$newvollist, $newvolid;
6987 my ($storeid, $volname) = PVE
::Storage
::parse_volume_id
($drive->{file
});
6988 $storeid = $storage if $storage;
6990 my $dst_format = resolve_dst_disk_format
($storecfg, $storeid, $volname, $format);
6991 my ($size) = PVE
::Storage
::volume_size_info
($storecfg, $drive->{file
}, 3);
6993 print "create full clone of drive $drivename ($drive->{file})\n";
6995 if (drive_is_cloudinit
($drive)) {
6996 $name = "vm-$newvmid-cloudinit";
6997 $name .= ".$dst_format" if $dst_format ne 'raw';
6999 $size = PVE
::QemuServer
::Cloudinit
::CLOUDINIT_DISK_SIZE
;
7001 $newvolid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024));
7002 push @$newvollist, $newvolid;
7004 PVE
::Storage
::activate_volumes
($storecfg, [$newvolid]);
7006 if (drive_is_cloudinit
($drive)) {
7010 my $sparseinit = PVE
::Storage
::volume_has_feature
($storecfg, 'sparseinit', $newvolid);
7011 if (!$running || $snapname) {
7012 # TODO: handle bwlimits
7013 qemu_img_convert
($drive->{file
}, $newvolid, $size, $snapname, $sparseinit);
7016 my $kvmver = get_running_qemu_version
($vmid);
7017 if (!min_version
($kvmver, 2, 7)) {
7018 die "drive-mirror with iothread requires qemu version 2.7 or higher\n"
7019 if $drive->{iothread
};
7022 qemu_drive_mirror
($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs, $skipcomplete, $qga, $bwlimit);
7027 my ($size) = PVE
::Storage
::volume_size_info
($storecfg, $newvolid, 3);
7030 $disk->{format
} = undef;
7031 $disk->{file
} = $newvolid;
7032 $disk->{size
} = $size;
7037 sub get_running_qemu_version
{
7039 my $res = mon_cmd
($vmid, "query-version");
7040 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
7043 sub qemu_use_old_bios_files
{
7044 my ($machine_type) = @_;
7046 return if !$machine_type;
7048 my $use_old_bios_files = undef;
7050 if ($machine_type =~ m/^(\S+)\.pxe$/) {
7052 $use_old_bios_files = 1;
7054 my $version = PVE
::QemuServer
::Machine
::extract_version
($machine_type, kvm_user_version
());
7055 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
7056 # load new efi bios files on migration. So this hack is required to allow
7057 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
7058 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
7059 $use_old_bios_files = !min_version
($version, 2, 4);
7062 return ($use_old_bios_files, $machine_type);
7065 sub create_efidisk
($$$$$) {
7066 my ($storecfg, $storeid, $vmid, $fmt, $arch) = @_;
7068 my (undef, $ovmf_vars) = get_ovmf_files
($arch);
7069 die "EFI vars default image not found\n" if ! -f
$ovmf_vars;
7071 my $vars_size_b = -s
$ovmf_vars;
7072 my $vars_size = PVE
::Tools
::convert_size
($vars_size_b, 'b' => 'kb');
7073 my $volid = PVE
::Storage
::vdisk_alloc
($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
7074 PVE
::Storage
::activate_volumes
($storecfg, [$volid]);
7076 qemu_img_convert
($ovmf_vars, $volid, $vars_size_b, undef, 0);
7077 my ($size) = PVE
::Storage
::volume_size_info
($storecfg, $volid, 3);
7079 return ($volid, $size/1024);
7082 sub vm_iothreads_list
{
7085 my $res = mon_cmd
($vmid, 'query-iothreads');
7088 foreach my $iothread (@$res) {
7089 $iothreads->{ $iothread->{id
} } = $iothread->{"thread-id"};
7096 my ($conf, $drive) = @_;
7100 if (!$conf->{scsihw
} || ($conf->{scsihw
} =~ m/^lsi/)) {
7102 } elsif ($conf->{scsihw
} && ($conf->{scsihw
} eq 'virtio-scsi-single')) {
7108 my $controller = int($drive->{index} / $maxdev);
7109 my $controller_prefix = ($conf->{scsihw
} && $conf->{scsihw
} eq 'virtio-scsi-single') ?
"virtioscsi" : "scsihw";
7111 return ($maxdev, $controller, $controller_prefix);
7114 sub add_hyperv_enlightenments
{
7115 my ($cpuFlags, $winversion, $machine_version, $bios, $gpu_passthrough, $hv_vendor_id) = @_;
7117 return if $winversion < 6;
7118 return if $bios && $bios eq 'ovmf' && $winversion < 8;
7120 if ($gpu_passthrough || defined($hv_vendor_id)) {
7121 $hv_vendor_id //= 'proxmox';
7122 push @$cpuFlags , "hv_vendor_id=$hv_vendor_id";
7125 if (min_version
($machine_version, 2, 3)) {
7126 push @$cpuFlags , 'hv_spinlocks=0x1fff';
7127 push @$cpuFlags , 'hv_vapic';
7128 push @$cpuFlags , 'hv_time';
7130 push @$cpuFlags , 'hv_spinlocks=0xffff';
7133 if (min_version
($machine_version, 2, 6)) {
7134 push @$cpuFlags , 'hv_reset';
7135 push @$cpuFlags , 'hv_vpindex';
7136 push @$cpuFlags , 'hv_runtime';
7139 if ($winversion >= 7) {
7140 push @$cpuFlags , 'hv_relaxed';
7142 if (min_version
($machine_version, 2, 12)) {
7143 push @$cpuFlags , 'hv_synic';
7144 push @$cpuFlags , 'hv_stimer';
7147 if (min_version
($machine_version, 3, 1)) {
7148 push @$cpuFlags , 'hv_ipi';
7153 sub windows_version
{
7156 return 0 if !$ostype;
7160 if($ostype eq 'wxp' || $ostype eq 'w2k3' || $ostype eq 'w2k') {
7162 } elsif($ostype eq 'w2k8' || $ostype eq 'wvista') {
7164 } elsif ($ostype =~ m/^win(\d+)$/) {
7171 sub resolve_dst_disk_format
{
7172 my ($storecfg, $storeid, $src_volname, $format) = @_;
7173 my ($defFormat, $validFormats) = PVE
::Storage
::storage_default_format
($storecfg, $storeid);
7176 # if no target format is specified, use the source disk format as hint
7178 my $scfg = PVE
::Storage
::storage_config
($storecfg, $storeid);
7179 $format = qemu_img_format
($scfg, $src_volname);
7185 # test if requested format is supported - else use default
7186 my $supported = grep { $_ eq $format } @$validFormats;
7187 $format = $defFormat if !$supported;
7191 sub resolve_first_disk
{
7193 my @disks = PVE
::QemuServer
::valid_drive_names
();
7195 foreach my $ds (reverse @disks) {
7196 next if !$conf->{$ds};
7197 my $disk = PVE
::QemuServer
::parse_drive
($ds, $conf->{$ds});
7198 next if PVE
::QemuServer
::drive_is_cdrom
($disk);
7205 my ($uuid, $uuid_str);
7206 UUID
::generate
($uuid);
7207 UUID
::unparse
($uuid, $uuid_str);
7211 sub generate_smbios1_uuid
{
7212 return "uuid=".generate_uuid
();
7218 mon_cmd
($vmid, 'nbd-server-stop');
7221 sub create_reboot_request
{
7223 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
7224 or die "failed to create reboot trigger file: $!\n";
7228 sub clear_reboot_request
{
7230 my $path = "/run/qemu-server/$vmid.reboot";
7233 $res = unlink($path);
7234 die "could not remove reboot request for $vmid: $!"
7235 if !$res && $! != POSIX
::ENOENT
;
7240 # bash completion helper
7242 sub complete_backup_archives
{
7243 my ($cmdname, $pname, $cvalue) = @_;
7245 my $cfg = PVE
::Storage
::config
();
7249 if ($cvalue =~ m/^([^:]+):/) {
7253 my $data = PVE
::Storage
::template_list
($cfg, $storeid, 'backup');
7256 foreach my $id (keys %$data) {
7257 foreach my $item (@{$data->{$id}}) {
7258 next if $item->{format
} !~ m/^vma\.(gz|lzo)$/;
7259 push @$res, $item->{volid
} if defined($item->{volid
});
7266 my $complete_vmid_full = sub {
7269 my $idlist = vmstatus
();
7273 foreach my $id (keys %$idlist) {
7274 my $d = $idlist->{$id};
7275 if (defined($running)) {
7276 next if $d->{template
};
7277 next if $running && $d->{status
} ne 'running';
7278 next if !$running && $d->{status
} eq 'running';
7287 return &$complete_vmid_full();
7290 sub complete_vmid_stopped
{
7291 return &$complete_vmid_full(0);
7294 sub complete_vmid_running
{
7295 return &$complete_vmid_full(1);
7298 sub complete_storage
{
7300 my $cfg = PVE
::Storage
::config
();
7301 my $ids = $cfg->{ids
};
7304 foreach my $sid (keys %$ids) {
7305 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, undef, 1);
7306 next if !$ids->{$sid}->{content
}->{images
};
7313 sub complete_migration_storage
{
7314 my ($cmd, $param, $current_value, $all_args) = @_;
7316 my $targetnode = @$all_args[1];
7318 my $cfg = PVE
::Storage
::config
();
7319 my $ids = $cfg->{ids
};
7322 foreach my $sid (keys %$ids) {
7323 next if !PVE
::Storage
::storage_check_enabled
($cfg, $sid, $targetnode, 1);
7324 next if !$ids->{$sid}->{content
}->{images
};