]> git.proxmox.com Git - qemu-server.git/blame_incremental - PVE/QemuServer.pm
Replace drivename_hash with drivedesc_hash
[qemu-server.git] / PVE / QemuServer.pm
... / ...
CommitLineData
1package PVE::QemuServer;
2
3use strict;
4use warnings;
5
6use Cwd 'abs_path';
7use Digest::SHA;
8use Fcntl ':flock';
9use Fcntl;
10use File::Basename;
11use File::Copy qw(copy);
12use File::Path;
13use File::stat;
14use Getopt::Long;
15use IO::Dir;
16use IO::File;
17use IO::Handle;
18use IO::Select;
19use IO::Socket::UNIX;
20use IPC::Open3;
21use JSON;
22use MIME::Base64;
23use POSIX;
24use Storable qw(dclone);
25use Time::HiRes qw(gettimeofday);
26use URI::Escape;
27use UUID;
28
29use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
30use PVE::DataCenterConfig;
31use PVE::Exception qw(raise raise_param_exc);
32use PVE::GuestHelpers;
33use PVE::INotify;
34use PVE::JSONSchema qw(get_standard_option);
35use PVE::ProcFSTools;
36use PVE::RPCEnvironment;
37use PVE::Storage;
38use PVE::SysFSTools;
39use PVE::Systemd;
40use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline file_get_contents dir_glob_foreach get_host_arch $IPV6RE);
41
42use PVE::QMPClient;
43use PVE::QemuConfig;
44use PVE::QemuServer::Helpers qw(min_version config_aware_timeout);
45use PVE::QemuServer::Cloudinit;
46use PVE::QemuServer::CPUConfig qw(print_cpu_device get_cpu_options);
47use PVE::QemuServer::Machine;
48use PVE::QemuServer::Memory;
49use PVE::QemuServer::Monitor qw(mon_cmd);
50use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port);
51use PVE::QemuServer::USB qw(parse_usb_device);
52
53my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
54my $OVMF = {
55 x86_64 => [
56 "$EDK2_FW_BASE/OVMF_CODE.fd",
57 "$EDK2_FW_BASE/OVMF_VARS.fd"
58 ],
59 aarch64 => [
60 "$EDK2_FW_BASE/AAVMF_CODE.fd",
61 "$EDK2_FW_BASE/AAVMF_VARS.fd"
62 ],
63};
64
65my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
66
67my $QEMU_FORMAT_RE = qr/raw|cow|qcow|qcow2|qed|vmdk|cloop/;
68
69# Note about locking: we use flock on the config file protect
70# against concurent actions.
71# Aditionaly, we have a 'lock' setting in the config file. This
72# can be set to 'migrate', 'backup', 'snapshot' or 'rollback'. Most actions are not
73# allowed when such lock is set. But you can ignore this kind of
74# lock with the --skiplock flag.
75
76cfs_register_file('/qemu-server/',
77 \&parse_vm_config,
78 \&write_vm_config);
79
80PVE::JSONSchema::register_standard_option('pve-qm-stateuri', {
81 description => "Some command save/restore state from this location.",
82 type => 'string',
83 maxLength => 128,
84 optional => 1,
85});
86
87PVE::JSONSchema::register_standard_option('pve-qm-image-format', {
88 type => 'string',
89 enum => [qw(raw cow qcow qed qcow2 vmdk cloop)],
90 description => "The drive's backing file's data format.",
91 optional => 1,
92});
93
94PVE::JSONSchema::register_standard_option('pve-qemu-machine', {
95 description => "Specifies the Qemu machine type.",
96 type => 'string',
97 pattern => '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
98 maxLength => 40,
99 optional => 1,
100});
101
102#no warnings 'redefine';
103
104sub cgroups_write {
105 my ($controller, $vmid, $option, $value) = @_;
106
107 my $path = "/sys/fs/cgroup/$controller/qemu.slice/$vmid.scope/$option";
108 PVE::ProcFSTools::write_proc_entry($path, $value);
109
110}
111
112my $nodename_cache;
113sub nodename {
114 $nodename_cache //= PVE::INotify::nodename();
115 return $nodename_cache;
116}
117
118my $watchdog_fmt = {
119 model => {
120 default_key => 1,
121 type => 'string',
122 enum => [qw(i6300esb ib700)],
123 description => "Watchdog type to emulate.",
124 default => 'i6300esb',
125 optional => 1,
126 },
127 action => {
128 type => 'string',
129 enum => [qw(reset shutdown poweroff pause debug none)],
130 description => "The action to perform if after activation the guest fails to poll the watchdog in time.",
131 optional => 1,
132 },
133};
134PVE::JSONSchema::register_format('pve-qm-watchdog', $watchdog_fmt);
135
136my $agent_fmt = {
137 enabled => {
138 description => "Enable/disable Qemu GuestAgent.",
139 type => 'boolean',
140 default => 0,
141 default_key => 1,
142 },
143 fstrim_cloned_disks => {
144 description => "Run fstrim after cloning/moving a disk.",
145 type => 'boolean',
146 optional => 1,
147 default => 0
148 },
149 type => {
150 description => "Select the agent type",
151 type => 'string',
152 default => 'virtio',
153 optional => 1,
154 enum => [qw(virtio isa)],
155 },
156};
157
158my $vga_fmt = {
159 type => {
160 description => "Select the VGA type.",
161 type => 'string',
162 default => 'std',
163 optional => 1,
164 default_key => 1,
165 enum => [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio vmware)],
166 },
167 memory => {
168 description => "Sets the VGA memory (in MiB). Has no effect with serial display.",
169 type => 'integer',
170 optional => 1,
171 minimum => 4,
172 maximum => 512,
173 },
174};
175
176my $ivshmem_fmt = {
177 size => {
178 type => 'integer',
179 minimum => 1,
180 description => "The size of the file in MB.",
181 },
182 name => {
183 type => 'string',
184 pattern => '[a-zA-Z0-9\-]+',
185 optional => 1,
186 format_description => 'string',
187 description => "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
188 },
189};
190
191my $audio_fmt = {
192 device => {
193 type => 'string',
194 enum => [qw(ich9-intel-hda intel-hda AC97)],
195 description => "Configure an audio device."
196 },
197 driver => {
198 type => 'string',
199 enum => ['spice'],
200 default => 'spice',
201 optional => 1,
202 description => "Driver backend for the audio device."
203 },
204};
205
206my $spice_enhancements_fmt = {
207 foldersharing => {
208 type => 'boolean',
209 optional => 1,
210 default => '0',
211 description => "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
212 },
213 videostreaming => {
214 type => 'string',
215 enum => ['off', 'all', 'filter'],
216 default => 'off',
217 optional => 1,
218 description => "Enable video streaming. Uses compression for detected video streams."
219 },
220};
221
222my $rng_fmt = {
223 source => {
224 type => 'string',
225 enum => ['/dev/urandom', '/dev/random', '/dev/hwrng'],
226 default_key => 1,
227 description => "The file on the host to gather entropy from. In most"
228 . " cases /dev/urandom should be preferred over /dev/random"
229 . " to avoid entropy-starvation issues on the host. Using"
230 . " urandom does *not* decrease security in any meaningful"
231 . " way, as it's still seeded from real entropy, and the"
232 . " bytes provided will most likely be mixed with real"
233 . " entropy on the guest as well. /dev/hwrng can be used"
234 . " to pass through a hardware RNG from the host.",
235 },
236 max_bytes => {
237 type => 'integer',
238 description => "Maximum bytes of entropy injected into the guest every"
239 . " 'period' milliseconds. Prefer a lower value when using"
240 . " /dev/random as source. Use 0 to disable limiting"
241 . " (potentially dangerous!).",
242 optional => 1,
243
244 # default is 1 KiB/s, provides enough entropy to the guest to avoid
245 # boot-starvation issues (e.g. systemd etc...) while allowing no chance
246 # of overwhelming the host, provided we're reading from /dev/urandom
247 default => 1024,
248 },
249 period => {
250 type => 'integer',
251 description => "Every 'period' milliseconds the entropy-injection quota"
252 . " is reset, allowing the guest to retrieve another"
253 . " 'max_bytes' of entropy.",
254 optional => 1,
255 default => 1000,
256 },
257};
258
259my $confdesc = {
260 onboot => {
261 optional => 1,
262 type => 'boolean',
263 description => "Specifies whether a VM will be started during system bootup.",
264 default => 0,
265 },
266 autostart => {
267 optional => 1,
268 type => 'boolean',
269 description => "Automatic restart after crash (currently ignored).",
270 default => 0,
271 },
272 hotplug => {
273 optional => 1,
274 type => 'string', format => 'pve-hotplug-features',
275 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'.",
276 default => 'network,disk,usb',
277 },
278 reboot => {
279 optional => 1,
280 type => 'boolean',
281 description => "Allow reboot. If set to '0' the VM exit on reboot.",
282 default => 1,
283 },
284 lock => {
285 optional => 1,
286 type => 'string',
287 description => "Lock/unlock the VM.",
288 enum => [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
289 },
290 cpulimit => {
291 optional => 1,
292 type => 'number',
293 description => "Limit of CPU usage.",
294 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.",
295 minimum => 0,
296 maximum => 128,
297 default => 0,
298 },
299 cpuunits => {
300 optional => 1,
301 type => 'integer',
302 description => "CPU weight for a VM.",
303 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.",
304 minimum => 2,
305 maximum => 262144,
306 default => 1024,
307 },
308 memory => {
309 optional => 1,
310 type => 'integer',
311 description => "Amount of RAM for the VM in MB. This is the maximum available memory when you use the balloon device.",
312 minimum => 16,
313 default => 512,
314 },
315 balloon => {
316 optional => 1,
317 type => 'integer',
318 description => "Amount of target RAM for the VM in MB. Using zero disables the ballon driver.",
319 minimum => 0,
320 },
321 shares => {
322 optional => 1,
323 type => 'integer',
324 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.",
325 minimum => 0,
326 maximum => 50000,
327 default => 1000,
328 },
329 keyboard => {
330 optional => 1,
331 type => 'string',
332 description => "Keybord layout for vnc server. Default is read from the '/etc/pve/datacenter.cfg' configuration file.".
333 "It should not be necessary to set it.",
334 enum => PVE::Tools::kvmkeymaplist(),
335 default => undef,
336 },
337 name => {
338 optional => 1,
339 type => 'string', format => 'dns-name',
340 description => "Set a name for the VM. Only used on the configuration web interface.",
341 },
342 scsihw => {
343 optional => 1,
344 type => 'string',
345 description => "SCSI controller model",
346 enum => [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
347 default => 'lsi',
348 },
349 description => {
350 optional => 1,
351 type => 'string',
352 description => "Description for the VM. Only used on the configuration web interface. This is saved as comment inside the configuration file.",
353 },
354 ostype => {
355 optional => 1,
356 type => 'string',
357 enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 l24 l26 solaris)],
358 description => "Specify guest operating system.",
359 verbose_description => <<EODESC,
360Specify guest operating system. This is used to enable special
361optimization/features for specific operating systems:
362
363[horizontal]
364other;; unspecified OS
365wxp;; Microsoft Windows XP
366w2k;; Microsoft Windows 2000
367w2k3;; Microsoft Windows 2003
368w2k8;; Microsoft Windows 2008
369wvista;; Microsoft Windows Vista
370win7;; Microsoft Windows 7
371win8;; Microsoft Windows 8/2012/2012r2
372win10;; Microsoft Windows 10/2016
373l24;; Linux 2.4 Kernel
374l26;; Linux 2.6 - 5.X Kernel
375solaris;; Solaris/OpenSolaris/OpenIndiania kernel
376EODESC
377 },
378 boot => {
379 optional => 1,
380 type => 'string',
381 description => "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n).",
382 pattern => '[acdn]{1,4}',
383 default => 'cdn',
384 },
385 bootdisk => {
386 optional => 1,
387 type => 'string', format => 'pve-qm-bootdisk',
388 description => "Enable booting from specified disk.",
389 pattern => '(ide|sata|scsi|virtio)\d+',
390 },
391 smp => {
392 optional => 1,
393 type => 'integer',
394 description => "The number of CPUs. Please use option -sockets instead.",
395 minimum => 1,
396 default => 1,
397 },
398 sockets => {
399 optional => 1,
400 type => 'integer',
401 description => "The number of CPU sockets.",
402 minimum => 1,
403 default => 1,
404 },
405 cores => {
406 optional => 1,
407 type => 'integer',
408 description => "The number of cores per socket.",
409 minimum => 1,
410 default => 1,
411 },
412 numa => {
413 optional => 1,
414 type => 'boolean',
415 description => "Enable/disable NUMA.",
416 default => 0,
417 },
418 hugepages => {
419 optional => 1,
420 type => 'string',
421 description => "Enable/disable hugepages memory.",
422 enum => [qw(any 2 1024)],
423 },
424 vcpus => {
425 optional => 1,
426 type => 'integer',
427 description => "Number of hotplugged vcpus.",
428 minimum => 1,
429 default => 0,
430 },
431 acpi => {
432 optional => 1,
433 type => 'boolean',
434 description => "Enable/disable ACPI.",
435 default => 1,
436 },
437 agent => {
438 optional => 1,
439 description => "Enable/disable Qemu GuestAgent and its properties.",
440 type => 'string',
441 format => $agent_fmt,
442 },
443 kvm => {
444 optional => 1,
445 type => 'boolean',
446 description => "Enable/disable KVM hardware virtualization.",
447 default => 1,
448 },
449 tdf => {
450 optional => 1,
451 type => 'boolean',
452 description => "Enable/disable time drift fix.",
453 default => 0,
454 },
455 localtime => {
456 optional => 1,
457 type => 'boolean',
458 description => "Set the real time clock to local time. This is enabled by default if ostype indicates a Microsoft OS.",
459 },
460 freeze => {
461 optional => 1,
462 type => 'boolean',
463 description => "Freeze CPU at startup (use 'c' monitor command to start execution).",
464 },
465 vga => {
466 optional => 1,
467 type => 'string', format => $vga_fmt,
468 description => "Configure the VGA hardware.",
469 verbose_description => "Configure the VGA Hardware. If you want to use ".
470 "high resolution modes (>= 1280x1024x16) you may need to increase " .
471 "the vga memory option. Since QEMU 2.9 the default VGA display type " .
472 "is 'std' for all OS types besides some Windows versions (XP and " .
473 "older) which use 'cirrus'. The 'qxl' option enables the SPICE " .
474 "display server. For win* OS you can select how many independent " .
475 "displays you want, Linux guests can add displays them self.\n".
476 "You can also run without any graphic card, using a serial device as terminal.",
477 },
478 watchdog => {
479 optional => 1,
480 type => 'string', format => 'pve-qm-watchdog',
481 description => "Create a virtual hardware watchdog device.",
482 verbose_description => "Create a virtual hardware watchdog device. Once enabled" .
483 " (by a guest action), the watchdog must be periodically polled " .
484 "by an agent inside the guest or else the watchdog will reset " .
485 "the guest (or execute the respective action specified)",
486 },
487 startdate => {
488 optional => 1,
489 type => 'string',
490 typetext => "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
491 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'.",
492 pattern => '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
493 default => 'now',
494 },
495 startup => get_standard_option('pve-startup-order'),
496 template => {
497 optional => 1,
498 type => 'boolean',
499 description => "Enable/disable Template.",
500 default => 0,
501 },
502 args => {
503 optional => 1,
504 type => 'string',
505 description => "Arbitrary arguments passed to kvm.",
506 verbose_description => <<EODESCR,
507Arbitrary arguments passed to kvm, for example:
508
509args: -no-reboot -no-hpet
510
511NOTE: this option is for experts only.
512EODESCR
513 },
514 tablet => {
515 optional => 1,
516 type => 'boolean',
517 default => 1,
518 description => "Enable/disable the USB tablet device.",
519 verbose_description => "Enable/disable the USB tablet device. This device is " .
520 "usually needed to allow absolute mouse positioning with VNC. " .
521 "Else the mouse runs out of sync with normal VNC clients. " .
522 "If you're running lots of console-only guests on one host, " .
523 "you may consider disabling this to save some context switches. " .
524 "This is turned off by default if you use spice (-vga=qxl).",
525 },
526 migrate_speed => {
527 optional => 1,
528 type => 'integer',
529 description => "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
530 minimum => 0,
531 default => 0,
532 },
533 migrate_downtime => {
534 optional => 1,
535 type => 'number',
536 description => "Set maximum tolerated downtime (in seconds) for migrations.",
537 minimum => 0,
538 default => 0.1,
539 },
540 cdrom => {
541 optional => 1,
542 type => 'string', format => 'pve-qm-ide',
543 typetext => '<volume>',
544 description => "This is an alias for option -ide2",
545 },
546 cpu => {
547 optional => 1,
548 description => "Emulated CPU type.",
549 type => 'string',
550 format => $PVE::QemuServer::CPUConfig::cpu_fmt,
551 },
552 parent => get_standard_option('pve-snapshot-name', {
553 optional => 1,
554 description => "Parent snapshot name. This is used internally, and should not be modified.",
555 }),
556 snaptime => {
557 optional => 1,
558 description => "Timestamp for snapshots.",
559 type => 'integer',
560 minimum => 0,
561 },
562 vmstate => {
563 optional => 1,
564 type => 'string', format => 'pve-volume-id',
565 description => "Reference to a volume which stores the VM state. This is used internally for snapshots.",
566 },
567 vmstatestorage => get_standard_option('pve-storage-id', {
568 description => "Default storage for VM state volumes/files.",
569 optional => 1,
570 }),
571 runningmachine => get_standard_option('pve-qemu-machine', {
572 description => "Specifies the Qemu machine type of the running vm. This is used internally for snapshots.",
573 }),
574 machine => get_standard_option('pve-qemu-machine'),
575 arch => {
576 description => "Virtual processor architecture. Defaults to the host.",
577 optional => 1,
578 type => 'string',
579 enum => [qw(x86_64 aarch64)],
580 },
581 smbios1 => {
582 description => "Specify SMBIOS type 1 fields.",
583 type => 'string', format => 'pve-qm-smbios1',
584 maxLength => 512,
585 optional => 1,
586 },
587 protection => {
588 optional => 1,
589 type => 'boolean',
590 description => "Sets the protection flag of the VM. This will disable the remove VM and remove disk operations.",
591 default => 0,
592 },
593 bios => {
594 optional => 1,
595 type => 'string',
596 enum => [ qw(seabios ovmf) ],
597 description => "Select BIOS implementation.",
598 default => 'seabios',
599 },
600 vmgenid => {
601 type => 'string',
602 pattern => '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
603 format_description => 'UUID',
604 description => "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0' to disable explicitly.",
605 verbose_description => "The VM generation ID (vmgenid) device exposes a".
606 " 128-bit integer value identifier to the guest OS. This allows to".
607 " notify the guest operating system when the virtual machine is".
608 " executed with a different configuration (e.g. snapshot execution".
609 " or creation from a template). The guest operating system notices".
610 " the change, and is then able to react as appropriate by marking".
611 " its copies of distributed databases as dirty, re-initializing its".
612 " random number generator, etc.\n".
613 "Note that auto-creation only works when done throug API/CLI create".
614 " or update methods, but not when manually editing the config file.",
615 default => "1 (autogenerated)",
616 optional => 1,
617 },
618 hookscript => {
619 type => 'string',
620 format => 'pve-volume-id',
621 optional => 1,
622 description => "Script that will be executed during various steps in the vms lifetime.",
623 },
624 ivshmem => {
625 type => 'string',
626 format => $ivshmem_fmt,
627 description => "Inter-VM shared memory. Useful for direct communication between VMs, or to the host.",
628 optional => 1,
629 },
630 audio0 => {
631 type => 'string',
632 format => $audio_fmt,
633 description => "Configure a audio device, useful in combination with QXL/Spice.",
634 optional => 1
635 },
636 spice_enhancements => {
637 type => 'string',
638 format => $spice_enhancements_fmt,
639 description => "Configure additional enhancements for SPICE.",
640 optional => 1
641 },
642 tags => {
643 type => 'string', format => 'pve-tag-list',
644 description => 'Tags of the VM. This is only meta information.',
645 optional => 1,
646 },
647 rng0 => {
648 type => 'string',
649 format => $rng_fmt,
650 description => "Configure a VirtIO-based Random Number Generator.",
651 optional => 1,
652 },
653};
654
655my $cicustom_fmt = {
656 meta => {
657 type => 'string',
658 optional => 1,
659 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.',
660 format => 'pve-volume-id',
661 format_description => 'volume',
662 },
663 network => {
664 type => 'string',
665 optional => 1,
666 description => 'Specify a custom file containing all network data passed to the VM via cloud-init.',
667 format => 'pve-volume-id',
668 format_description => 'volume',
669 },
670 user => {
671 type => 'string',
672 optional => 1,
673 description => 'Specify a custom file containing all user data passed to the VM via cloud-init.',
674 format => 'pve-volume-id',
675 format_description => 'volume',
676 },
677};
678PVE::JSONSchema::register_format('pve-qm-cicustom', $cicustom_fmt);
679
680my $confdesc_cloudinit = {
681 citype => {
682 optional => 1,
683 type => 'string',
684 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.',
685 enum => ['configdrive2', 'nocloud'],
686 },
687 ciuser => {
688 optional => 1,
689 type => 'string',
690 description => "cloud-init: User name to change ssh keys and password for instead of the image's configured default user.",
691 },
692 cipassword => {
693 optional => 1,
694 type => 'string',
695 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.',
696 },
697 cicustom => {
698 optional => 1,
699 type => 'string',
700 description => 'cloud-init: Specify custom files to replace the automatically generated ones at start.',
701 format => 'pve-qm-cicustom',
702 },
703 searchdomain => {
704 optional => 1,
705 type => 'string',
706 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.",
707 },
708 nameserver => {
709 optional => 1,
710 type => 'string', format => 'address-list',
711 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.",
712 },
713 sshkeys => {
714 optional => 1,
715 type => 'string',
716 format => 'urlencoded',
717 description => "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
718 },
719};
720
721# what about other qemu settings ?
722#cpu => 'string',
723#machine => 'string',
724#fda => 'file',
725#fdb => 'file',
726#mtdblock => 'file',
727#sd => 'file',
728#pflash => 'file',
729#snapshot => 'bool',
730#bootp => 'file',
731##tftp => 'dir',
732##smb => 'dir',
733#kernel => 'file',
734#append => 'string',
735#initrd => 'file',
736##soundhw => 'string',
737
738while (my ($k, $v) = each %$confdesc) {
739 PVE::JSONSchema::register_standard_option("pve-qm-$k", $v);
740}
741
742my $MAX_IDE_DISKS = 4;
743my $MAX_SCSI_DISKS = 31;
744my $MAX_VIRTIO_DISKS = 16;
745my $MAX_SATA_DISKS = 6;
746my $MAX_USB_DEVICES = 5;
747my $MAX_NETS = 32;
748my $MAX_UNUSED_DISKS = 256;
749my $MAX_HOSTPCI_DEVICES = 16;
750my $MAX_SERIAL_PORTS = 4;
751my $MAX_PARALLEL_PORTS = 3;
752my $MAX_NUMA = 8;
753
754my $numa_fmt = {
755 cpus => {
756 type => "string",
757 pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
758 description => "CPUs accessing this NUMA node.",
759 format_description => "id[-id];...",
760 },
761 memory => {
762 type => "number",
763 description => "Amount of memory this NUMA node provides.",
764 optional => 1,
765 },
766 hostnodes => {
767 type => "string",
768 pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
769 description => "Host NUMA nodes to use.",
770 format_description => "id[-id];...",
771 optional => 1,
772 },
773 policy => {
774 type => 'string',
775 enum => [qw(preferred bind interleave)],
776 description => "NUMA allocation policy.",
777 optional => 1,
778 },
779};
780PVE::JSONSchema::register_format('pve-qm-numanode', $numa_fmt);
781my $numadesc = {
782 optional => 1,
783 type => 'string', format => $numa_fmt,
784 description => "NUMA topology.",
785};
786PVE::JSONSchema::register_standard_option("pve-qm-numanode", $numadesc);
787
788for (my $i = 0; $i < $MAX_NUMA; $i++) {
789 $confdesc->{"numa$i"} = $numadesc;
790}
791
792my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000', 'pcnet', 'virtio',
793 'ne2k_isa', 'i82551', 'i82557b', 'i82559er', 'vmxnet3',
794 'e1000-82540em', 'e1000-82544gc', 'e1000-82545em'];
795my $nic_model_list_txt = join(' ', sort @$nic_model_list);
796
797my $net_fmt_bridge_descr = <<__EOD__;
798Bridge to attach the network device to. The Proxmox VE standard bridge
799is called 'vmbr0'.
800
801If you do not specify a bridge, we create a kvm user (NATed) network
802device, which provides DHCP and DNS services. The following addresses
803are used:
804
805 10.0.2.2 Gateway
806 10.0.2.3 DNS Server
807 10.0.2.4 SMB Server
808
809The DHCP server assign addresses to the guest starting from 10.0.2.15.
810__EOD__
811
812my $net_fmt = {
813 macaddr => get_standard_option('mac-addr', {
814 description => "MAC address. That address must be unique withing your network. This is automatically generated if not specified.",
815 }),
816 model => {
817 type => 'string',
818 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'.",
819 enum => $nic_model_list,
820 default_key => 1,
821 },
822 (map { $_ => { keyAlias => 'model', alias => 'macaddr' }} @$nic_model_list),
823 bridge => {
824 type => 'string',
825 description => $net_fmt_bridge_descr,
826 format_description => 'bridge',
827 optional => 1,
828 },
829 queues => {
830 type => 'integer',
831 minimum => 0, maximum => 16,
832 description => 'Number of packet queues to be used on the device.',
833 optional => 1,
834 },
835 rate => {
836 type => 'number',
837 minimum => 0,
838 description => "Rate limit in mbps (megabytes per second) as floating point number.",
839 optional => 1,
840 },
841 tag => {
842 type => 'integer',
843 minimum => 1, maximum => 4094,
844 description => 'VLAN tag to apply to packets on this interface.',
845 optional => 1,
846 },
847 trunks => {
848 type => 'string',
849 pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
850 description => 'VLAN trunks to pass through this interface.',
851 format_description => 'vlanid[;vlanid...]',
852 optional => 1,
853 },
854 firewall => {
855 type => 'boolean',
856 description => 'Whether this interface should be protected by the firewall.',
857 optional => 1,
858 },
859 link_down => {
860 type => 'boolean',
861 description => 'Whether this interface should be disconnected (like pulling the plug).',
862 optional => 1,
863 },
864};
865
866my $netdesc = {
867 optional => 1,
868 type => 'string', format => $net_fmt,
869 description => "Specify network devices.",
870};
871
872PVE::JSONSchema::register_standard_option("pve-qm-net", $netdesc);
873
874my $ipconfig_fmt = {
875 ip => {
876 type => 'string',
877 format => 'pve-ipv4-config',
878 format_description => 'IPv4Format/CIDR',
879 description => 'IPv4 address in CIDR format.',
880 optional => 1,
881 default => 'dhcp',
882 },
883 gw => {
884 type => 'string',
885 format => 'ipv4',
886 format_description => 'GatewayIPv4',
887 description => 'Default gateway for IPv4 traffic.',
888 optional => 1,
889 requires => 'ip',
890 },
891 ip6 => {
892 type => 'string',
893 format => 'pve-ipv6-config',
894 format_description => 'IPv6Format/CIDR',
895 description => 'IPv6 address in CIDR format.',
896 optional => 1,
897 default => 'dhcp',
898 },
899 gw6 => {
900 type => 'string',
901 format => 'ipv6',
902 format_description => 'GatewayIPv6',
903 description => 'Default gateway for IPv6 traffic.',
904 optional => 1,
905 requires => 'ip6',
906 },
907};
908PVE::JSONSchema::register_format('pve-qm-ipconfig', $ipconfig_fmt);
909my $ipconfigdesc = {
910 optional => 1,
911 type => 'string', format => 'pve-qm-ipconfig',
912 description => <<'EODESCR',
913cloud-init: Specify IP addresses and gateways for the corresponding interface.
914
915IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
916
917The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit gateway should be provided.
918For IPv6 the special string 'auto' can be used to use stateless autoconfiguration.
919
920If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using dhcp on IPv4.
921EODESCR
922};
923PVE::JSONSchema::register_standard_option("pve-qm-ipconfig", $netdesc);
924
925for (my $i = 0; $i < $MAX_NETS; $i++) {
926 $confdesc->{"net$i"} = $netdesc;
927 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
928}
929
930foreach my $key (keys %$confdesc_cloudinit) {
931 $confdesc->{$key} = $confdesc_cloudinit->{$key};
932}
933
934PVE::JSONSchema::register_format('pve-volume-id-or-qm-path', \&verify_volume_id_or_qm_path);
935sub verify_volume_id_or_qm_path {
936 my ($volid, $noerr) = @_;
937
938 if ($volid eq 'none' || $volid eq 'cdrom' || $volid =~ m|^/|) {
939 return $volid;
940 }
941
942 # if its neither 'none' nor 'cdrom' nor a path, check if its a volume-id
943 $volid = eval { PVE::JSONSchema::check_format('pve-volume-id', $volid, '') };
944 if ($@) {
945 return undef if $noerr;
946 die $@;
947 }
948 return $volid;
949}
950
951my $drivedesc_hash;
952
953my %drivedesc_base = (
954 volume => { alias => 'file' },
955 file => {
956 type => 'string',
957 format => 'pve-volume-id-or-qm-path',
958 default_key => 1,
959 format_description => 'volume',
960 description => "The drive's backing volume.",
961 },
962 media => {
963 type => 'string',
964 enum => [qw(cdrom disk)],
965 description => "The drive's media type.",
966 default => 'disk',
967 optional => 1
968 },
969 cyls => {
970 type => 'integer',
971 description => "Force the drive's physical geometry to have a specific cylinder count.",
972 optional => 1
973 },
974 heads => {
975 type => 'integer',
976 description => "Force the drive's physical geometry to have a specific head count.",
977 optional => 1
978 },
979 secs => {
980 type => 'integer',
981 description => "Force the drive's physical geometry to have a specific sector count.",
982 optional => 1
983 },
984 trans => {
985 type => 'string',
986 enum => [qw(none lba auto)],
987 description => "Force disk geometry bios translation mode.",
988 optional => 1,
989 },
990 snapshot => {
991 type => 'boolean',
992 description => "Controls qemu's snapshot mode feature."
993 . " If activated, changes made to the disk are temporary and will"
994 . " be discarded when the VM is shutdown.",
995 optional => 1,
996 },
997 cache => {
998 type => 'string',
999 enum => [qw(none writethrough writeback unsafe directsync)],
1000 description => "The drive's cache mode",
1001 optional => 1,
1002 },
1003 format => get_standard_option('pve-qm-image-format'),
1004 size => {
1005 type => 'string',
1006 format => 'disk-size',
1007 format_description => 'DiskSize',
1008 description => "Disk size. This is purely informational and has no effect.",
1009 optional => 1,
1010 },
1011 backup => {
1012 type => 'boolean',
1013 description => "Whether the drive should be included when making backups.",
1014 optional => 1,
1015 },
1016 replicate => {
1017 type => 'boolean',
1018 description => 'Whether the drive should considered for replication jobs.',
1019 optional => 1,
1020 default => 1,
1021 },
1022 rerror => {
1023 type => 'string',
1024 enum => [qw(ignore report stop)],
1025 description => 'Read error action.',
1026 optional => 1,
1027 },
1028 werror => {
1029 type => 'string',
1030 enum => [qw(enospc ignore report stop)],
1031 description => 'Write error action.',
1032 optional => 1,
1033 },
1034 aio => {
1035 type => 'string',
1036 enum => [qw(native threads)],
1037 description => 'AIO type to use.',
1038 optional => 1,
1039 },
1040 discard => {
1041 type => 'string',
1042 enum => [qw(ignore on)],
1043 description => 'Controls whether to pass discard/trim requests to the underlying storage.',
1044 optional => 1,
1045 },
1046 detect_zeroes => {
1047 type => 'boolean',
1048 description => 'Controls whether to detect and try to optimize writes of zeroes.',
1049 optional => 1,
1050 },
1051 serial => {
1052 type => 'string',
1053 format => 'urlencoded',
1054 format_description => 'serial',
1055 maxLength => 20*3, # *3 since it's %xx url enoded
1056 description => "The drive's reported serial number, url-encoded, up to 20 bytes long.",
1057 optional => 1,
1058 },
1059 shared => {
1060 type => 'boolean',
1061 description => 'Mark this locally-managed volume as available on all nodes',
1062 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!",
1063 optional => 1,
1064 default => 0,
1065 }
1066);
1067
1068my %iothread_fmt = ( iothread => {
1069 type => 'boolean',
1070 description => "Whether to use iothreads for this drive",
1071 optional => 1,
1072});
1073
1074my %model_fmt = (
1075 model => {
1076 type => 'string',
1077 format => 'urlencoded',
1078 format_description => 'model',
1079 maxLength => 40*3, # *3 since it's %xx url enoded
1080 description => "The drive's reported model name, url-encoded, up to 40 bytes long.",
1081 optional => 1,
1082 },
1083);
1084
1085my %queues_fmt = (
1086 queues => {
1087 type => 'integer',
1088 description => "Number of queues.",
1089 minimum => 2,
1090 optional => 1
1091 }
1092);
1093
1094my %scsiblock_fmt = (
1095 scsiblock => {
1096 type => 'boolean',
1097 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",
1098 optional => 1,
1099 default => 0,
1100 },
1101);
1102
1103my %ssd_fmt = (
1104 ssd => {
1105 type => 'boolean',
1106 description => "Whether to expose this drive as an SSD, rather than a rotational hard disk.",
1107 optional => 1,
1108 },
1109);
1110
1111my %wwn_fmt = (
1112 wwn => {
1113 type => 'string',
1114 pattern => qr/^(0x)[0-9a-fA-F]{16}/,
1115 format_description => 'wwn',
1116 description => "The drive's worldwide name, encoded as 16 bytes hex string, prefixed by '0x'.",
1117 optional => 1,
1118 },
1119);
1120
1121my $add_throttle_desc = sub {
1122 my ($key, $type, $what, $unit, $longunit, $minimum) = @_;
1123 my $d = {
1124 type => $type,
1125 format_description => $unit,
1126 description => "Maximum $what in $longunit.",
1127 optional => 1,
1128 };
1129 $d->{minimum} = $minimum if defined($minimum);
1130 $drivedesc_base{$key} = $d;
1131};
1132# throughput: (leaky bucket)
1133$add_throttle_desc->('bps', 'integer', 'r/w speed', 'bps', 'bytes per second');
1134$add_throttle_desc->('bps_rd', 'integer', 'read speed', 'bps', 'bytes per second');
1135$add_throttle_desc->('bps_wr', 'integer', 'write speed', 'bps', 'bytes per second');
1136$add_throttle_desc->('mbps', 'number', 'r/w speed', 'mbps', 'megabytes per second');
1137$add_throttle_desc->('mbps_rd', 'number', 'read speed', 'mbps', 'megabytes per second');
1138$add_throttle_desc->('mbps_wr', 'number', 'write speed', 'mbps', 'megabytes per second');
1139$add_throttle_desc->('iops', 'integer', 'r/w I/O', 'iops', 'operations per second');
1140$add_throttle_desc->('iops_rd', 'integer', 'read I/O', 'iops', 'operations per second');
1141$add_throttle_desc->('iops_wr', 'integer', 'write I/O', 'iops', 'operations per second');
1142
1143# pools: (pool of IO before throttling starts taking effect)
1144$add_throttle_desc->('mbps_max', 'number', 'unthrottled r/w pool', 'mbps', 'megabytes per second');
1145$add_throttle_desc->('mbps_rd_max', 'number', 'unthrottled read pool', 'mbps', 'megabytes per second');
1146$add_throttle_desc->('mbps_wr_max', 'number', 'unthrottled write pool', 'mbps', 'megabytes per second');
1147$add_throttle_desc->('iops_max', 'integer', 'unthrottled r/w I/O pool', 'iops', 'operations per second');
1148$add_throttle_desc->('iops_rd_max', 'integer', 'unthrottled read I/O pool', 'iops', 'operations per second');
1149$add_throttle_desc->('iops_wr_max', 'integer', 'unthrottled write I/O pool', 'iops', 'operations per second');
1150
1151# burst lengths
1152$add_throttle_desc->('bps_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
1153$add_throttle_desc->('bps_rd_max_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
1154$add_throttle_desc->('bps_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
1155$add_throttle_desc->('iops_max_length', 'integer', 'length of I/O bursts', 'seconds', 'seconds', 1);
1156$add_throttle_desc->('iops_rd_max_length', 'integer', 'length of read I/O bursts', 'seconds', 'seconds', 1);
1157$add_throttle_desc->('iops_wr_max_length', 'integer', 'length of write I/O bursts', 'seconds', 'seconds', 1);
1158
1159# legacy support
1160$drivedesc_base{'bps_rd_length'} = { alias => 'bps_rd_max_length' };
1161$drivedesc_base{'bps_wr_length'} = { alias => 'bps_wr_max_length' };
1162$drivedesc_base{'iops_rd_length'} = { alias => 'iops_rd_max_length' };
1163$drivedesc_base{'iops_wr_length'} = { alias => 'iops_wr_max_length' };
1164
1165my $ide_fmt = {
1166 %drivedesc_base,
1167 %model_fmt,
1168 %ssd_fmt,
1169 %wwn_fmt,
1170};
1171PVE::JSONSchema::register_format("pve-qm-ide", $ide_fmt);
1172
1173my $idedesc = {
1174 optional => 1,
1175 type => 'string', format => $ide_fmt,
1176 description => "Use volume as IDE hard disk or CD-ROM (n is 0 to " .($MAX_IDE_DISKS -1) . ").",
1177};
1178PVE::JSONSchema::register_standard_option("pve-qm-ide", $idedesc);
1179
1180my $scsi_fmt = {
1181 %drivedesc_base,
1182 %iothread_fmt,
1183 %queues_fmt,
1184 %scsiblock_fmt,
1185 %ssd_fmt,
1186 %wwn_fmt,
1187};
1188my $scsidesc = {
1189 optional => 1,
1190 type => 'string', format => $scsi_fmt,
1191 description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to " . ($MAX_SCSI_DISKS - 1) . ").",
1192};
1193PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc);
1194
1195my $sata_fmt = {
1196 %drivedesc_base,
1197 %ssd_fmt,
1198 %wwn_fmt,
1199};
1200my $satadesc = {
1201 optional => 1,
1202 type => 'string', format => $sata_fmt,
1203 description => "Use volume as SATA hard disk or CD-ROM (n is 0 to " . ($MAX_SATA_DISKS - 1). ").",
1204};
1205PVE::JSONSchema::register_standard_option("pve-qm-sata", $satadesc);
1206
1207my $virtio_fmt = {
1208 %drivedesc_base,
1209 %iothread_fmt,
1210};
1211my $virtiodesc = {
1212 optional => 1,
1213 type => 'string', format => $virtio_fmt,
1214 description => "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . ").",
1215};
1216PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc);
1217
1218my $alldrive_fmt = {
1219 %drivedesc_base,
1220 %iothread_fmt,
1221 %model_fmt,
1222 %queues_fmt,
1223 %scsiblock_fmt,
1224 %ssd_fmt,
1225 %wwn_fmt,
1226};
1227
1228my $efidisk_fmt = {
1229 volume => { alias => 'file' },
1230 file => {
1231 type => 'string',
1232 format => 'pve-volume-id-or-qm-path',
1233 default_key => 1,
1234 format_description => 'volume',
1235 description => "The drive's backing volume.",
1236 },
1237 format => get_standard_option('pve-qm-image-format'),
1238 size => {
1239 type => 'string',
1240 format => 'disk-size',
1241 format_description => 'DiskSize',
1242 description => "Disk size. This is purely informational and has no effect.",
1243 optional => 1,
1244 },
1245};
1246
1247my $efidisk_desc = {
1248 optional => 1,
1249 type => 'string', format => $efidisk_fmt,
1250 description => "Configure a Disk for storing EFI vars",
1251};
1252
1253PVE::JSONSchema::register_standard_option("pve-qm-efidisk", $efidisk_desc);
1254
1255my $usb_fmt = {
1256 host => {
1257 default_key => 1,
1258 type => 'string', format => 'pve-qm-usb-device',
1259 format_description => 'HOSTUSBDEVICE|spice',
1260 description => <<EODESCR,
1261The Host USB device or port or the value 'spice'. HOSTUSBDEVICE syntax is:
1262
1263 'bus-port(.port)*' (decimal numbers) or
1264 'vendor_id:product_id' (hexadeciaml numbers) or
1265 'spice'
1266
1267You can use the 'lsusb -t' command to list existing usb devices.
1268
1269NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
1270
1271The value 'spice' can be used to add a usb redirection devices for spice.
1272EODESCR
1273 },
1274 usb3 => {
1275 optional => 1,
1276 type => 'boolean',
1277 description => "Specifies whether if given host option is a USB3 device or port.",
1278 default => 0,
1279 },
1280};
1281
1282my $usbdesc = {
1283 optional => 1,
1284 type => 'string', format => $usb_fmt,
1285 description => "Configure an USB device (n is 0 to 4).",
1286};
1287PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
1288
1289my $PCIRE = qr/([a-f0-9]{4}:)?[a-f0-9]{2}:[a-f0-9]{2}(?:\.[a-f0-9])?/;
1290my $hostpci_fmt = {
1291 host => {
1292 default_key => 1,
1293 type => 'string',
1294 pattern => qr/$PCIRE(;$PCIRE)*/,
1295 format_description => 'HOSTPCIID[;HOSTPCIID2...]',
1296 description => <<EODESCR,
1297Host PCI device pass through. The PCI ID of a host's PCI device or a list
1298of PCI virtual functions of the host. HOSTPCIID syntax is:
1299
1300'bus:dev.func' (hexadecimal numbers)
1301
1302You can us the 'lspci' command to list existing PCI devices.
1303EODESCR
1304 },
1305 rombar => {
1306 type => 'boolean',
1307 description => "Specify whether or not the device's ROM will be visible in the guest's memory map.",
1308 optional => 1,
1309 default => 1,
1310 },
1311 romfile => {
1312 type => 'string',
1313 pattern => '[^,;]+',
1314 format_description => 'string',
1315 description => "Custom pci device rom filename (must be located in /usr/share/kvm/).",
1316 optional => 1,
1317 },
1318 pcie => {
1319 type => 'boolean',
1320 description => "Choose the PCI-express bus (needs the 'q35' machine model).",
1321 optional => 1,
1322 default => 0,
1323 },
1324 'x-vga' => {
1325 type => 'boolean',
1326 description => "Enable vfio-vga device support.",
1327 optional => 1,
1328 default => 0,
1329 },
1330 'mdev' => {
1331 type => 'string',
1332 format_description => 'string',
1333 pattern => '[^/\.:]+',
1334 optional => 1,
1335 description => <<EODESCR
1336The type of mediated device to use.
1337An instance of this type will be created on startup of the VM and
1338will be cleaned up when the VM stops.
1339EODESCR
1340 }
1341};
1342PVE::JSONSchema::register_format('pve-qm-hostpci', $hostpci_fmt);
1343
1344my $hostpcidesc = {
1345 optional => 1,
1346 type => 'string', format => 'pve-qm-hostpci',
1347 description => "Map host PCI devices into guest.",
1348 verbose_description => <<EODESCR,
1349Map host PCI devices into guest.
1350
1351NOTE: This option allows direct access to host hardware. So it is no longer
1352possible to migrate such machines - use with special care.
1353
1354CAUTION: Experimental! User reported problems with this option.
1355EODESCR
1356};
1357PVE::JSONSchema::register_standard_option("pve-qm-hostpci", $hostpcidesc);
1358
1359my $serialdesc = {
1360 optional => 1,
1361 type => 'string',
1362 pattern => '(/dev/.+|socket)',
1363 description => "Create a serial device inside the VM (n is 0 to 3)",
1364 verbose_description => <<EODESCR,
1365Create a serial device inside the VM (n is 0 to 3), and pass through a
1366host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1367host side (use 'qm terminal' to open a terminal connection).
1368
1369NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines - use with special care.
1370
1371CAUTION: Experimental! User reported problems with this option.
1372EODESCR
1373};
1374
1375my $paralleldesc= {
1376 optional => 1,
1377 type => 'string',
1378 pattern => '/dev/parport\d+|/dev/usb/lp\d+',
1379 description => "Map host parallel devices (n is 0 to 2).",
1380 verbose_description => <<EODESCR,
1381Map host parallel devices (n is 0 to 2).
1382
1383NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care.
1384
1385CAUTION: Experimental! User reported problems with this option.
1386EODESCR
1387};
1388
1389for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1390 $confdesc->{"parallel$i"} = $paralleldesc;
1391}
1392
1393for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1394 $confdesc->{"serial$i"} = $serialdesc;
1395}
1396
1397for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
1398 $confdesc->{"hostpci$i"} = $hostpcidesc;
1399}
1400
1401for (my $i = 0; $i < $MAX_IDE_DISKS; $i++) {
1402 $drivedesc_hash->{"ide$i"} = $idedesc;
1403 $confdesc->{"ide$i"} = $idedesc;
1404}
1405
1406for (my $i = 0; $i < $MAX_SATA_DISKS; $i++) {
1407 $drivedesc_hash->{"sata$i"} = $satadesc;
1408 $confdesc->{"sata$i"} = $satadesc;
1409}
1410
1411for (my $i = 0; $i < $MAX_SCSI_DISKS; $i++) {
1412 $drivedesc_hash->{"scsi$i"} = $scsidesc;
1413 $confdesc->{"scsi$i"} = $scsidesc ;
1414}
1415
1416for (my $i = 0; $i < $MAX_VIRTIO_DISKS; $i++) {
1417 $drivedesc_hash->{"virtio$i"} = $virtiodesc;
1418 $confdesc->{"virtio$i"} = $virtiodesc;
1419}
1420
1421$drivedesc_hash->{efidisk0} = $efidisk_desc;
1422$confdesc->{efidisk0} = $efidisk_desc;
1423
1424for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
1425 $confdesc->{"usb$i"} = $usbdesc;
1426}
1427
1428my $unuseddesc = {
1429 optional => 1,
1430 type => 'string', format => 'pve-volume-id',
1431 description => "Reference to unused volumes. This is used internally, and should not be modified manually.",
1432};
1433
1434for (my $i = 0; $i < $MAX_UNUSED_DISKS; $i++) {
1435 $confdesc->{"unused$i"} = $unuseddesc;
1436}
1437
1438my $kvm_api_version = 0;
1439
1440sub kvm_version {
1441 return $kvm_api_version if $kvm_api_version;
1442
1443 open my $fh, '<', '/dev/kvm'
1444 or return undef;
1445
1446 # 0xae00 => KVM_GET_API_VERSION
1447 $kvm_api_version = ioctl($fh, 0xae00, 0);
1448
1449 return $kvm_api_version;
1450}
1451
1452my $kvm_user_version = {};
1453my $kvm_mtime = {};
1454
1455sub kvm_user_version {
1456 my ($binary) = @_;
1457
1458 $binary //= get_command_for_arch(get_host_arch()); # get the native arch by default
1459 my $st = stat($binary);
1460
1461 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1462 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1463 $cachedmtime == $st->mtime;
1464
1465 $kvm_user_version->{$binary} = 'unknown';
1466 $kvm_mtime->{$binary} = $st->mtime;
1467
1468 my $code = sub {
1469 my $line = shift;
1470 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1471 $kvm_user_version->{$binary} = $2;
1472 }
1473 };
1474
1475 eval { run_command([$binary, '--version'], outfunc => $code); };
1476 warn $@ if $@;
1477
1478 return $kvm_user_version->{$binary};
1479
1480}
1481
1482sub kernel_has_vhost_net {
1483 return -c '/dev/vhost-net';
1484}
1485
1486sub valid_drive_names {
1487 # order is important - used to autoselect boot disk
1488 return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))),
1489 (map { "scsi$_" } (0 .. ($MAX_SCSI_DISKS - 1))),
1490 (map { "virtio$_" } (0 .. ($MAX_VIRTIO_DISKS - 1))),
1491 (map { "sata$_" } (0 .. ($MAX_SATA_DISKS - 1))),
1492 'efidisk0');
1493}
1494
1495sub is_valid_drivename {
1496 my $dev = shift;
1497
1498 return defined($drivedesc_hash->{$dev});
1499}
1500
1501sub option_exists {
1502 my $key = shift;
1503 return defined($confdesc->{$key});
1504}
1505
1506my $cdrom_path;
1507sub get_cdrom_path {
1508
1509 return $cdrom_path if $cdrom_path;
1510
1511 return $cdrom_path = "/dev/cdrom" if -l "/dev/cdrom";
1512 return $cdrom_path = "/dev/cdrom1" if -l "/dev/cdrom1";
1513 return $cdrom_path = "/dev/cdrom2" if -l "/dev/cdrom2";
1514}
1515
1516sub get_iso_path {
1517 my ($storecfg, $vmid, $cdrom) = @_;
1518
1519 if ($cdrom eq 'cdrom') {
1520 return get_cdrom_path();
1521 } elsif ($cdrom eq 'none') {
1522 return '';
1523 } elsif ($cdrom =~ m|^/|) {
1524 return $cdrom;
1525 } else {
1526 return PVE::Storage::path($storecfg, $cdrom);
1527 }
1528}
1529
1530# try to convert old style file names to volume IDs
1531sub filename_to_volume_id {
1532 my ($vmid, $file, $media) = @_;
1533
1534 if (!($file eq 'none' || $file eq 'cdrom' ||
1535 $file =~ m|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1536
1537 return undef if $file =~ m|/|;
1538
1539 if ($media && $media eq 'cdrom') {
1540 $file = "local:iso/$file";
1541 } else {
1542 $file = "local:$vmid/$file";
1543 }
1544 }
1545
1546 return $file;
1547}
1548
1549sub verify_media_type {
1550 my ($opt, $vtype, $media) = @_;
1551
1552 return if !$media;
1553
1554 my $etype;
1555 if ($media eq 'disk') {
1556 $etype = 'images';
1557 } elsif ($media eq 'cdrom') {
1558 $etype = 'iso';
1559 } else {
1560 die "internal error";
1561 }
1562
1563 return if ($vtype eq $etype);
1564
1565 raise_param_exc({ $opt => "unexpected media type ($vtype != $etype)" });
1566}
1567
1568sub cleanup_drive_path {
1569 my ($opt, $storecfg, $drive) = @_;
1570
1571 # try to convert filesystem paths to volume IDs
1572
1573 if (($drive->{file} !~ m/^(cdrom|none)$/) &&
1574 ($drive->{file} !~ m|^/dev/.+|) &&
1575 ($drive->{file} !~ m/^([^:]+):(.+)$/) &&
1576 ($drive->{file} !~ m/^\d+$/)) {
1577 my ($vtype, $volid) = PVE::Storage::path_to_volume_id($storecfg, $drive->{file});
1578 raise_param_exc({ $opt => "unable to associate path '$drive->{file}' to any storage"}) if !$vtype;
1579 $drive->{media} = 'cdrom' if !$drive->{media} && $vtype eq 'iso';
1580 verify_media_type($opt, $vtype, $drive->{media});
1581 $drive->{file} = $volid;
1582 }
1583
1584 $drive->{media} = 'cdrom' if !$drive->{media} && $drive->{file} =~ m/^(cdrom|none)$/;
1585}
1586
1587sub parse_hotplug_features {
1588 my ($data) = @_;
1589
1590 my $res = {};
1591
1592 return $res if $data eq '0';
1593
1594 $data = $confdesc->{hotplug}->{default} if $data eq '1';
1595
1596 foreach my $feature (PVE::Tools::split_list($data)) {
1597 if ($feature =~ m/^(network|disk|cpu|memory|usb)$/) {
1598 $res->{$1} = 1;
1599 } else {
1600 die "invalid hotplug feature '$feature'\n";
1601 }
1602 }
1603 return $res;
1604}
1605
1606PVE::JSONSchema::register_format('pve-hotplug-features', \&pve_verify_hotplug_features);
1607sub pve_verify_hotplug_features {
1608 my ($value, $noerr) = @_;
1609
1610 return $value if parse_hotplug_features($value);
1611
1612 return undef if $noerr;
1613
1614 die "unable to parse hotplug option\n";
1615}
1616
1617# ideX = [volume=]volume-id[,media=d][,cyls=c,heads=h,secs=s[,trans=t]]
1618# [,snapshot=on|off][,cache=on|off][,format=f][,backup=yes|no]
1619# [,rerror=ignore|report|stop][,werror=enospc|ignore|report|stop]
1620# [,aio=native|threads][,discard=ignore|on][,detect_zeroes=on|off]
1621# [,iothread=on][,serial=serial][,model=model]
1622
1623sub parse_drive {
1624 my ($key, $data) = @_;
1625
1626 my ($interface, $index);
1627
1628 if ($key =~ m/^([^\d]+)(\d+)$/) {
1629 $interface = $1;
1630 $index = $2;
1631 } else {
1632 return undef;
1633 }
1634
1635 my $desc = $key =~ /^unused\d+$/ ? $alldrive_fmt
1636 : $drivedesc_hash->{$key}->{format};
1637 if (!$desc) {
1638 warn "invalid drive key: $key\n";
1639 return undef;
1640 }
1641 my $res = eval { PVE::JSONSchema::parse_property_string($desc, $data) };
1642 return undef if !$res;
1643 $res->{interface} = $interface;
1644 $res->{index} = $index;
1645
1646 my $error = 0;
1647 foreach my $opt (qw(bps bps_rd bps_wr)) {
1648 if (my $bps = defined(delete $res->{$opt})) {
1649 if (defined($res->{"m$opt"})) {
1650 warn "both $opt and m$opt specified\n";
1651 ++$error;
1652 next;
1653 }
1654 $res->{"m$opt"} = sprintf("%.3f", $bps / (1024*1024.0));
1655 }
1656 }
1657
1658 # can't use the schema's 'requires' because of the mbps* => bps* "transforming aliases"
1659 for my $requirement (
1660 [mbps_max => 'mbps'],
1661 [mbps_rd_max => 'mbps_rd'],
1662 [mbps_wr_max => 'mbps_wr'],
1663 [miops_max => 'miops'],
1664 [miops_rd_max => 'miops_rd'],
1665 [miops_wr_max => 'miops_wr'],
1666 [bps_max_length => 'mbps_max'],
1667 [bps_rd_max_length => 'mbps_rd_max'],
1668 [bps_wr_max_length => 'mbps_wr_max'],
1669 [iops_max_length => 'iops_max'],
1670 [iops_rd_max_length => 'iops_rd_max'],
1671 [iops_wr_max_length => 'iops_wr_max']) {
1672 my ($option, $requires) = @$requirement;
1673 if ($res->{$option} && !$res->{$requires}) {
1674 warn "$option requires $requires\n";
1675 ++$error;
1676 }
1677 }
1678
1679 return undef if $error;
1680
1681 return undef if $res->{mbps_rd} && $res->{mbps};
1682 return undef if $res->{mbps_wr} && $res->{mbps};
1683 return undef if $res->{iops_rd} && $res->{iops};
1684 return undef if $res->{iops_wr} && $res->{iops};
1685
1686 if ($res->{media} && ($res->{media} eq 'cdrom')) {
1687 return undef if $res->{snapshot} || $res->{trans} || $res->{format};
1688 return undef if $res->{heads} || $res->{secs} || $res->{cyls};
1689 return undef if $res->{interface} eq 'virtio';
1690 }
1691
1692 if (my $size = $res->{size}) {
1693 return undef if !defined($res->{size} = PVE::JSONSchema::parse_size($size));
1694 }
1695
1696 return $res;
1697}
1698
1699sub print_drive {
1700 my ($drive) = @_;
1701 my $skip = [ 'index', 'interface' ];
1702 return PVE::JSONSchema::print_property_string($drive, $alldrive_fmt, $skip);
1703}
1704
1705sub scsi_inquiry {
1706 my($fh, $noerr) = @_;
1707
1708 my $SG_IO = 0x2285;
1709 my $SG_GET_VERSION_NUM = 0x2282;
1710
1711 my $versionbuf = "\x00" x 8;
1712 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1713 if (!$ret) {
1714 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
1715 return undef;
1716 }
1717 my $version = unpack("I", $versionbuf);
1718 if ($version < 30000) {
1719 die "scsi generic interface too old\n" if !$noerr;
1720 return undef;
1721 }
1722
1723 my $buf = "\x00" x 36;
1724 my $sensebuf = "\x00" x 8;
1725 my $cmd = pack("C x3 C x1", 0x12, 36);
1726
1727 # see /usr/include/scsi/sg.h
1728 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";
1729
1730 my $packet = pack($sg_io_hdr_t, ord('S'), -3, length($cmd),
1731 length($sensebuf), 0, length($buf), $buf,
1732 $cmd, $sensebuf, 6000);
1733
1734 $ret = ioctl($fh, $SG_IO, $packet);
1735 if (!$ret) {
1736 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
1737 return undef;
1738 }
1739
1740 my @res = unpack($sg_io_hdr_t, $packet);
1741 if ($res[17] || $res[18]) {
1742 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
1743 return undef;
1744 }
1745
1746 my $res = {};
1747 (my $byte0, my $byte1, $res->{vendor},
1748 $res->{product}, $res->{revision}) = unpack("C C x6 A8 A16 A4", $buf);
1749
1750 $res->{removable} = $byte1 & 128 ? 1 : 0;
1751 $res->{type} = $byte0 & 31;
1752
1753 return $res;
1754}
1755
1756sub path_is_scsi {
1757 my ($path) = @_;
1758
1759 my $fh = IO::File->new("+<$path") || return undef;
1760 my $res = scsi_inquiry($fh, 1);
1761 close($fh);
1762
1763 return $res;
1764}
1765
1766sub print_tabletdevice_full {
1767 my ($conf, $arch) = @_;
1768
1769 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
1770
1771 # we use uhci for old VMs because tablet driver was buggy in older qemu
1772 my $usbbus;
1773 if (PVE::QemuServer::Machine::machine_type_is_q35($conf) || $arch eq 'aarch64') {
1774 $usbbus = 'ehci';
1775 } else {
1776 $usbbus = 'uhci';
1777 }
1778
1779 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1780}
1781
1782sub print_keyboarddevice_full {
1783 my ($conf, $arch, $machine) = @_;
1784
1785 return undef if $arch ne 'aarch64';
1786
1787 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1788}
1789
1790sub print_drivedevice_full {
1791 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1792
1793 my $device = '';
1794 my $maxdev = 0;
1795
1796 if ($drive->{interface} eq 'virtio') {
1797 my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}", $bridges, $arch, $machine_type);
1798 $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr";
1799 $device .= ",iothread=iothread-$drive->{interface}$drive->{index}" if $drive->{iothread};
1800 } elsif ($drive->{interface} eq 'scsi') {
1801
1802 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive);
1803 my $unit = $drive->{index} % $maxdev;
1804 my $devicetype = 'hd';
1805 my $path = '';
1806 if (drive_is_cdrom($drive)) {
1807 $devicetype = 'cd';
1808 } else {
1809 if ($drive->{file} =~ m|^/|) {
1810 $path = $drive->{file};
1811 if (my $info = path_is_scsi($path)) {
1812 if ($info->{type} == 0 && $drive->{scsiblock}) {
1813 $devicetype = 'block';
1814 } elsif ($info->{type} == 1) { # tape
1815 $devicetype = 'generic';
1816 }
1817 }
1818 } else {
1819 $path = PVE::Storage::path($storecfg, $drive->{file});
1820 }
1821
1822 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1823 my $version = PVE::QemuServer::Machine::extract_version($machine_type, kvm_user_version());
1824 if ($path =~ m/^iscsi\:\/\// &&
1825 !min_version($version, 4, 1)) {
1826 $devicetype = 'generic';
1827 }
1828 }
1829
1830 if (!$conf->{scsihw} || ($conf->{scsihw} =~ m/^lsi/)){
1831 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
1832 } else {
1833 $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}";
1834 }
1835
1836 if ($drive->{ssd} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1837 $device .= ",rotation_rate=1";
1838 }
1839 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn};
1840
1841 } elsif ($drive->{interface} eq 'ide' || $drive->{interface} eq 'sata') {
1842 my $maxdev = ($drive->{interface} eq 'sata') ? $MAX_SATA_DISKS : 2;
1843 my $controller = int($drive->{index} / $maxdev);
1844 my $unit = $drive->{index} % $maxdev;
1845 my $devicetype = ($drive->{media} && $drive->{media} eq 'cdrom') ? "cd" : "hd";
1846
1847 $device = "ide-$devicetype";
1848 if ($drive->{interface} eq 'ide') {
1849 $device .= ",bus=ide.$controller,unit=$unit";
1850 } else {
1851 $device .= ",bus=ahci$controller.$unit";
1852 }
1853 $device .= ",drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}";
1854
1855 if ($devicetype eq 'hd') {
1856 if (my $model = $drive->{model}) {
1857 $model = URI::Escape::uri_unescape($model);
1858 $device .= ",model=$model";
1859 }
1860 if ($drive->{ssd}) {
1861 $device .= ",rotation_rate=1";
1862 }
1863 }
1864 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn};
1865 } elsif ($drive->{interface} eq 'usb') {
1866 die "implement me";
1867 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1868 } else {
1869 die "unsupported interface type";
1870 }
1871
1872 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex};
1873
1874 if (my $serial = $drive->{serial}) {
1875 $serial = URI::Escape::uri_unescape($serial);
1876 $device .= ",serial=$serial";
1877 }
1878
1879
1880 return $device;
1881}
1882
1883sub get_initiator_name {
1884 my $initiator;
1885
1886 my $fh = IO::File->new('/etc/iscsi/initiatorname.iscsi') || return undef;
1887 while (defined(my $line = <$fh>)) {
1888 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1889 $initiator = $1;
1890 last;
1891 }
1892 $fh->close();
1893
1894 return $initiator;
1895}
1896
1897sub print_drive_full {
1898 my ($storecfg, $vmid, $drive) = @_;
1899
1900 my $path;
1901 my $volid = $drive->{file};
1902 my $format;
1903
1904 if (drive_is_cdrom($drive)) {
1905 $path = get_iso_path($storecfg, $vmid, $volid);
1906 } else {
1907 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
1908 if ($storeid) {
1909 $path = PVE::Storage::path($storecfg, $volid);
1910 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
1911 $format = qemu_img_format($scfg, $volname);
1912 } else {
1913 $path = $volid;
1914 $format = "raw";
1915 }
1916 }
1917
1918 my $opts = '';
1919 my @qemu_drive_options = qw(heads secs cyls trans media format cache rerror werror aio discard);
1920 foreach my $o (@qemu_drive_options) {
1921 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1922 }
1923
1924 # snapshot only accepts on|off
1925 if (defined($drive->{snapshot})) {
1926 my $v = $drive->{snapshot} ? 'on' : 'off';
1927 $opts .= ",snapshot=$v";
1928 }
1929
1930 foreach my $type (['', '-total'], [_rd => '-read'], [_wr => '-write']) {
1931 my ($dir, $qmpname) = @$type;
1932 if (my $v = $drive->{"mbps$dir"}) {
1933 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1934 }
1935 if (my $v = $drive->{"mbps${dir}_max"}) {
1936 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1937 }
1938 if (my $v = $drive->{"bps${dir}_max_length"}) {
1939 $opts .= ",throttling.bps$qmpname-max-length=$v";
1940 }
1941 if (my $v = $drive->{"iops${dir}"}) {
1942 $opts .= ",throttling.iops$qmpname=$v";
1943 }
1944 if (my $v = $drive->{"iops${dir}_max"}) {
1945 $opts .= ",throttling.iops$qmpname-max=$v";
1946 }
1947 if (my $v = $drive->{"iops${dir}_max_length"}) {
1948 $opts .= ",throttling.iops$qmpname-max-length=$v";
1949 }
1950 }
1951
1952 $opts .= ",format=$format" if $format && !$drive->{format};
1953
1954 my $cache_direct = 0;
1955
1956 if (my $cache = $drive->{cache}) {
1957 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1958 } elsif (!drive_is_cdrom($drive)) {
1959 $opts .= ",cache=none";
1960 $cache_direct = 1;
1961 }
1962
1963 # aio native works only with O_DIRECT
1964 if (!$drive->{aio}) {
1965 if($cache_direct) {
1966 $opts .= ",aio=native";
1967 } else {
1968 $opts .= ",aio=threads";
1969 }
1970 }
1971
1972 if (!drive_is_cdrom($drive)) {
1973 my $detectzeroes;
1974 if (defined($drive->{detect_zeroes}) && !$drive->{detect_zeroes}) {
1975 $detectzeroes = 'off';
1976 } elsif ($drive->{discard}) {
1977 $detectzeroes = $drive->{discard} eq 'on' ? 'unmap' : 'on';
1978 } else {
1979 # This used to be our default with discard not being specified:
1980 $detectzeroes = 'on';
1981 }
1982 $opts .= ",detect-zeroes=$detectzeroes" if $detectzeroes;
1983 }
1984
1985 my $pathinfo = $path ? "file=$path," : '';
1986
1987 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1988}
1989
1990sub print_netdevice_full {
1991 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type) = @_;
1992
1993 my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
1994
1995 my $device = $net->{model};
1996 if ($net->{model} eq 'virtio') {
1997 $device = 'virtio-net-pci';
1998 };
1999
2000 my $pciaddr = print_pci_addr("$netid", $bridges, $arch, $machine_type);
2001 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
2002 if ($net->{queues} && $net->{queues} > 1 && $net->{model} eq 'virtio'){
2003 #Consider we have N queues, the number of vectors needed is 2*N + 2 (plus one config interrupt and control vq)
2004 my $vectors = $net->{queues} * 2 + 2;
2005 $tmpstr .= ",vectors=$vectors,mq=on";
2006 }
2007 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex} ;
2008
2009 if ($use_old_bios_files) {
2010 my $romfile;
2011 if ($device eq 'virtio-net-pci') {
2012 $romfile = 'pxe-virtio.rom';
2013 } elsif ($device eq 'e1000') {
2014 $romfile = 'pxe-e1000.rom';
2015 } elsif ($device eq 'ne2k') {
2016 $romfile = 'pxe-ne2k_pci.rom';
2017 } elsif ($device eq 'pcnet') {
2018 $romfile = 'pxe-pcnet.rom';
2019 } elsif ($device eq 'rtl8139') {
2020 $romfile = 'pxe-rtl8139.rom';
2021 }
2022 $tmpstr .= ",romfile=$romfile" if $romfile;
2023 }
2024
2025 return $tmpstr;
2026}
2027
2028sub print_netdev_full {
2029 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
2030
2031 my $i = '';
2032 if ($netid =~ m/^net(\d+)$/) {
2033 $i = int($1);
2034 }
2035
2036 die "got strange net id '$i'\n" if $i >= ${MAX_NETS};
2037
2038 my $ifname = "tap${vmid}i$i";
2039
2040 # kvm uses TUNSETIFF ioctl, and that limits ifname length
2041 die "interface name '$ifname' is too long (max 15 character)\n"
2042 if length($ifname) >= 16;
2043
2044 my $vhostparam = '';
2045 if (is_native($arch)) {
2046 $vhostparam = ',vhost=on' if kernel_has_vhost_net() && $net->{model} eq 'virtio';
2047 }
2048
2049 my $vmname = $conf->{name} || "vm$vmid";
2050
2051 my $netdev = "";
2052 my $script = $hotplug ? "pve-bridge-hotplug" : "pve-bridge";
2053
2054 if ($net->{bridge}) {
2055 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script,downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
2056 } else {
2057 $netdev = "type=user,id=$netid,hostname=$vmname";
2058 }
2059
2060 $netdev .= ",queues=$net->{queues}" if ($net->{queues} && $net->{model} eq 'virtio');
2061
2062 return $netdev;
2063}
2064
2065my $vga_map = {
2066 'cirrus' => 'cirrus-vga',
2067 'std' => 'VGA',
2068 'vmware' => 'vmware-svga',
2069 'virtio' => 'virtio-vga',
2070};
2071
2072sub print_vga_device {
2073 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
2074
2075 my $type = $vga_map->{$vga->{type}};
2076 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
2077 $type = 'virtio-gpu';
2078 }
2079 my $vgamem_mb = $vga->{memory};
2080
2081 my $max_outputs = '';
2082 if ($qxlnum) {
2083 $type = $id ? 'qxl' : 'qxl-vga';
2084
2085 if (!$conf->{ostype} || $conf->{ostype} =~ m/^(?:l\d\d)|(?:other)$/) {
2086 # set max outputs so linux can have up to 4 qxl displays with one device
2087 if (min_version($machine_version, 4, 1)) {
2088 $max_outputs = ",max_outputs=4";
2089 }
2090 }
2091 }
2092
2093 die "no devicetype for $vga->{type}\n" if !$type;
2094
2095 my $memory = "";
2096 if ($vgamem_mb) {
2097 if ($vga->{type} eq 'virtio') {
2098 my $bytes = PVE::Tools::convert_size($vgamem_mb, "mb" => "b");
2099 $memory = ",max_hostmem=$bytes";
2100 } elsif ($qxlnum) {
2101 # from https://www.spice-space.org/multiple-monitors.html
2102 $memory = ",vgamem_mb=$vga->{memory}";
2103 my $ram = $vgamem_mb * 4;
2104 my $vram = $vgamem_mb * 2;
2105 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
2106 } else {
2107 $memory = ",vgamem_mb=$vga->{memory}";
2108 }
2109 } elsif ($qxlnum && $id) {
2110 $memory = ",ram_size=67108864,vram_size=33554432";
2111 }
2112
2113 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
2114 my $vgaid = "vga" . ($id // '');
2115 my $pciaddr;
2116
2117 if ($q35 && $vgaid eq 'vga') {
2118 # the first display uses pcie.0 bus on q35 machines
2119 $pciaddr = print_pcie_addr($vgaid, $bridges, $arch, $machine);
2120 } else {
2121 $pciaddr = print_pci_addr($vgaid, $bridges, $arch, $machine);
2122 }
2123
2124 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}";
2125}
2126
2127sub drive_is_cloudinit {
2128 my ($drive) = @_;
2129 return $drive->{file} =~ m@[:/]vm-\d+-cloudinit(?:\.$QEMU_FORMAT_RE)?$@;
2130}
2131
2132sub drive_is_cdrom {
2133 my ($drive, $exclude_cloudinit) = @_;
2134
2135 return 0 if $exclude_cloudinit && drive_is_cloudinit($drive);
2136
2137 return $drive && $drive->{media} && ($drive->{media} eq 'cdrom');
2138
2139}
2140
2141sub parse_number_sets {
2142 my ($set) = @_;
2143 my $res = [];
2144 foreach my $part (split(/;/, $set)) {
2145 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
2146 die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1;
2147 push @$res, [ $1, $2 ];
2148 } else {
2149 die "invalid range: $part\n";
2150 }
2151 }
2152 return $res;
2153}
2154
2155sub parse_numa {
2156 my ($data) = @_;
2157
2158 my $res = PVE::JSONSchema::parse_property_string($numa_fmt, $data);
2159 $res->{cpus} = parse_number_sets($res->{cpus}) if defined($res->{cpus});
2160 $res->{hostnodes} = parse_number_sets($res->{hostnodes}) if defined($res->{hostnodes});
2161 return $res;
2162}
2163
2164sub parse_hostpci {
2165 my ($value) = @_;
2166
2167 return undef if !$value;
2168
2169 my $res = PVE::JSONSchema::parse_property_string($hostpci_fmt, $value);
2170
2171 my @idlist = split(/;/, $res->{host});
2172 delete $res->{host};
2173 foreach my $id (@idlist) {
2174 my $devs = PVE::SysFSTools::lspci($id);
2175 die "no PCI device found for '$id'\n" if !scalar(@$devs);
2176 push @{$res->{pciid}}, @$devs;
2177 }
2178 return $res;
2179}
2180
2181# netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
2182sub parse_net {
2183 my ($data) = @_;
2184
2185 my $res = eval { PVE::JSONSchema::parse_property_string($net_fmt, $data) };
2186 if ($@) {
2187 warn $@;
2188 return undef;
2189 }
2190 if (!defined($res->{macaddr})) {
2191 my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
2192 $res->{macaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
2193 }
2194 return $res;
2195}
2196
2197# ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
2198sub parse_ipconfig {
2199 my ($data) = @_;
2200
2201 my $res = eval { PVE::JSONSchema::parse_property_string($ipconfig_fmt, $data) };
2202 if ($@) {
2203 warn $@;
2204 return undef;
2205 }
2206
2207 if ($res->{gw} && !$res->{ip}) {
2208 warn 'gateway specified without specifying an IP address';
2209 return undef;
2210 }
2211 if ($res->{gw6} && !$res->{ip6}) {
2212 warn 'IPv6 gateway specified without specifying an IPv6 address';
2213 return undef;
2214 }
2215 if ($res->{gw} && $res->{ip} eq 'dhcp') {
2216 warn 'gateway specified together with DHCP';
2217 return undef;
2218 }
2219 if ($res->{gw6} && $res->{ip6} !~ /^$IPV6RE/) {
2220 # gw6 + auto/dhcp
2221 warn "IPv6 gateway specified together with $res->{ip6} address";
2222 return undef;
2223 }
2224
2225 if (!$res->{ip} && !$res->{ip6}) {
2226 return { ip => 'dhcp', ip6 => 'dhcp' };
2227 }
2228
2229 return $res;
2230}
2231
2232sub print_net {
2233 my $net = shift;
2234
2235 return PVE::JSONSchema::print_property_string($net, $net_fmt);
2236}
2237
2238sub add_random_macs {
2239 my ($settings) = @_;
2240
2241 foreach my $opt (keys %$settings) {
2242 next if $opt !~ m/^net(\d+)$/;
2243 my $net = parse_net($settings->{$opt});
2244 next if !$net;
2245 $settings->{$opt} = print_net($net);
2246 }
2247}
2248
2249sub vm_is_volid_owner {
2250 my ($storecfg, $vmid, $volid) = @_;
2251
2252 if ($volid !~ m|^/|) {
2253 my ($path, $owner);
2254 eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); };
2255 if ($owner && ($owner == $vmid)) {
2256 return 1;
2257 }
2258 }
2259
2260 return undef;
2261}
2262
2263sub vmconfig_register_unused_drive {
2264 my ($storecfg, $vmid, $conf, $drive) = @_;
2265
2266 if (drive_is_cloudinit($drive)) {
2267 eval { PVE::Storage::vdisk_free($storecfg, $drive->{file}) };
2268 warn $@ if $@;
2269 } elsif (!drive_is_cdrom($drive)) {
2270 my $volid = $drive->{file};
2271 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
2272 PVE::QemuConfig->add_unused_volume($conf, $volid, $vmid);
2273 }
2274 }
2275}
2276
2277# smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
2278my $smbios1_fmt = {
2279 uuid => {
2280 type => 'string',
2281 pattern => '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
2282 format_description => 'UUID',
2283 description => "Set SMBIOS1 UUID.",
2284 optional => 1,
2285 },
2286 version => {
2287 type => 'string',
2288 pattern => '[A-Za-z0-9+\/]+={0,2}',
2289 format_description => 'Base64 encoded string',
2290 description => "Set SMBIOS1 version.",
2291 optional => 1,
2292 },
2293 serial => {
2294 type => 'string',
2295 pattern => '[A-Za-z0-9+\/]+={0,2}',
2296 format_description => 'Base64 encoded string',
2297 description => "Set SMBIOS1 serial number.",
2298 optional => 1,
2299 },
2300 manufacturer => {
2301 type => 'string',
2302 pattern => '[A-Za-z0-9+\/]+={0,2}',
2303 format_description => 'Base64 encoded string',
2304 description => "Set SMBIOS1 manufacturer.",
2305 optional => 1,
2306 },
2307 product => {
2308 type => 'string',
2309 pattern => '[A-Za-z0-9+\/]+={0,2}',
2310 format_description => 'Base64 encoded string',
2311 description => "Set SMBIOS1 product ID.",
2312 optional => 1,
2313 },
2314 sku => {
2315 type => 'string',
2316 pattern => '[A-Za-z0-9+\/]+={0,2}',
2317 format_description => 'Base64 encoded string',
2318 description => "Set SMBIOS1 SKU string.",
2319 optional => 1,
2320 },
2321 family => {
2322 type => 'string',
2323 pattern => '[A-Za-z0-9+\/]+={0,2}',
2324 format_description => 'Base64 encoded string',
2325 description => "Set SMBIOS1 family string.",
2326 optional => 1,
2327 },
2328 base64 => {
2329 type => 'boolean',
2330 description => 'Flag to indicate that the SMBIOS values are base64 encoded',
2331 optional => 1,
2332 },
2333};
2334
2335sub parse_smbios1 {
2336 my ($data) = @_;
2337
2338 my $res = eval { PVE::JSONSchema::parse_property_string($smbios1_fmt, $data) };
2339 warn $@ if $@;
2340 return $res;
2341}
2342
2343sub print_smbios1 {
2344 my ($smbios1) = @_;
2345 return PVE::JSONSchema::print_property_string($smbios1, $smbios1_fmt);
2346}
2347
2348PVE::JSONSchema::register_format('pve-qm-smbios1', $smbios1_fmt);
2349
2350PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk);
2351sub verify_bootdisk {
2352 my ($value, $noerr) = @_;
2353
2354 return $value if is_valid_drivename($value);
2355
2356 return undef if $noerr;
2357
2358 die "invalid boot disk '$value'\n";
2359}
2360
2361sub parse_watchdog {
2362 my ($value) = @_;
2363
2364 return undef if !$value;
2365
2366 my $res = eval { PVE::JSONSchema::parse_property_string($watchdog_fmt, $value) };
2367 warn $@ if $@;
2368 return $res;
2369}
2370
2371sub parse_guest_agent {
2372 my ($value) = @_;
2373
2374 return {} if !defined($value->{agent});
2375
2376 my $res = eval { PVE::JSONSchema::parse_property_string($agent_fmt, $value->{agent}) };
2377 warn $@ if $@;
2378
2379 # if the agent is disabled ignore the other potentially set properties
2380 return {} if !$res->{enabled};
2381 return $res;
2382}
2383
2384sub parse_vga {
2385 my ($value) = @_;
2386
2387 return {} if !$value;
2388 my $res = eval { PVE::JSONSchema::parse_property_string($vga_fmt, $value) };
2389 warn $@ if $@;
2390 return $res;
2391}
2392
2393sub parse_rng {
2394 my ($value) = @_;
2395
2396 return undef if !$value;
2397
2398 my $res = eval { PVE::JSONSchema::parse_property_string($rng_fmt, $value) };
2399 warn $@ if $@;
2400 return $res;
2401}
2402
2403PVE::JSONSchema::register_format('pve-qm-usb-device', \&verify_usb_device);
2404sub verify_usb_device {
2405 my ($value, $noerr) = @_;
2406
2407 return $value if parse_usb_device($value);
2408
2409 return undef if $noerr;
2410
2411 die "unable to parse usb device\n";
2412}
2413
2414# add JSON properties for create and set function
2415sub json_config_properties {
2416 my $prop = shift;
2417
2418 foreach my $opt (keys %$confdesc) {
2419 next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'vmstate' || $opt eq 'runningmachine';
2420 $prop->{$opt} = $confdesc->{$opt};
2421 }
2422
2423 return $prop;
2424}
2425
2426# return copy of $confdesc_cloudinit to generate documentation
2427sub cloudinit_config_properties {
2428
2429 return dclone($confdesc_cloudinit);
2430}
2431
2432sub check_type {
2433 my ($key, $value) = @_;
2434
2435 die "unknown setting '$key'\n" if !$confdesc->{$key};
2436
2437 my $type = $confdesc->{$key}->{type};
2438
2439 if (!defined($value)) {
2440 die "got undefined value\n";
2441 }
2442
2443 if ($value =~ m/[\n\r]/) {
2444 die "property contains a line feed\n";
2445 }
2446
2447 if ($type eq 'boolean') {
2448 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2449 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2450 die "type check ('boolean') failed - got '$value'\n";
2451 } elsif ($type eq 'integer') {
2452 return int($1) if $value =~ m/^(\d+)$/;
2453 die "type check ('integer') failed - got '$value'\n";
2454 } elsif ($type eq 'number') {
2455 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2456 die "type check ('number') failed - got '$value'\n";
2457 } elsif ($type eq 'string') {
2458 if (my $fmt = $confdesc->{$key}->{format}) {
2459 PVE::JSONSchema::check_format($fmt, $value);
2460 return $value;
2461 }
2462 $value =~ s/^\"(.*)\"$/$1/;
2463 return $value;
2464 } else {
2465 die "internal error"
2466 }
2467}
2468
2469sub destroy_vm {
2470 my ($storecfg, $vmid, $skiplock, $replacement_conf) = @_;
2471
2472 my $conf = PVE::QemuConfig->load_config($vmid);
2473
2474 PVE::QemuConfig->check_lock($conf) if !$skiplock;
2475
2476 if ($conf->{template}) {
2477 # check if any base image is still used by a linked clone
2478 foreach_drive($conf, sub {
2479 my ($ds, $drive) = @_;
2480 return if drive_is_cdrom($drive);
2481
2482 my $volid = $drive->{file};
2483 return if !$volid || $volid =~ m|^/|;
2484
2485 die "base volume '$volid' is still in use by linked cloned\n"
2486 if PVE::Storage::volume_is_base_and_used($storecfg, $volid);
2487
2488 });
2489 }
2490
2491 # only remove disks owned by this VM
2492 foreach_drive($conf, sub {
2493 my ($ds, $drive) = @_;
2494 return if drive_is_cdrom($drive, 1);
2495
2496 my $volid = $drive->{file};
2497 return if !$volid || $volid =~ m|^/|;
2498
2499 my ($path, $owner) = PVE::Storage::path($storecfg, $volid);
2500 return if !$path || !$owner || ($owner != $vmid);
2501
2502 eval { PVE::Storage::vdisk_free($storecfg, $volid) };
2503 warn "Could not remove disk '$volid', check manually: $@" if $@;
2504 });
2505
2506 # also remove unused disk
2507 my $vmdisks = PVE::Storage::vdisk_list($storecfg, undef, $vmid);
2508 PVE::Storage::foreach_volid($vmdisks, sub {
2509 my ($volid, $sid, $volname, $d) = @_;
2510 eval { PVE::Storage::vdisk_free($storecfg, $volid) };
2511 warn $@ if $@;
2512 });
2513
2514 if (defined $replacement_conf) {
2515 PVE::QemuConfig->write_config($vmid, $replacement_conf);
2516 } else {
2517 PVE::QemuConfig->destroy_config($vmid);
2518 }
2519}
2520
2521sub parse_vm_config {
2522 my ($filename, $raw) = @_;
2523
2524 return undef if !defined($raw);
2525
2526 my $res = {
2527 digest => Digest::SHA::sha1_hex($raw),
2528 snapshots => {},
2529 pending => {},
2530 };
2531
2532 $filename =~ m|/qemu-server/(\d+)\.conf$|
2533 || die "got strange filename '$filename'";
2534
2535 my $vmid = $1;
2536
2537 my $conf = $res;
2538 my $descr;
2539 my $section = '';
2540
2541 my @lines = split(/\n/, $raw);
2542 foreach my $line (@lines) {
2543 next if $line =~ m/^\s*$/;
2544
2545 if ($line =~ m/^\[PENDING\]\s*$/i) {
2546 $section = 'pending';
2547 if (defined($descr)) {
2548 $descr =~ s/\s+$//;
2549 $conf->{description} = $descr;
2550 }
2551 $descr = undef;
2552 $conf = $res->{$section} = {};
2553 next;
2554
2555 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2556 $section = $1;
2557 if (defined($descr)) {
2558 $descr =~ s/\s+$//;
2559 $conf->{description} = $descr;
2560 }
2561 $descr = undef;
2562 $conf = $res->{snapshots}->{$section} = {};
2563 next;
2564 }
2565
2566 if ($line =~ m/^\#(.*)\s*$/) {
2567 $descr = '' if !defined($descr);
2568 $descr .= PVE::Tools::decode_text($1) . "\n";
2569 next;
2570 }
2571
2572 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2573 $descr = '' if !defined($descr);
2574 $descr .= PVE::Tools::decode_text($2);
2575 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2576 $conf->{snapstate} = $1;
2577 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2578 my $key = $1;
2579 my $value = $2;
2580 $conf->{$key} = $value;
2581 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2582 my $value = $1;
2583 if ($section eq 'pending') {
2584 $conf->{delete} = $value; # we parse this later
2585 } else {
2586 warn "vm $vmid - propertry 'delete' is only allowed in [PENDING]\n";
2587 }
2588 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2589 my $key = $1;
2590 my $value = $2;
2591 eval { $value = check_type($key, $value); };
2592 if ($@) {
2593 warn "vm $vmid - unable to parse value of '$key' - $@";
2594 } else {
2595 $key = 'ide2' if $key eq 'cdrom';
2596 my $fmt = $confdesc->{$key}->{format};
2597 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2598 my $v = parse_drive($key, $value);
2599 if (my $volid = filename_to_volume_id($vmid, $v->{file}, $v->{media})) {
2600 $v->{file} = $volid;
2601 $value = print_drive($v);
2602 } else {
2603 warn "vm $vmid - unable to parse value of '$key'\n";
2604 next;
2605 }
2606 }
2607
2608 $conf->{$key} = $value;
2609 }
2610 }
2611 }
2612
2613 if (defined($descr)) {
2614 $descr =~ s/\s+$//;
2615 $conf->{description} = $descr;
2616 }
2617 delete $res->{snapstate}; # just to be sure
2618
2619 return $res;
2620}
2621
2622sub write_vm_config {
2623 my ($filename, $conf) = @_;
2624
2625 delete $conf->{snapstate}; # just to be sure
2626
2627 if ($conf->{cdrom}) {
2628 die "option ide2 conflicts with cdrom\n" if $conf->{ide2};
2629 $conf->{ide2} = $conf->{cdrom};
2630 delete $conf->{cdrom};
2631 }
2632
2633 # we do not use 'smp' any longer
2634 if ($conf->{sockets}) {
2635 delete $conf->{smp};
2636 } elsif ($conf->{smp}) {
2637 $conf->{sockets} = $conf->{smp};
2638 delete $conf->{cores};
2639 delete $conf->{smp};
2640 }
2641
2642 my $used_volids = {};
2643
2644 my $cleanup_config = sub {
2645 my ($cref, $pending, $snapname) = @_;
2646
2647 foreach my $key (keys %$cref) {
2648 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2649 $key eq 'snapstate' || $key eq 'pending';
2650 my $value = $cref->{$key};
2651 if ($key eq 'delete') {
2652 die "propertry 'delete' is only allowed in [PENDING]\n"
2653 if !$pending;
2654 # fixme: check syntax?
2655 next;
2656 }
2657 eval { $value = check_type($key, $value); };
2658 die "unable to parse value of '$key' - $@" if $@;
2659
2660 $cref->{$key} = $value;
2661
2662 if (!$snapname && is_valid_drivename($key)) {
2663 my $drive = parse_drive($key, $value);
2664 $used_volids->{$drive->{file}} = 1 if $drive && $drive->{file};
2665 }
2666 }
2667 };
2668
2669 &$cleanup_config($conf);
2670
2671 &$cleanup_config($conf->{pending}, 1);
2672
2673 foreach my $snapname (keys %{$conf->{snapshots}}) {
2674 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2675 &$cleanup_config($conf->{snapshots}->{$snapname}, undef, $snapname);
2676 }
2677
2678 # remove 'unusedX' settings if we re-add a volume
2679 foreach my $key (keys %$conf) {
2680 my $value = $conf->{$key};
2681 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2682 delete $conf->{$key};
2683 }
2684 }
2685
2686 my $generate_raw_config = sub {
2687 my ($conf, $pending) = @_;
2688
2689 my $raw = '';
2690
2691 # add description as comment to top of file
2692 if (defined(my $descr = $conf->{description})) {
2693 if ($descr) {
2694 foreach my $cl (split(/\n/, $descr)) {
2695 $raw .= '#' . PVE::Tools::encode_text($cl) . "\n";
2696 }
2697 } else {
2698 $raw .= "#\n" if $pending;
2699 }
2700 }
2701
2702 foreach my $key (sort keys %$conf) {
2703 next if $key eq 'digest' || $key eq 'description' || $key eq 'pending' || $key eq 'snapshots';
2704 $raw .= "$key: $conf->{$key}\n";
2705 }
2706 return $raw;
2707 };
2708
2709 my $raw = &$generate_raw_config($conf);
2710
2711 if (scalar(keys %{$conf->{pending}})){
2712 $raw .= "\n[PENDING]\n";
2713 $raw .= &$generate_raw_config($conf->{pending}, 1);
2714 }
2715
2716 foreach my $snapname (sort keys %{$conf->{snapshots}}) {
2717 $raw .= "\n[$snapname]\n";
2718 $raw .= &$generate_raw_config($conf->{snapshots}->{$snapname});
2719 }
2720
2721 return $raw;
2722}
2723
2724sub load_defaults {
2725
2726 my $res = {};
2727
2728 # we use static defaults from our JSON schema configuration
2729 foreach my $key (keys %$confdesc) {
2730 if (defined(my $default = $confdesc->{$key}->{default})) {
2731 $res->{$key} = $default;
2732 }
2733 }
2734
2735 return $res;
2736}
2737
2738sub config_list {
2739 my $vmlist = PVE::Cluster::get_vmlist();
2740 my $res = {};
2741 return $res if !$vmlist || !$vmlist->{ids};
2742 my $ids = $vmlist->{ids};
2743 my $nodename = nodename();
2744
2745 foreach my $vmid (keys %$ids) {
2746 my $d = $ids->{$vmid};
2747 next if !$d->{node} || $d->{node} ne $nodename;
2748 next if !$d->{type} || $d->{type} ne 'qemu';
2749 $res->{$vmid}->{exists} = 1;
2750 }
2751 return $res;
2752}
2753
2754# test if VM uses local resources (to prevent migration)
2755sub check_local_resources {
2756 my ($conf, $noerr) = @_;
2757
2758 my @loc_res = ();
2759
2760 push @loc_res, "hostusb" if $conf->{hostusb}; # old syntax
2761 push @loc_res, "hostpci" if $conf->{hostpci}; # old syntax
2762
2763 push @loc_res, "ivshmem" if $conf->{ivshmem};
2764
2765 foreach my $k (keys %$conf) {
2766 next if $k =~ m/^usb/ && ($conf->{$k} =~ m/^spice(?![^,])/);
2767 # sockets are safe: they will recreated be on the target side post-migrate
2768 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2769 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2770 }
2771
2772 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2773
2774 return \@loc_res;
2775}
2776
2777# check if used storages are available on all nodes (use by migrate)
2778sub check_storage_availability {
2779 my ($storecfg, $conf, $node) = @_;
2780
2781 foreach_drive($conf, sub {
2782 my ($ds, $drive) = @_;
2783
2784 my $volid = $drive->{file};
2785 return if !$volid;
2786
2787 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2788 return if !$sid;
2789
2790 # check if storage is available on both nodes
2791 my $scfg = PVE::Storage::storage_check_node($storecfg, $sid);
2792 PVE::Storage::storage_check_node($storecfg, $sid, $node);
2793 });
2794}
2795
2796# list nodes where all VM images are available (used by has_feature API)
2797sub shared_nodes {
2798 my ($conf, $storecfg) = @_;
2799
2800 my $nodelist = PVE::Cluster::get_nodelist();
2801 my $nodehash = { map { $_ => 1 } @$nodelist };
2802 my $nodename = nodename();
2803
2804 foreach_drive($conf, sub {
2805 my ($ds, $drive) = @_;
2806
2807 my $volid = $drive->{file};
2808 return if !$volid;
2809
2810 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2811 if ($storeid) {
2812 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
2813 if ($scfg->{disable}) {
2814 $nodehash = {};
2815 } elsif (my $avail = $scfg->{nodes}) {
2816 foreach my $node (keys %$nodehash) {
2817 delete $nodehash->{$node} if !$avail->{$node};
2818 }
2819 } elsif (!$scfg->{shared}) {
2820 foreach my $node (keys %$nodehash) {
2821 delete $nodehash->{$node} if $node ne $nodename
2822 }
2823 }
2824 }
2825 });
2826
2827 return $nodehash
2828}
2829
2830sub check_local_storage_availability {
2831 my ($conf, $storecfg) = @_;
2832
2833 my $nodelist = PVE::Cluster::get_nodelist();
2834 my $nodehash = { map { $_ => {} } @$nodelist };
2835
2836 foreach_drive($conf, sub {
2837 my ($ds, $drive) = @_;
2838
2839 my $volid = $drive->{file};
2840 return if !$volid;
2841
2842 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2843 if ($storeid) {
2844 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
2845
2846 if ($scfg->{disable}) {
2847 foreach my $node (keys %$nodehash) {
2848 $nodehash->{$node}->{unavailable_storages}->{$storeid} = 1;
2849 }
2850 } elsif (my $avail = $scfg->{nodes}) {
2851 foreach my $node (keys %$nodehash) {
2852 if (!$avail->{$node}) {
2853 $nodehash->{$node}->{unavailable_storages}->{$storeid} = 1;
2854 }
2855 }
2856 }
2857 }
2858 });
2859
2860 foreach my $node (values %$nodehash) {
2861 if (my $unavail = $node->{unavailable_storages}) {
2862 $node->{unavailable_storages} = [ sort keys %$unavail ];
2863 }
2864 }
2865
2866 return $nodehash
2867}
2868
2869# Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2870sub check_running {
2871 my ($vmid, $nocheck, $node) = @_;
2872
2873 PVE::QemuConfig::assert_config_exists_on_node($vmid, $node) if !$nocheck;
2874 return PVE::QemuServer::Helpers::vm_running_locally($vmid);
2875}
2876
2877sub vzlist {
2878
2879 my $vzlist = config_list();
2880
2881 my $fd = IO::Dir->new($PVE::QemuServer::Helpers::var_run_tmpdir) || return $vzlist;
2882
2883 while (defined(my $de = $fd->read)) {
2884 next if $de !~ m/^(\d+)\.pid$/;
2885 my $vmid = $1;
2886 next if !defined($vzlist->{$vmid});
2887 if (my $pid = check_running($vmid)) {
2888 $vzlist->{$vmid}->{pid} = $pid;
2889 }
2890 }
2891
2892 return $vzlist;
2893}
2894
2895sub disksize {
2896 my ($storecfg, $conf) = @_;
2897
2898 my $bootdisk = $conf->{bootdisk};
2899 return undef if !$bootdisk;
2900 return undef if !is_valid_drivename($bootdisk);
2901
2902 return undef if !$conf->{$bootdisk};
2903
2904 my $drive = parse_drive($bootdisk, $conf->{$bootdisk});
2905 return undef if !defined($drive);
2906
2907 return undef if drive_is_cdrom($drive);
2908
2909 my $volid = $drive->{file};
2910 return undef if !$volid;
2911
2912 return $drive->{size};
2913}
2914
2915our $vmstatus_return_properties = {
2916 vmid => get_standard_option('pve-vmid'),
2917 status => {
2918 description => "Qemu process status.",
2919 type => 'string',
2920 enum => ['stopped', 'running'],
2921 },
2922 maxmem => {
2923 description => "Maximum memory in bytes.",
2924 type => 'integer',
2925 optional => 1,
2926 renderer => 'bytes',
2927 },
2928 maxdisk => {
2929 description => "Root disk size in bytes.",
2930 type => 'integer',
2931 optional => 1,
2932 renderer => 'bytes',
2933 },
2934 name => {
2935 description => "VM name.",
2936 type => 'string',
2937 optional => 1,
2938 },
2939 qmpstatus => {
2940 description => "Qemu QMP agent status.",
2941 type => 'string',
2942 optional => 1,
2943 },
2944 pid => {
2945 description => "PID of running qemu process.",
2946 type => 'integer',
2947 optional => 1,
2948 },
2949 uptime => {
2950 description => "Uptime.",
2951 type => 'integer',
2952 optional => 1,
2953 renderer => 'duration',
2954 },
2955 cpus => {
2956 description => "Maximum usable CPUs.",
2957 type => 'number',
2958 optional => 1,
2959 },
2960 lock => {
2961 description => "The current config lock, if any.",
2962 type => 'string',
2963 optional => 1,
2964 },
2965 tags => {
2966 description => "The current configured tags, if any",
2967 type => 'string',
2968 optional => 1,
2969 },
2970};
2971
2972my $last_proc_pid_stat;
2973
2974# get VM status information
2975# This must be fast and should not block ($full == false)
2976# We only query KVM using QMP if $full == true (this can be slow)
2977sub vmstatus {
2978 my ($opt_vmid, $full) = @_;
2979
2980 my $res = {};
2981
2982 my $storecfg = PVE::Storage::config();
2983
2984 my $list = vzlist();
2985 my $defaults = load_defaults();
2986
2987 my ($uptime) = PVE::ProcFSTools::read_proc_uptime(1);
2988
2989 my $cpucount = $cpuinfo->{cpus} || 1;
2990
2991 foreach my $vmid (keys %$list) {
2992 next if $opt_vmid && ($vmid ne $opt_vmid);
2993
2994 my $conf = PVE::QemuConfig->load_config($vmid);
2995
2996 my $d = { vmid => $vmid };
2997 $d->{pid} = $list->{$vmid}->{pid};
2998
2999 # fixme: better status?
3000 $d->{status} = $list->{$vmid}->{pid} ? 'running' : 'stopped';
3001
3002 my $size = disksize($storecfg, $conf);
3003 if (defined($size)) {
3004 $d->{disk} = 0; # no info available
3005 $d->{maxdisk} = $size;
3006 } else {
3007 $d->{disk} = 0;
3008 $d->{maxdisk} = 0;
3009 }
3010
3011 $d->{cpus} = ($conf->{sockets} || $defaults->{sockets})
3012 * ($conf->{cores} || $defaults->{cores});
3013 $d->{cpus} = $cpucount if $d->{cpus} > $cpucount;
3014 $d->{cpus} = $conf->{vcpus} if $conf->{vcpus};
3015
3016 $d->{name} = $conf->{name} || "VM $vmid";
3017 $d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024)
3018 : $defaults->{memory}*(1024*1024);
3019
3020 if ($conf->{balloon}) {
3021 $d->{balloon_min} = $conf->{balloon}*(1024*1024);
3022 $d->{shares} = defined($conf->{shares}) ? $conf->{shares}
3023 : $defaults->{shares};
3024 }
3025
3026 $d->{uptime} = 0;
3027 $d->{cpu} = 0;
3028 $d->{mem} = 0;
3029
3030 $d->{netout} = 0;
3031 $d->{netin} = 0;
3032
3033 $d->{diskread} = 0;
3034 $d->{diskwrite} = 0;
3035
3036 $d->{template} = PVE::QemuConfig->is_template($conf);
3037
3038 $d->{serial} = 1 if conf_has_serial($conf);
3039 $d->{lock} = $conf->{lock} if $conf->{lock};
3040 $d->{tags} = $conf->{tags} if defined($conf->{tags});
3041
3042 $res->{$vmid} = $d;
3043 }
3044
3045 my $netdev = PVE::ProcFSTools::read_proc_net_dev();
3046 foreach my $dev (keys %$netdev) {
3047 next if $dev !~ m/^tap([1-9]\d*)i/;
3048 my $vmid = $1;
3049 my $d = $res->{$vmid};
3050 next if !$d;
3051
3052 $d->{netout} += $netdev->{$dev}->{receive};
3053 $d->{netin} += $netdev->{$dev}->{transmit};
3054
3055 if ($full) {
3056 $d->{nics}->{$dev}->{netout} = $netdev->{$dev}->{receive};
3057 $d->{nics}->{$dev}->{netin} = $netdev->{$dev}->{transmit};
3058 }
3059
3060 }
3061
3062 my $ctime = gettimeofday;
3063
3064 foreach my $vmid (keys %$list) {
3065
3066 my $d = $res->{$vmid};
3067 my $pid = $d->{pid};
3068 next if !$pid;
3069
3070 my $pstat = PVE::ProcFSTools::read_proc_pid_stat($pid);
3071 next if !$pstat; # not running
3072
3073 my $used = $pstat->{utime} + $pstat->{stime};
3074
3075 $d->{uptime} = int(($uptime - $pstat->{starttime})/$cpuinfo->{user_hz});
3076
3077 if ($pstat->{vsize}) {
3078 $d->{mem} = int(($pstat->{rss}/$pstat->{vsize})*$d->{maxmem});
3079 }
3080
3081 my $old = $last_proc_pid_stat->{$pid};
3082 if (!$old) {
3083 $last_proc_pid_stat->{$pid} = {
3084 time => $ctime,
3085 used => $used,
3086 cpu => 0,
3087 };
3088 next;
3089 }
3090
3091 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz};
3092
3093 if ($dtime > 1000) {
3094 my $dutime = $used - $old->{used};
3095
3096 $d->{cpu} = (($dutime/$dtime)* $cpucount) / $d->{cpus};
3097 $last_proc_pid_stat->{$pid} = {
3098 time => $ctime,
3099 used => $used,
3100 cpu => $d->{cpu},
3101 };
3102 } else {
3103 $d->{cpu} = $old->{cpu};
3104 }
3105 }
3106
3107 return $res if !$full;
3108
3109 my $qmpclient = PVE::QMPClient->new();
3110
3111 my $ballooncb = sub {
3112 my ($vmid, $resp) = @_;
3113
3114 my $info = $resp->{'return'};
3115 return if !$info->{max_mem};
3116
3117 my $d = $res->{$vmid};
3118
3119 # use memory assigned to VM
3120 $d->{maxmem} = $info->{max_mem};
3121 $d->{balloon} = $info->{actual};
3122
3123 if (defined($info->{total_mem}) && defined($info->{free_mem})) {
3124 $d->{mem} = $info->{total_mem} - $info->{free_mem};
3125 $d->{freemem} = $info->{free_mem};
3126 }
3127
3128 $d->{ballooninfo} = $info;
3129 };
3130
3131 my $blockstatscb = sub {
3132 my ($vmid, $resp) = @_;
3133 my $data = $resp->{'return'} || [];
3134 my $totalrdbytes = 0;
3135 my $totalwrbytes = 0;
3136
3137 for my $blockstat (@$data) {
3138 $totalrdbytes = $totalrdbytes + $blockstat->{stats}->{rd_bytes};
3139 $totalwrbytes = $totalwrbytes + $blockstat->{stats}->{wr_bytes};
3140
3141 $blockstat->{device} =~ s/drive-//;
3142 $res->{$vmid}->{blockstat}->{$blockstat->{device}} = $blockstat->{stats};
3143 }
3144 $res->{$vmid}->{diskread} = $totalrdbytes;
3145 $res->{$vmid}->{diskwrite} = $totalwrbytes;
3146 };
3147
3148 my $statuscb = sub {
3149 my ($vmid, $resp) = @_;
3150
3151 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
3152 # this fails if ballon driver is not loaded, so this must be
3153 # the last commnand (following command are aborted if this fails).
3154 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
3155
3156 my $status = 'unknown';
3157 if (!defined($status = $resp->{'return'}->{status})) {
3158 warn "unable to get VM status\n";
3159 return;
3160 }
3161
3162 $res->{$vmid}->{qmpstatus} = $resp->{'return'}->{status};
3163 };
3164
3165 foreach my $vmid (keys %$list) {
3166 next if $opt_vmid && ($vmid ne $opt_vmid);
3167 next if !$res->{$vmid}->{pid}; # not running
3168 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
3169 }
3170
3171 $qmpclient->queue_execute(undef, 2);
3172
3173 foreach my $vmid (keys %$list) {
3174 next if $opt_vmid && ($vmid ne $opt_vmid);
3175 $res->{$vmid}->{qmpstatus} = $res->{$vmid}->{status} if !$res->{$vmid}->{qmpstatus};
3176 }
3177
3178 return $res;
3179}
3180
3181sub foreach_drive {
3182 my ($conf, $func, @param) = @_;
3183
3184 foreach my $ds (valid_drive_names()) {
3185 next if !defined($conf->{$ds});
3186
3187 my $drive = parse_drive($ds, $conf->{$ds});
3188 next if !$drive;
3189
3190 &$func($ds, $drive, @param);
3191 }
3192}
3193
3194sub foreach_volid {
3195 my ($conf, $func, @param) = @_;
3196
3197 my $volhash = {};
3198
3199 my $test_volid = sub {
3200 my ($volid, $is_cdrom, $replicate, $shared, $snapname, $size) = @_;
3201
3202 return if !$volid;
3203
3204 $volhash->{$volid}->{cdrom} //= 1;
3205 $volhash->{$volid}->{cdrom} = 0 if !$is_cdrom;
3206
3207 $volhash->{$volid}->{replicate} //= 0;
3208 $volhash->{$volid}->{replicate} = 1 if $replicate;
3209
3210 $volhash->{$volid}->{shared} //= 0;
3211 $volhash->{$volid}->{shared} = 1 if $shared;
3212
3213 $volhash->{$volid}->{referenced_in_config} //= 0;
3214 $volhash->{$volid}->{referenced_in_config} = 1 if !defined($snapname);
3215
3216 $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
3217 if defined($snapname);
3218 $volhash->{$volid}->{size} = $size if $size;
3219 };
3220
3221 foreach_drive($conf, sub {
3222 my ($ds, $drive) = @_;
3223 $test_volid->($drive->{file}, drive_is_cdrom($drive), $drive->{replicate} // 1, $drive->{shared}, undef, $drive->{size});
3224 });
3225
3226 foreach my $snapname (keys %{$conf->{snapshots}}) {
3227 my $snap = $conf->{snapshots}->{$snapname};
3228 $test_volid->($snap->{vmstate}, 0, 1, $snapname);
3229 foreach_drive($snap, sub {
3230 my ($ds, $drive) = @_;
3231 $test_volid->($drive->{file}, drive_is_cdrom($drive), $drive->{replicate} // 1, $drive->{shared}, $snapname);
3232 });
3233 }
3234
3235 foreach my $volid (keys %$volhash) {
3236 &$func($volid, $volhash->{$volid}, @param);
3237 }
3238}
3239
3240sub conf_has_serial {
3241 my ($conf) = @_;
3242
3243 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3244 if ($conf->{"serial$i"}) {
3245 return 1;
3246 }
3247 }
3248
3249 return 0;
3250}
3251
3252sub conf_has_audio {
3253 my ($conf, $id) = @_;
3254
3255 $id //= 0;
3256 my $audio = $conf->{"audio$id"};
3257 return undef if !defined($audio);
3258
3259 my $audioproperties = PVE::JSONSchema::parse_property_string($audio_fmt, $audio);
3260 my $audiodriver = $audioproperties->{driver} // 'spice';
3261
3262 return {
3263 dev => $audioproperties->{device},
3264 dev_id => "audiodev$id",
3265 backend => $audiodriver,
3266 backend_id => "$audiodriver-backend${id}",
3267 };
3268}
3269
3270sub vga_conf_has_spice {
3271 my ($vga) = @_;
3272
3273 my $vgaconf = parse_vga($vga);
3274 my $vgatype = $vgaconf->{type};
3275 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
3276
3277 return $1 || 1;
3278}
3279
3280sub is_native($) {
3281 my ($arch) = @_;
3282 return get_host_arch() eq $arch;
3283}
3284
3285sub get_vm_arch {
3286 my ($conf) = @_;
3287 return $conf->{arch} // get_host_arch();
3288}
3289
3290my $default_machines = {
3291 x86_64 => 'pc',
3292 aarch64 => 'virt',
3293};
3294
3295sub get_vm_machine {
3296 my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
3297
3298 my $machine = $forcemachine || $conf->{machine};
3299
3300 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
3301 $arch //= 'x86_64';
3302 $machine ||= $default_machines->{$arch};
3303 if ($add_pve_version) {
3304 $kvmversion //= kvm_user_version();
3305 my $pvever = PVE::QemuServer::Machine::get_pve_version($kvmversion);
3306 $machine .= "+pve$pvever";
3307 }
3308 }
3309
3310 if ($add_pve_version && $machine !~ m/\+pve\d+$/) {
3311 # for version-pinned machines that do not include a pve-version (e.g.
3312 # pc-q35-4.1), we assume 0 to keep them stable in case we bump
3313 $machine .= '+pve0';
3314 }
3315
3316 return $machine;
3317}
3318
3319sub get_ovmf_files($) {
3320 my ($arch) = @_;
3321
3322 my $ovmf = $OVMF->{$arch}
3323 or die "no OVMF images known for architecture '$arch'\n";
3324
3325 return @$ovmf;
3326}
3327
3328my $Arch2Qemu = {
3329 aarch64 => '/usr/bin/qemu-system-aarch64',
3330 x86_64 => '/usr/bin/qemu-system-x86_64',
3331};
3332sub get_command_for_arch($) {
3333 my ($arch) = @_;
3334 return '/usr/bin/kvm' if is_native($arch);
3335
3336 my $cmd = $Arch2Qemu->{$arch}
3337 or die "don't know how to emulate architecture '$arch'\n";
3338 return $cmd;
3339}
3340
3341# To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
3342# to use in a QEMU command line (-cpu element), first array_intersect the result
3343# of query_supported_ with query_understood_. This is necessary because:
3344#
3345# a) query_understood_ returns flags the host cannot use and
3346# b) query_supported_ (rather the QMP call) doesn't actually return CPU
3347# flags, but CPU settings - with most of them being flags. Those settings
3348# (and some flags, curiously) cannot be specified as a "-cpu" argument.
3349#
3350# query_supported_ needs to start up to 2 temporary VMs and is therefore rather
3351# expensive. If you need the value returned from this, you can get it much
3352# cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags-$accel') with
3353# $accel being 'kvm' or 'tcg'.
3354#
3355# pvestatd calls this function on startup and whenever the QEMU/KVM version
3356# changes, automatically populating pmxcfs.
3357#
3358# Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
3359# since kvm and tcg machines support different flags
3360#
3361sub query_supported_cpu_flags {
3362 my ($arch) = @_;
3363
3364 $arch //= get_host_arch();
3365 my $default_machine = $default_machines->{$arch};
3366
3367 my $flags = {};
3368
3369 # FIXME: Once this is merged, the code below should work for ARM as well:
3370 # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
3371 die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
3372 $arch eq "aarch64";
3373
3374 my $kvm_supported = defined(kvm_version());
3375 my $qemu_cmd = get_command_for_arch($arch);
3376 my $fakevmid = -1;
3377 my $pidfile = PVE::QemuServer::Helpers::pidfile_name($fakevmid);
3378
3379 # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
3380 my $query_supported_run_qemu = sub {
3381 my ($kvm) = @_;
3382
3383 my $flags = {};
3384 my $cmd = [
3385 $qemu_cmd,
3386 '-machine', $default_machine,
3387 '-display', 'none',
3388 '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server,nowait",
3389 '-mon', 'chardev=qmp,mode=control',
3390 '-pidfile', $pidfile,
3391 '-S', '-daemonize'
3392 ];
3393
3394 if (!$kvm) {
3395 push @$cmd, '-accel', 'tcg';
3396 }
3397
3398 my $rc = run_command($cmd, noerr => 1, quiet => 0);
3399 die "QEMU flag querying VM exited with code " . $rc if $rc;
3400
3401 eval {
3402 my $cmd_result = mon_cmd(
3403 $fakevmid,
3404 'query-cpu-model-expansion',
3405 type => 'full',
3406 model => { name => 'host' }
3407 );
3408
3409 my $props = $cmd_result->{model}->{props};
3410 foreach my $prop (keys %$props) {
3411 next if $props->{$prop} ne '1';
3412 # QEMU returns some flags multiple times, with '_', '.' or '-'
3413 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
3414 # We only keep those with underscores, to match /proc/cpuinfo
3415 $prop =~ s/\.|-/_/g;
3416 $flags->{$prop} = 1;
3417 }
3418 };
3419 my $err = $@;
3420
3421 # force stop with 10 sec timeout and 'nocheck'
3422 # always stop, even if QMP failed
3423 vm_stop(undef, $fakevmid, 1, 1, 10, 0, 1);
3424
3425 die $err if $err;
3426
3427 return [ sort keys %$flags ];
3428 };
3429
3430 # We need to query QEMU twice, since KVM and TCG have different supported flags
3431 PVE::QemuConfig->lock_config($fakevmid, sub {
3432 $flags->{tcg} = eval { $query_supported_run_qemu->(0) };
3433 warn "warning: failed querying supported tcg flags: $@\n" if $@;
3434
3435 if ($kvm_supported) {
3436 $flags->{kvm} = eval { $query_supported_run_qemu->(1) };
3437 warn "warning: failed querying supported kvm flags: $@\n" if $@;
3438 }
3439 });
3440
3441 return $flags;
3442}
3443
3444# Understood CPU flags are written to a file at 'pve-qemu' compile time
3445my $understood_cpu_flag_dir = "/usr/share/kvm";
3446sub query_understood_cpu_flags {
3447 my $arch = get_host_arch();
3448 my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
3449
3450 die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
3451 if ! -e $filepath;
3452
3453 my $raw = file_get_contents($filepath);
3454 $raw =~ s/^\s+|\s+$//g;
3455 my @flags = split(/\s+/, $raw);
3456
3457 return \@flags;
3458}
3459
3460sub config_to_command {
3461 my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_;
3462
3463 my $cmd = [];
3464 my $globalFlags = [];
3465 my $machineFlags = [];
3466 my $rtcFlags = [];
3467 my $devices = [];
3468 my $pciaddr = '';
3469 my $bridges = {};
3470 my $ostype = $conf->{ostype};
3471 my $winversion = windows_version($ostype);
3472 my $kvm = $conf->{kvm};
3473 my $nodename = nodename();
3474
3475 my $arch = get_vm_arch($conf);
3476 my $kvm_binary = get_command_for_arch($arch);
3477 my $kvmver = kvm_user_version($kvm_binary);
3478
3479 if (!$kvmver || $kvmver !~ m/^(\d+)\.(\d+)/ || $1 < 3) {
3480 $kvmver //= "undefined";
3481 die "Detected old QEMU binary ('$kvmver', at least 3.0 is required)\n";
3482 }
3483
3484 my $add_pve_version = min_version($kvmver, 4, 1);
3485
3486 my $machine_type = get_vm_machine($conf, $forcemachine, $arch, $add_pve_version);
3487 my $machine_version = PVE::QemuServer::Machine::extract_version($machine_type, $kvmver);
3488 $kvm //= 1 if is_native($arch);
3489
3490 $machine_version =~ m/(\d+)\.(\d+)/;
3491 my ($machine_major, $machine_minor) = ($1, $2);
3492 die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type', please upgrade node '$nodename'\n"
3493 if !PVE::QemuServer::min_version($kvmver, $machine_major, $machine_minor);
3494
3495 if (!PVE::QemuServer::Machine::can_run_pve_machine_version($machine_version, $kvmver)) {
3496 my $max_pve_version = PVE::QemuServer::Machine::get_pve_version($machine_version);
3497 die "Installed qemu-server (max feature level for $machine_major.$machine_minor is pve$max_pve_version)"
3498 . " is too old to run machine type '$machine_type', please upgrade node '$nodename'\n";
3499 }
3500
3501 # if a specific +pve version is required for a feature, use $version_guard
3502 # instead of min_version to allow machines to be run with the minimum
3503 # required version
3504 my $required_pve_version = 0;
3505 my $version_guard = sub {
3506 my ($major, $minor, $pve) = @_;
3507 return 0 if !min_version($machine_version, $major, $minor, $pve);
3508 $required_pve_version = $pve if $pve && $pve > $required_pve_version;
3509 return 1;
3510 };
3511
3512 if ($kvm) {
3513 die "KVM virtualisation configured, but not available. Either disable in VM configuration or enable in BIOS.\n"
3514 if !defined kvm_version();
3515 }
3516
3517 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
3518 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
3519 my $use_old_bios_files = undef;
3520 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
3521
3522 my $cpuunits = defined($conf->{cpuunits}) ?
3523 $conf->{cpuunits} : $defaults->{cpuunits};
3524
3525 push @$cmd, $kvm_binary;
3526
3527 push @$cmd, '-id', $vmid;
3528
3529 my $vmname = $conf->{name} || "vm$vmid";
3530
3531 push @$cmd, '-name', $vmname;
3532
3533 my $use_virtio = 0;
3534
3535 my $qmpsocket = PVE::QemuServer::Helpers::qmp_socket($vmid);
3536 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait";
3537 push @$cmd, '-mon', "chardev=qmp,mode=control";
3538
3539 if (min_version($machine_version, 2, 12)) {
3540 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
3541 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3542 }
3543
3544 push @$cmd, '-pidfile' , PVE::QemuServer::Helpers::pidfile_name($vmid);
3545
3546 push @$cmd, '-daemonize';
3547
3548 if ($conf->{smbios1}) {
3549 my $smbios_conf = parse_smbios1($conf->{smbios1});
3550 if ($smbios_conf->{base64}) {
3551 # Do not pass base64 flag to qemu
3552 delete $smbios_conf->{base64};
3553 my $smbios_string = "";
3554 foreach my $key (keys %$smbios_conf) {
3555 my $value;
3556 if ($key eq "uuid") {
3557 $value = $smbios_conf->{uuid}
3558 } else {
3559 $value = decode_base64($smbios_conf->{$key});
3560 }
3561 # qemu accepts any binary data, only commas need escaping by double comma
3562 $value =~ s/,/,,/g;
3563 $smbios_string .= "," . $key . "=" . $value if $value;
3564 }
3565 push @$cmd, '-smbios', "type=1" . $smbios_string;
3566 } else {
3567 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3568 }
3569 }
3570
3571 my ($ovmf_code, $ovmf_vars) = get_ovmf_files($arch);
3572 if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
3573 die "uefi base image not found\n" if ! -f $ovmf_code;
3574
3575 my $path;
3576 my $format;
3577 if (my $efidisk = $conf->{efidisk0}) {
3578 my $d = PVE::JSONSchema::parse_property_string($efidisk_fmt, $efidisk);
3579 my ($storeid, $volname) = PVE::Storage::parse_volume_id($d->{file}, 1);
3580 $format = $d->{format};
3581 if ($storeid) {
3582 $path = PVE::Storage::path($storecfg, $d->{file});
3583 if (!defined($format)) {
3584 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
3585 $format = qemu_img_format($scfg, $volname);
3586 }
3587 } else {
3588 $path = $d->{file};
3589 die "efidisk format must be specified\n"
3590 if !defined($format);
3591 }
3592 } else {
3593 warn "no efidisk configured! Using temporary efivars disk.\n";
3594 $path = "/tmp/$vmid-ovmf.fd";
3595 PVE::Tools::file_copy($ovmf_vars, $path, -s $ovmf_vars);
3596 $format = 'raw';
3597 }
3598
3599 push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly,file=$ovmf_code";
3600 push @$cmd, '-drive', "if=pflash,unit=1,format=$format,id=drive-efidisk0,file=$path";
3601 }
3602
3603 # load q35 config
3604 if ($q35) {
3605 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
3606 if (min_version($machine_version, 4, 0)) {
3607 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3608 } else {
3609 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3610 }
3611 }
3612
3613 if ($conf->{vmgenid}) {
3614 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid};
3615 }
3616
3617 # add usb controllers
3618 my @usbcontrollers = PVE::QemuServer::USB::get_usb_controllers($conf, $bridges, $arch, $machine_type, $usbdesc->{format}, $MAX_USB_DEVICES);
3619 push @$devices, @usbcontrollers if @usbcontrollers;
3620 my $vga = parse_vga($conf->{vga});
3621
3622 my $qxlnum = vga_conf_has_spice($conf->{vga});
3623 $vga->{type} = 'qxl' if $qxlnum;
3624
3625 if (!$vga->{type}) {
3626 if ($arch eq 'aarch64') {
3627 $vga->{type} = 'virtio';
3628 } elsif (min_version($machine_version, 2, 9)) {
3629 $vga->{type} = (!$winversion || $winversion >= 6) ? 'std' : 'cirrus';
3630 } else {
3631 $vga->{type} = ($winversion >= 6) ? 'std' : 'cirrus';
3632 }
3633 }
3634
3635 # enable absolute mouse coordinates (needed by vnc)
3636 my $tablet;
3637 if (defined($conf->{tablet})) {
3638 $tablet = $conf->{tablet};
3639 } else {
3640 $tablet = $defaults->{tablet};
3641 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
3642 $tablet = 0 if $vga->{type} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
3643 }
3644
3645 if ($tablet) {
3646 push @$devices, '-device', print_tabletdevice_full($conf, $arch) if $tablet;
3647 my $kbd = print_keyboarddevice_full($conf, $arch);
3648 push @$devices, '-device', $kbd if defined($kbd);
3649 }
3650
3651 my $kvm_off = 0;
3652 my $gpu_passthrough;
3653
3654 # host pci devices
3655 for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
3656 my $id = "hostpci$i";
3657 my $d = parse_hostpci($conf->{$id});
3658 next if !$d;
3659
3660 if (my $pcie = $d->{pcie}) {
3661 die "q35 machine model is not enabled" if !$q35;
3662 # win7 wants to have the pcie devices directly on the pcie bus
3663 # instead of in the root port
3664 if ($winversion == 7) {
3665 $pciaddr = print_pcie_addr("${id}bus0");
3666 } else {
3667 # add more root ports if needed, 4 are present by default
3668 # by pve-q35 cfgs, rest added here on demand.
3669 if ($i > 3) {
3670 push @$devices, '-device', print_pcie_root_port($i);
3671 }
3672 $pciaddr = print_pcie_addr($id);
3673 }
3674 } else {
3675 $pciaddr = print_pci_addr($id, $bridges, $arch, $machine_type);
3676 }
3677
3678 my $xvga = '';
3679 if ($d->{'x-vga'}) {
3680 $xvga = ',x-vga=on' if !($conf->{bios} && $conf->{bios} eq 'ovmf');
3681 $kvm_off = 1;
3682 $vga->{type} = 'none' if !defined($conf->{vga});
3683 $gpu_passthrough = 1;
3684 }
3685
3686 my $pcidevices = $d->{pciid};
3687 my $multifunction = 1 if @$pcidevices > 1;
3688
3689 my $sysfspath;
3690 if ($d->{mdev} && scalar(@$pcidevices) == 1) {
3691 my $pci_id = $pcidevices->[0]->{id};
3692 my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
3693 $sysfspath = "/sys/bus/pci/devices/$pci_id/$uuid";
3694 } elsif ($d->{mdev}) {
3695 warn "ignoring mediated device '$id' with multifunction device\n";
3696 }
3697
3698 my $j=0;
3699 foreach my $pcidevice (@$pcidevices) {
3700 my $devicestr = "vfio-pci";
3701
3702 if ($sysfspath) {
3703 $devicestr .= ",sysfsdev=$sysfspath";
3704 } else {
3705 $devicestr .= ",host=$pcidevice->{id}";
3706 }
3707
3708 my $mf_addr = $multifunction ? ".$j" : '';
3709 $devicestr .= ",id=${id}${mf_addr}${pciaddr}${mf_addr}";
3710
3711 if ($j == 0) {
3712 $devicestr .= ',rombar=0' if defined($d->{rombar}) && !$d->{rombar};
3713 $devicestr .= "$xvga";
3714 $devicestr .= ",multifunction=on" if $multifunction;
3715 $devicestr .= ",romfile=/usr/share/kvm/$d->{romfile}" if $d->{romfile};
3716 }
3717
3718 push @$devices, '-device', $devicestr;
3719 $j++;
3720 }
3721 }
3722
3723 # usb devices
3724 my $usb_dev_features = {};
3725 $usb_dev_features->{spice_usb3} = 1 if min_version($machine_version, 4, 0);
3726
3727 my @usbdevices = PVE::QemuServer::USB::get_usb_devices($conf, $usbdesc->{format}, $MAX_USB_DEVICES, $usb_dev_features);
3728 push @$devices, @usbdevices if @usbdevices;
3729 # serial devices
3730 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3731 if (my $path = $conf->{"serial$i"}) {
3732 if ($path eq 'socket') {
3733 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3734 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server,nowait";
3735 # On aarch64, serial0 is the UART device. Qemu only allows
3736 # connecting UART devices via the '-serial' command line, as
3737 # the device has a fixed slot on the hardware...
3738 if ($arch eq 'aarch64' && $i == 0) {
3739 push @$devices, '-serial', "chardev:serial$i";
3740 } else {
3741 push @$devices, '-device', "isa-serial,chardev=serial$i";
3742 }
3743 } else {
3744 die "no such serial device\n" if ! -c $path;
3745 push @$devices, '-chardev', "tty,id=serial$i,path=$path";
3746 push @$devices, '-device', "isa-serial,chardev=serial$i";
3747 }
3748 }
3749 }
3750
3751 # parallel devices
3752 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
3753 if (my $path = $conf->{"parallel$i"}) {
3754 die "no such parallel device\n" if ! -c $path;
3755 my $devtype = $path =~ m!^/dev/usb/lp! ? 'tty' : 'parport';
3756 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
3757 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
3758 }
3759 }
3760
3761 if (my $audio = conf_has_audio($conf)) {
3762
3763 my $audiopciaddr = print_pci_addr("audio0", $bridges, $arch, $machine_type);
3764
3765 my $id = $audio->{dev_id};
3766 if ($audio->{dev} eq 'AC97') {
3767 push @$devices, '-device', "AC97,id=${id}${audiopciaddr}";
3768 } elsif ($audio->{dev} =~ /intel\-hda$/) {
3769 push @$devices, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
3770 push @$devices, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0";
3771 push @$devices, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1";
3772 } else {
3773 die "unkown audio device '$audio->{dev}', implement me!";
3774 }
3775
3776 push @$devices, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
3777 }
3778
3779 my $sockets = 1;
3780 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
3781 $sockets = $conf->{sockets} if $conf->{sockets};
3782
3783 my $cores = $conf->{cores} || 1;
3784
3785 my $maxcpus = $sockets * $cores;
3786
3787 my $vcpus = $conf->{vcpus} ? $conf->{vcpus} : $maxcpus;
3788
3789 my $allowed_vcpus = $cpuinfo->{cpus};
3790
3791 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n"
3792 if ($allowed_vcpus < $maxcpus);
3793
3794 if($hotplug_features->{cpu} && min_version($machine_version, 2, 7)) {
3795
3796 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3797 for (my $i = 2; $i <= $vcpus; $i++) {
3798 my $cpustr = print_cpu_device($conf,$i);
3799 push @$cmd, '-device', $cpustr;
3800 }
3801
3802 } else {
3803
3804 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3805 }
3806 push @$cmd, '-nodefaults';
3807
3808 my $bootorder = $conf->{boot} || $confdesc->{boot}->{default};
3809
3810 my $bootindex_hash = {};
3811 my $i = 1;
3812 foreach my $o (split(//, $bootorder)) {
3813 $bootindex_hash->{$o} = $i*100;
3814 $i++;
3815 }
3816
3817 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
3818
3819 push @$cmd, '-no-acpi' if defined($conf->{acpi}) && $conf->{acpi} == 0;
3820
3821 push @$cmd, '-no-reboot' if defined($conf->{reboot}) && $conf->{reboot} == 0;
3822
3823 if ($vga->{type} && $vga->{type} !~ m/^serial\d+$/ && $vga->{type} ne 'none'){
3824 push @$devices, '-device', print_vga_device($conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
3825 my $socket = PVE::QemuServer::Helpers::vnc_socket($vmid);
3826 push @$cmd, '-vnc', "unix:$socket,password";
3827 } else {
3828 push @$cmd, '-vga', 'none' if $vga->{type} eq 'none';
3829 push @$cmd, '-nographic';
3830 }
3831
3832 # time drift fix
3833 my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
3834
3835 my $useLocaltime = $conf->{localtime};
3836
3837 if ($winversion >= 5) { # windows
3838 $useLocaltime = 1 if !defined($conf->{localtime});
3839
3840 # use time drift fix when acpi is enabled
3841 if (!(defined($conf->{acpi}) && $conf->{acpi} == 0)) {
3842 $tdf = 1 if !defined($conf->{tdf});
3843 }
3844 }
3845
3846 if ($winversion >= 6) {
3847 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3848 push @$cmd, '-no-hpet';
3849 }
3850
3851 push @$rtcFlags, 'driftfix=slew' if $tdf;
3852
3853 if (($conf->{startdate}) && ($conf->{startdate} ne 'now')) {
3854 push @$rtcFlags, "base=$conf->{startdate}";
3855 } elsif ($useLocaltime) {
3856 push @$rtcFlags, 'base=localtime';
3857 }
3858
3859 push @$cmd, get_cpu_options($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
3860
3861 PVE::QemuServer::Memory::config($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
3862
3863 push @$cmd, '-S' if $conf->{freeze};
3864
3865 push @$cmd, '-k', $conf->{keyboard} if defined($conf->{keyboard});
3866
3867 my $guest_agent = parse_guest_agent($conf);
3868
3869 if ($guest_agent->{enabled}) {
3870 my $qgasocket = PVE::QemuServer::Helpers::qmp_socket($vmid, 1);
3871 push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0";
3872
3873 if (!$guest_agent->{type} || $guest_agent->{type} eq 'virtio') {
3874 my $pciaddr = print_pci_addr("qga0", $bridges, $arch, $machine_type);
3875 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3876 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3877 } elsif ($guest_agent->{type} eq 'isa') {
3878 push @$devices, '-device', "isa-serial,chardev=qga0";
3879 }
3880 }
3881
3882 my $rng = parse_rng($conf->{rng0}) if $conf->{rng0};
3883 if ($rng && &$version_guard(4, 1, 2)) {
3884 my $max_bytes = $rng->{max_bytes} // $rng_fmt->{max_bytes}->{default};
3885 my $period = $rng->{period} // $rng_fmt->{period}->{default};
3886
3887 my $limiter_str = "";
3888 if ($max_bytes) {
3889 $limiter_str = ",max-bytes=$max_bytes,period=$period";
3890 }
3891
3892 # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
3893 die "cannot create VirtIO RNG device: source file '$rng->{source}' doesn't exist\n"
3894 if ! -e $rng->{source};
3895
3896 my $rng_addr = print_pci_addr("rng0", $bridges, $arch, $machine_type);
3897
3898 push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
3899 push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
3900 }
3901
3902 my $spice_port;
3903
3904 if ($qxlnum) {
3905 if ($qxlnum > 1) {
3906 if ($winversion){
3907 for(my $i = 1; $i < $qxlnum; $i++){
3908 push @$devices, '-device', print_vga_device($conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
3909 }
3910 } else {
3911 # assume other OS works like Linux
3912 my ($ram, $vram) = ("134217728", "67108864");
3913 if ($vga->{memory}) {
3914 $ram = PVE::Tools::convert_size($qxlnum*4*$vga->{memory}, 'mb' => 'b');
3915 $vram = PVE::Tools::convert_size($qxlnum*2*$vga->{memory}, 'mb' => 'b');
3916 }
3917 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3918 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
3919 }
3920 }
3921
3922 my $pciaddr = print_pci_addr("spice", $bridges, $arch, $machine_type);
3923
3924 my $pfamily = PVE::Tools::get_host_address_family($nodename);
3925 my @nodeaddrs = PVE::Tools::getaddrinfo_all('localhost', family => $pfamily);
3926 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
3927
3928 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3929 push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
3930 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3931
3932 my $localhost = PVE::Network::addr_to_ip($nodeaddrs[0]->{addr});
3933 $spice_port = PVE::Tools::next_spice_port($pfamily, $localhost);
3934
3935 my $spice_enhancement = PVE::JSONSchema::parse_property_string($spice_enhancements_fmt, $conf->{spice_enhancements} // '');
3936 if ($spice_enhancement->{foldersharing}) {
3937 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3938 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3939 }
3940
3941 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
3942 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}" if $spice_enhancement->{videostreaming};
3943 push @$devices, '-spice', "$spice_opts";
3944 }
3945
3946 # enable balloon by default, unless explicitly disabled
3947 if (!defined($conf->{balloon}) || $conf->{balloon}) {
3948 $pciaddr = print_pci_addr("balloon0", $bridges, $arch, $machine_type);
3949 push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr";
3950 }
3951
3952 if ($conf->{watchdog}) {
3953 my $wdopts = parse_watchdog($conf->{watchdog});
3954 $pciaddr = print_pci_addr("watchdog", $bridges, $arch, $machine_type);
3955 my $watchdog = $wdopts->{model} || 'i6300esb';
3956 push @$devices, '-device', "$watchdog$pciaddr";
3957 push @$devices, '-watchdog-action', $wdopts->{action} if $wdopts->{action};
3958 }
3959
3960 my $vollist = [];
3961 my $scsicontroller = {};
3962 my $ahcicontroller = {};
3963 my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : $defaults->{scsihw};
3964
3965 # Add iscsi initiator name if available
3966 if (my $initiator = get_initiator_name()) {
3967 push @$devices, '-iscsi', "initiator-name=$initiator";
3968 }
3969
3970 foreach_drive($conf, sub {
3971 my ($ds, $drive) = @_;
3972
3973 if (PVE::Storage::parse_volume_id($drive->{file}, 1)) {
3974 push @$vollist, $drive->{file};
3975 }
3976
3977 # ignore efidisk here, already added in bios/fw handling code above
3978 return if $drive->{interface} eq 'efidisk';
3979
3980 $use_virtio = 1 if $ds =~ m/^virtio/;
3981
3982 if (drive_is_cdrom ($drive)) {
3983 if ($bootindex_hash->{d}) {
3984 $drive->{bootindex} = $bootindex_hash->{d};
3985 $bootindex_hash->{d} += 1;
3986 }
3987 } else {
3988 if ($bootindex_hash->{c}) {
3989 $drive->{bootindex} = $bootindex_hash->{c} if $conf->{bootdisk} && ($conf->{bootdisk} eq $ds);
3990 $bootindex_hash->{c} += 1;
3991 }
3992 }
3993
3994 if($drive->{interface} eq 'virtio'){
3995 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread};
3996 }
3997
3998 if ($drive->{interface} eq 'scsi') {
3999
4000 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive);
4001
4002 die "scsi$drive->{index}: machine version 4.1~pve2 or higher is required to use more than 14 SCSI disks\n"
4003 if $drive->{index} > 13 && !&$version_guard(4, 1, 2);
4004
4005 $pciaddr = print_pci_addr("$controller_prefix$controller", $bridges, $arch, $machine_type);
4006 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ? "virtio-scsi-pci" : $scsihw;
4007
4008 my $iothread = '';
4009 if($conf->{scsihw} && $conf->{scsihw} eq "virtio-scsi-single" && $drive->{iothread}){
4010 $iothread .= ",iothread=iothread-$controller_prefix$controller";
4011 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
4012 } elsif ($drive->{iothread}) {
4013 warn "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n";
4014 }
4015
4016 my $queues = '';
4017 if($conf->{scsihw} && $conf->{scsihw} eq "virtio-scsi-single" && $drive->{queues}){
4018 $queues = ",num_queues=$drive->{queues}";
4019 }
4020
4021 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues" if !$scsicontroller->{$controller};
4022 $scsicontroller->{$controller}=1;
4023 }
4024
4025 if ($drive->{interface} eq 'sata') {
4026 my $controller = int($drive->{index} / $MAX_SATA_DISKS);
4027 $pciaddr = print_pci_addr("ahci$controller", $bridges, $arch, $machine_type);
4028 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller};
4029 $ahcicontroller->{$controller}=1;
4030 }
4031
4032 my $drive_cmd = print_drive_full($storecfg, $vmid, $drive);
4033 push @$devices, '-drive',$drive_cmd;
4034 push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
4035 });
4036
4037 for (my $i = 0; $i < $MAX_NETS; $i++) {
4038 next if !$conf->{"net$i"};
4039 my $d = parse_net($conf->{"net$i"});
4040 next if !$d;
4041
4042 $use_virtio = 1 if $d->{model} eq 'virtio';
4043
4044 if ($bootindex_hash->{n}) {
4045 $d->{bootindex} = $bootindex_hash->{n};
4046 $bootindex_hash->{n} += 1;
4047 }
4048
4049 my $netdevfull = print_netdev_full($vmid, $conf, $arch, $d, "net$i");
4050 push @$devices, '-netdev', $netdevfull;
4051
4052 my $netdevicefull = print_netdevice_full($vmid, $conf, $d, "net$i", $bridges, $use_old_bios_files, $arch, $machine_type);
4053 push @$devices, '-device', $netdevicefull;
4054 }
4055
4056 if ($conf->{ivshmem}) {
4057 my $ivshmem = PVE::JSONSchema::parse_property_string($ivshmem_fmt, $conf->{ivshmem});
4058
4059 my $bus;
4060 if ($q35) {
4061 $bus = print_pcie_addr("ivshmem");
4062 } else {
4063 $bus = print_pci_addr("ivshmem", $bridges, $arch, $machine_type);
4064 }
4065
4066 my $ivshmem_name = $ivshmem->{name} // $vmid;
4067 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
4068
4069 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
4070 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path,size=$ivshmem->{size}M";
4071 }
4072
4073 # pci.4 is nested in pci.1
4074 $bridges->{1} = 1 if $bridges->{4};
4075
4076 if (!$q35) {
4077 # add pci bridges
4078 if (min_version($machine_version, 2, 3)) {
4079 $bridges->{1} = 1;
4080 $bridges->{2} = 1;
4081 }
4082
4083 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
4084
4085 }
4086
4087 for my $k (sort {$b cmp $a} keys %$bridges) {
4088 next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
4089 $pciaddr = print_pci_addr("pci.$k", undef, $arch, $machine_type);
4090 my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
4091 if ($q35) {
4092 # add after -readconfig pve-q35.cfg
4093 splice @$devices, 2, 0, '-device', $devstr;
4094 } else {
4095 unshift @$devices, '-device', $devstr if $k > 0;
4096 }
4097 }
4098
4099 if (!$kvm) {
4100 push @$machineFlags, 'accel=tcg';
4101 }
4102
4103 my $machine_type_min = $machine_type;
4104 if ($add_pve_version) {
4105 $machine_type_min =~ s/\+pve\d+$//;
4106 $machine_type_min .= "+pve$required_pve_version";
4107 }
4108 push @$machineFlags, "type=${machine_type_min}";
4109
4110 push @$cmd, @$devices;
4111 push @$cmd, '-rtc', join(',', @$rtcFlags)
4112 if scalar(@$rtcFlags);
4113 push @$cmd, '-machine', join(',', @$machineFlags)
4114 if scalar(@$machineFlags);
4115 push @$cmd, '-global', join(',', @$globalFlags)
4116 if scalar(@$globalFlags);
4117
4118 if (my $vmstate = $conf->{vmstate}) {
4119 my $statepath = PVE::Storage::path($storecfg, $vmstate);
4120 push @$vollist, $vmstate;
4121 push @$cmd, '-loadstate', $statepath;
4122 print "activating and using '$vmstate' as vmstate\n";
4123 }
4124
4125 # add custom args
4126 if ($conf->{args}) {
4127 my $aa = PVE::Tools::split_args($conf->{args});
4128 push @$cmd, @$aa;
4129 }
4130
4131 return wantarray ? ($cmd, $vollist, $spice_port) : $cmd;
4132}
4133
4134sub spice_port {
4135 my ($vmid) = @_;
4136
4137 my $res = mon_cmd($vmid, 'query-spice');
4138
4139 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
4140}
4141
4142sub vm_devices_list {
4143 my ($vmid) = @_;
4144
4145 my $res = mon_cmd($vmid, 'query-pci');
4146 my $devices_to_check = [];
4147 my $devices = {};
4148 foreach my $pcibus (@$res) {
4149 push @$devices_to_check, @{$pcibus->{devices}},
4150 }
4151
4152 while (@$devices_to_check) {
4153 my $to_check = [];
4154 for my $d (@$devices_to_check) {
4155 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
4156 next if !$d->{'pci_bridge'};
4157
4158 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices}});
4159 push @$to_check, @{$d->{'pci_bridge'}->{devices}};
4160 }
4161 $devices_to_check = $to_check;
4162 }
4163
4164 my $resblock = mon_cmd($vmid, 'query-block');
4165 foreach my $block (@$resblock) {
4166 if($block->{device} =~ m/^drive-(\S+)/){
4167 $devices->{$1} = 1;
4168 }
4169 }
4170
4171 my $resmice = mon_cmd($vmid, 'query-mice');
4172 foreach my $mice (@$resmice) {
4173 if ($mice->{name} eq 'QEMU HID Tablet') {
4174 $devices->{tablet} = 1;
4175 last;
4176 }
4177 }
4178
4179 # for usb devices there is no query-usb
4180 # but we can iterate over the entries in
4181 # qom-list path=/machine/peripheral
4182 my $resperipheral = mon_cmd($vmid, 'qom-list', path => '/machine/peripheral');
4183 foreach my $per (@$resperipheral) {
4184 if ($per->{name} =~ m/^usb\d+$/) {
4185 $devices->{$per->{name}} = 1;
4186 }
4187 }
4188
4189 return $devices;
4190}
4191
4192sub vm_deviceplug {
4193 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4194
4195 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
4196
4197 my $devices_list = vm_devices_list($vmid);
4198 return 1 if defined($devices_list->{$deviceid});
4199
4200 qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type); # add PCI bridge if we need it for the device
4201
4202 if ($deviceid eq 'tablet') {
4203
4204 qemu_deviceadd($vmid, print_tabletdevice_full($conf, $arch));
4205
4206 } elsif ($deviceid eq 'keyboard') {
4207
4208 qemu_deviceadd($vmid, print_keyboarddevice_full($conf, $arch));
4209
4210 } elsif ($deviceid =~ m/^usb(\d+)$/) {
4211
4212 die "usb hotplug currently not reliable\n";
4213 # since we can't reliably hot unplug all added usb devices
4214 # and usb passthrough disables live migration
4215 # we disable usb hotplugging for now
4216 qemu_deviceadd($vmid, PVE::QemuServer::USB::print_usbdevice_full($conf, $deviceid, $device));
4217
4218 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4219
4220 qemu_iothread_add($vmid, $deviceid, $device);
4221
4222 qemu_driveadd($storecfg, $vmid, $device);
4223 my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device, $arch, $machine_type);
4224
4225 qemu_deviceadd($vmid, $devicefull);
4226 eval { qemu_deviceaddverify($vmid, $deviceid); };
4227 if (my $err = $@) {
4228 eval { qemu_drivedel($vmid, $deviceid); };
4229 warn $@ if $@;
4230 die $err;
4231 }
4232
4233 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4234
4235
4236 my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : "lsi";
4237 my $pciaddr = print_pci_addr($deviceid, undef, $arch, $machine_type);
4238 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ? "virtio-scsi-pci" : $scsihw;
4239
4240 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
4241
4242 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread}) {
4243 qemu_iothread_add($vmid, $deviceid, $device);
4244 $devicefull .= ",iothread=iothread-$deviceid";
4245 }
4246
4247 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues}) {
4248 $devicefull .= ",num_queues=$device->{queues}";
4249 }
4250
4251 qemu_deviceadd($vmid, $devicefull);
4252 qemu_deviceaddverify($vmid, $deviceid);
4253
4254 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4255
4256 qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device, $arch, $machine_type);
4257 qemu_driveadd($storecfg, $vmid, $device);
4258
4259 my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device, $arch, $machine_type);
4260 eval { qemu_deviceadd($vmid, $devicefull); };
4261 if (my $err = $@) {
4262 eval { qemu_drivedel($vmid, $deviceid); };
4263 warn $@ if $@;
4264 die $err;
4265 }
4266
4267 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4268
4269 return undef if !qemu_netdevadd($vmid, $conf, $arch, $device, $deviceid);
4270
4271 my $machine_type = PVE::QemuServer::Machine::qemu_machine_pxe($vmid, $conf);
4272 my $use_old_bios_files = undef;
4273 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
4274
4275 my $netdevicefull = print_netdevice_full($vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type);
4276 qemu_deviceadd($vmid, $netdevicefull);
4277 eval {
4278 qemu_deviceaddverify($vmid, $deviceid);
4279 qemu_set_link_status($vmid, $deviceid, !$device->{link_down});
4280 };
4281 if (my $err = $@) {
4282 eval { qemu_netdevdel($vmid, $deviceid); };
4283 warn $@ if $@;
4284 die $err;
4285 }
4286
4287 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
4288
4289 my $bridgeid = $2;
4290 my $pciaddr = print_pci_addr($deviceid, undef, $arch, $machine_type);
4291 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
4292
4293 qemu_deviceadd($vmid, $devicefull);
4294 qemu_deviceaddverify($vmid, $deviceid);
4295
4296 } else {
4297 die "can't hotplug device '$deviceid'\n";
4298 }
4299
4300 return 1;
4301}
4302
4303# fixme: this should raise exceptions on error!
4304sub vm_deviceunplug {
4305 my ($vmid, $conf, $deviceid) = @_;
4306
4307 my $devices_list = vm_devices_list($vmid);
4308 return 1 if !defined($devices_list->{$deviceid});
4309
4310 die "can't unplug bootdisk" if $conf->{bootdisk} && $conf->{bootdisk} eq $deviceid;
4311
4312 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard') {
4313
4314 qemu_devicedel($vmid, $deviceid);
4315
4316 } elsif ($deviceid =~ m/^usb\d+$/) {
4317
4318 die "usb hotplug currently not reliable\n";
4319 # when unplugging usb devices this way,
4320 # there may be remaining usb controllers/hubs
4321 # so we disable it for now
4322 qemu_devicedel($vmid, $deviceid);
4323 qemu_devicedelverify($vmid, $deviceid);
4324
4325 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
4326
4327 qemu_devicedel($vmid, $deviceid);
4328 qemu_devicedelverify($vmid, $deviceid);
4329 qemu_drivedel($vmid, $deviceid);
4330 qemu_iothread_del($conf, $vmid, $deviceid);
4331
4332 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
4333
4334 qemu_devicedel($vmid, $deviceid);
4335 qemu_devicedelverify($vmid, $deviceid);
4336 qemu_iothread_del($conf, $vmid, $deviceid);
4337
4338 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
4339
4340 qemu_devicedel($vmid, $deviceid);
4341 qemu_drivedel($vmid, $deviceid);
4342 qemu_deletescsihw($conf, $vmid, $deviceid);
4343
4344 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
4345
4346 qemu_devicedel($vmid, $deviceid);
4347 qemu_devicedelverify($vmid, $deviceid);
4348 qemu_netdevdel($vmid, $deviceid);
4349
4350 } else {
4351 die "can't unplug device '$deviceid'\n";
4352 }
4353
4354 return 1;
4355}
4356
4357sub qemu_deviceadd {
4358 my ($vmid, $devicefull) = @_;
4359
4360 $devicefull = "driver=".$devicefull;
4361 my %options = split(/[=,]/, $devicefull);
4362
4363 mon_cmd($vmid, "device_add" , %options);
4364}
4365
4366sub qemu_devicedel {
4367 my ($vmid, $deviceid) = @_;
4368
4369 my $ret = mon_cmd($vmid, "device_del", id => $deviceid);
4370}
4371
4372sub qemu_iothread_add {
4373 my($vmid, $deviceid, $device) = @_;
4374
4375 if ($device->{iothread}) {
4376 my $iothreads = vm_iothreads_list($vmid);
4377 qemu_objectadd($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4378 }
4379}
4380
4381sub qemu_iothread_del {
4382 my($conf, $vmid, $deviceid) = @_;
4383
4384 my $confid = $deviceid;
4385 if ($deviceid =~ m/^(?:virtioscsi|scsihw)(\d+)$/) {
4386 $confid = 'scsi' . $1;
4387 }
4388 my $device = parse_drive($confid, $conf->{$confid});
4389 if ($device->{iothread}) {
4390 my $iothreads = vm_iothreads_list($vmid);
4391 qemu_objectdel($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4392 }
4393}
4394
4395sub qemu_objectadd {
4396 my($vmid, $objectid, $qomtype) = @_;
4397
4398 mon_cmd($vmid, "object-add", id => $objectid, "qom-type" => $qomtype);
4399
4400 return 1;
4401}
4402
4403sub qemu_objectdel {
4404 my($vmid, $objectid) = @_;
4405
4406 mon_cmd($vmid, "object-del", id => $objectid);
4407
4408 return 1;
4409}
4410
4411sub qemu_driveadd {
4412 my ($storecfg, $vmid, $device) = @_;
4413
4414 my $drive = print_drive_full($storecfg, $vmid, $device);
4415 $drive =~ s/\\/\\\\/g;
4416 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_add auto \"$drive\"");
4417
4418 # If the command succeeds qemu prints: "OK"
4419 return 1 if $ret =~ m/OK/s;
4420
4421 die "adding drive failed: $ret\n";
4422}
4423
4424sub qemu_drivedel {
4425 my($vmid, $deviceid) = @_;
4426
4427 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-$deviceid");
4428 $ret =~ s/^\s+//;
4429
4430 return 1 if $ret eq "";
4431
4432 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
4433 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4434
4435 die "deleting drive $deviceid failed : $ret\n";
4436}
4437
4438sub qemu_deviceaddverify {
4439 my ($vmid, $deviceid) = @_;
4440
4441 for (my $i = 0; $i <= 5; $i++) {
4442 my $devices_list = vm_devices_list($vmid);
4443 return 1 if defined($devices_list->{$deviceid});
4444 sleep 1;
4445 }
4446
4447 die "error on hotplug device '$deviceid'\n";
4448}
4449
4450
4451sub qemu_devicedelverify {
4452 my ($vmid, $deviceid) = @_;
4453
4454 # need to verify that the device is correctly removed as device_del
4455 # is async and empty return is not reliable
4456
4457 for (my $i = 0; $i <= 5; $i++) {
4458 my $devices_list = vm_devices_list($vmid);
4459 return 1 if !defined($devices_list->{$deviceid});
4460 sleep 1;
4461 }
4462
4463 die "error on hot-unplugging device '$deviceid'\n";
4464}
4465
4466sub qemu_findorcreatescsihw {
4467 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4468
4469 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4470
4471 my $scsihwid="$controller_prefix$controller";
4472 my $devices_list = vm_devices_list($vmid);
4473
4474 if(!defined($devices_list->{$scsihwid})) {
4475 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
4476 }
4477
4478 return 1;
4479}
4480
4481sub qemu_deletescsihw {
4482 my ($conf, $vmid, $opt) = @_;
4483
4484 my $device = parse_drive($opt, $conf->{$opt});
4485
4486 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
4487 vm_deviceunplug($vmid, $conf, "virtioscsi$device->{index}");
4488 return 1;
4489 }
4490
4491 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
4492
4493 my $devices_list = vm_devices_list($vmid);
4494 foreach my $opt (keys %{$devices_list}) {
4495 if (PVE::QemuServer::is_valid_drivename($opt)) {
4496 my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt});
4497 if($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
4498 return 1;
4499 }
4500 }
4501 }
4502
4503 my $scsihwid="scsihw$controller";
4504
4505 vm_deviceunplug($vmid, $conf, $scsihwid);
4506
4507 return 1;
4508}
4509
4510sub qemu_add_pci_bridge {
4511 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
4512
4513 my $bridges = {};
4514
4515 my $bridgeid;
4516
4517 print_pci_addr($device, $bridges, $arch, $machine_type);
4518
4519 while (my ($k, $v) = each %$bridges) {
4520 $bridgeid = $k;
4521 }
4522 return 1 if !defined($bridgeid) || $bridgeid < 1;
4523
4524 my $bridge = "pci.$bridgeid";
4525 my $devices_list = vm_devices_list($vmid);
4526
4527 if (!defined($devices_list->{$bridge})) {
4528 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
4529 }
4530
4531 return 1;
4532}
4533
4534sub qemu_set_link_status {
4535 my ($vmid, $device, $up) = @_;
4536
4537 mon_cmd($vmid, "set_link", name => $device,
4538 up => $up ? JSON::true : JSON::false);
4539}
4540
4541sub qemu_netdevadd {
4542 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
4543
4544 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
4545 my %options = split(/[=,]/, $netdev);
4546
4547 mon_cmd($vmid, "netdev_add", %options);
4548 return 1;
4549}
4550
4551sub qemu_netdevdel {
4552 my ($vmid, $deviceid) = @_;
4553
4554 mon_cmd($vmid, "netdev_del", id => $deviceid);
4555}
4556
4557sub qemu_usb_hotplug {
4558 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
4559
4560 return if !$device;
4561
4562 # remove the old one first
4563 vm_deviceunplug($vmid, $conf, $deviceid);
4564
4565 # check if xhci controller is necessary and available
4566 if ($device->{usb3}) {
4567
4568 my $devicelist = vm_devices_list($vmid);
4569
4570 if (!$devicelist->{xhci}) {
4571 my $pciaddr = print_pci_addr("xhci", undef, $arch, $machine_type);
4572 qemu_deviceadd($vmid, "nec-usb-xhci,id=xhci$pciaddr");
4573 }
4574 }
4575 my $d = parse_usb_device($device->{host});
4576 $d->{usb3} = $device->{usb3};
4577
4578 # add the new one
4579 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d, $arch, $machine_type);
4580}
4581
4582sub qemu_cpu_hotplug {
4583 my ($vmid, $conf, $vcpus) = @_;
4584
4585 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
4586
4587 my $sockets = 1;
4588 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4589 $sockets = $conf->{sockets} if $conf->{sockets};
4590 my $cores = $conf->{cores} || 1;
4591 my $maxcpus = $sockets * $cores;
4592
4593 $vcpus = $maxcpus if !$vcpus;
4594
4595 die "you can't add more vcpus than maxcpus\n"
4596 if $vcpus > $maxcpus;
4597
4598 my $currentvcpus = $conf->{vcpus} || $maxcpus;
4599
4600 if ($vcpus < $currentvcpus) {
4601
4602 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4603
4604 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4605 qemu_devicedel($vmid, "cpu$i");
4606 my $retry = 0;
4607 my $currentrunningvcpus = undef;
4608 while (1) {
4609 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4610 last if scalar(@{$currentrunningvcpus}) == $i-1;
4611 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
4612 $retry++;
4613 sleep 1;
4614 }
4615 #update conf after each succesfull cpu unplug
4616 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4617 PVE::QemuConfig->write_config($vmid, $conf);
4618 }
4619 } else {
4620 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
4621 }
4622
4623 return;
4624 }
4625
4626 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4627 die "vcpus in running vm does not match its configuration\n"
4628 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
4629
4630 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
4631
4632 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4633 my $cpustr = print_cpu_device($conf, $i);
4634 qemu_deviceadd($vmid, $cpustr);
4635
4636 my $retry = 0;
4637 my $currentrunningvcpus = undef;
4638 while (1) {
4639 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
4640 last if scalar(@{$currentrunningvcpus}) == $i;
4641 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
4642 sleep 1;
4643 $retry++;
4644 }
4645 #update conf after each succesfull cpu hotplug
4646 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4647 PVE::QemuConfig->write_config($vmid, $conf);
4648 }
4649 } else {
4650
4651 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
4652 mon_cmd($vmid, "cpu-add", id => int($i));
4653 }
4654 }
4655}
4656
4657sub qemu_block_set_io_throttle {
4658 my ($vmid, $deviceid,
4659 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
4660 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4661 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4662 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
4663
4664 return if !check_running($vmid) ;
4665
4666 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
4667 bps => int($bps),
4668 bps_rd => int($bps_rd),
4669 bps_wr => int($bps_wr),
4670 iops => int($iops),
4671 iops_rd => int($iops_rd),
4672 iops_wr => int($iops_wr),
4673 bps_max => int($bps_max),
4674 bps_rd_max => int($bps_rd_max),
4675 bps_wr_max => int($bps_wr_max),
4676 iops_max => int($iops_max),
4677 iops_rd_max => int($iops_rd_max),
4678 iops_wr_max => int($iops_wr_max),
4679 bps_max_length => int($bps_max_length),
4680 bps_rd_max_length => int($bps_rd_max_length),
4681 bps_wr_max_length => int($bps_wr_max_length),
4682 iops_max_length => int($iops_max_length),
4683 iops_rd_max_length => int($iops_rd_max_length),
4684 iops_wr_max_length => int($iops_wr_max_length),
4685 );
4686
4687}
4688
4689# old code, only used to shutdown old VM after update
4690sub __read_avail {
4691 my ($fh, $timeout) = @_;
4692
4693 my $sel = new IO::Select;
4694 $sel->add($fh);
4695
4696 my $res = '';
4697 my $buf;
4698
4699 my @ready;
4700 while (scalar (@ready = $sel->can_read($timeout))) {
4701 my $count;
4702 if ($count = $fh->sysread($buf, 8192)) {
4703 if ($buf =~ /^(.*)\(qemu\) $/s) {
4704 $res .= $1;
4705 last;
4706 } else {
4707 $res .= $buf;
4708 }
4709 } else {
4710 if (!defined($count)) {
4711 die "$!\n";
4712 }
4713 last;
4714 }
4715 }
4716
4717 die "monitor read timeout\n" if !scalar(@ready);
4718
4719 return $res;
4720}
4721
4722sub qemu_block_resize {
4723 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4724
4725 my $running = check_running($vmid);
4726
4727 $size = 0 if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
4728
4729 return if !$running;
4730
4731 my $padding = (1024 - $size % 1024) % 1024;
4732 $size = $size + $padding;
4733
4734 mon_cmd($vmid, "block_resize", device => $deviceid, size => int($size));
4735
4736}
4737
4738sub qemu_volume_snapshot {
4739 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4740
4741 my $running = check_running($vmid);
4742
4743 if ($running && do_snapshots_with_qemu($storecfg, $volid)){
4744 mon_cmd($vmid, 'blockdev-snapshot-internal-sync', device => $deviceid, name => $snap);
4745 } else {
4746 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4747 }
4748}
4749
4750sub qemu_volume_snapshot_delete {
4751 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4752
4753 my $running = check_running($vmid);
4754
4755 if($running) {
4756
4757 $running = undef;
4758 my $conf = PVE::QemuConfig->load_config($vmid);
4759 foreach_drive($conf, sub {
4760 my ($ds, $drive) = @_;
4761 $running = 1 if $drive->{file} eq $volid;
4762 });
4763 }
4764
4765 if ($running && do_snapshots_with_qemu($storecfg, $volid)){
4766 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync', device => $deviceid, name => $snap);
4767 } else {
4768 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4769 }
4770}
4771
4772sub set_migration_caps {
4773 my ($vmid) = @_;
4774
4775 my $cap_ref = [];
4776
4777 my $enabled_cap = {
4778 "auto-converge" => 1,
4779 "xbzrle" => 1,
4780 "x-rdma-pin-all" => 0,
4781 "zero-blocks" => 0,
4782 "compress" => 0
4783 };
4784
4785 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
4786
4787 for my $supported_capability (@$supported_capabilities) {
4788 push @$cap_ref, {
4789 capability => $supported_capability->{capability},
4790 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4791 };
4792 }
4793
4794 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
4795}
4796
4797my $fast_plug_option = {
4798 'lock' => 1,
4799 'name' => 1,
4800 'onboot' => 1,
4801 'shares' => 1,
4802 'startup' => 1,
4803 'description' => 1,
4804 'protection' => 1,
4805 'vmstatestorage' => 1,
4806 'hookscript' => 1,
4807 'tags' => 1,
4808};
4809
4810# hotplug changes in [PENDING]
4811# $selection hash can be used to only apply specified options, for
4812# example: { cores => 1 } (only apply changed 'cores')
4813# $errors ref is used to return error messages
4814sub vmconfig_hotplug_pending {
4815 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
4816
4817 my $defaults = load_defaults();
4818 my $arch = get_vm_arch($conf);
4819 my $machine_type = get_vm_machine($conf, undef, $arch);
4820
4821 # commit values which do not have any impact on running VM first
4822 # Note: those option cannot raise errors, we we do not care about
4823 # $selection and always apply them.
4824
4825 my $add_error = sub {
4826 my ($opt, $msg) = @_;
4827 $errors->{$opt} = "hotplug problem - $msg";
4828 };
4829
4830 my $changes = 0;
4831 foreach my $opt (keys %{$conf->{pending}}) { # add/change
4832 if ($fast_plug_option->{$opt}) {
4833 $conf->{$opt} = $conf->{pending}->{$opt};
4834 delete $conf->{pending}->{$opt};
4835 $changes = 1;
4836 }
4837 }
4838
4839 if ($changes) {
4840 PVE::QemuConfig->write_config($vmid, $conf);
4841 }
4842
4843 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
4844
4845 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
4846 foreach my $opt (sort keys %$pending_delete_hash) {
4847 next if $selection && !$selection->{$opt};
4848 my $force = $pending_delete_hash->{$opt}->{force};
4849 eval {
4850 if ($opt eq 'hotplug') {
4851 die "skip\n" if ($conf->{hotplug} =~ /memory/);
4852 } elsif ($opt eq 'tablet') {
4853 die "skip\n" if !$hotplug_features->{usb};
4854 if ($defaults->{tablet}) {
4855 vm_deviceplug($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
4856 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
4857 if $arch eq 'aarch64';
4858 } else {
4859 vm_deviceunplug($vmid, $conf, 'tablet');
4860 vm_deviceunplug($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
4861 }
4862 } elsif ($opt =~ m/^usb\d+/) {
4863 die "skip\n";
4864 # since we cannot reliably hot unplug usb devices
4865 # we are disabling it
4866 die "skip\n" if !$hotplug_features->{usb} || $conf->{$opt} =~ m/spice/i;
4867 vm_deviceunplug($vmid, $conf, $opt);
4868 } elsif ($opt eq 'vcpus') {
4869 die "skip\n" if !$hotplug_features->{cpu};
4870 qemu_cpu_hotplug($vmid, $conf, undef);
4871 } elsif ($opt eq 'balloon') {
4872 # enable balloon device is not hotpluggable
4873 die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
4874 # here we reset the ballooning value to memory
4875 my $balloon = $conf->{memory} || $defaults->{memory};
4876 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4877 } elsif ($fast_plug_option->{$opt}) {
4878 # do nothing
4879 } elsif ($opt =~ m/^net(\d+)$/) {
4880 die "skip\n" if !$hotplug_features->{network};
4881 vm_deviceunplug($vmid, $conf, $opt);
4882 } elsif (is_valid_drivename($opt)) {
4883 die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
4884 vm_deviceunplug($vmid, $conf, $opt);
4885 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4886 } elsif ($opt =~ m/^memory$/) {
4887 die "skip\n" if !$hotplug_features->{memory};
4888 PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
4889 } elsif ($opt eq 'cpuunits') {
4890 cgroups_write("cpu", $vmid, "cpu.shares", $defaults->{cpuunits});
4891 } elsif ($opt eq 'cpulimit') {
4892 cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", -1);
4893 } else {
4894 die "skip\n";
4895 }
4896 };
4897 if (my $err = $@) {
4898 &$add_error($opt, $err) if $err ne "skip\n";
4899 } else {
4900 delete $conf->{$opt};
4901 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
4902 }
4903 }
4904
4905 my ($apply_pending_cloudinit, $apply_pending_cloudinit_done);
4906 $apply_pending_cloudinit = sub {
4907 return if $apply_pending_cloudinit_done; # once is enough
4908 $apply_pending_cloudinit_done = 1; # once is enough
4909
4910 my ($key, $value) = @_;
4911
4912 my @cloudinit_opts = keys %$confdesc_cloudinit;
4913 foreach my $opt (keys %{$conf->{pending}}) {
4914 next if !grep { $_ eq $opt } @cloudinit_opts;
4915 $conf->{$opt} = delete $conf->{pending}->{$opt};
4916 }
4917
4918 my $new_conf = { %$conf };
4919 $new_conf->{$key} = $value;
4920 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($new_conf, $vmid);
4921 };
4922
4923 foreach my $opt (keys %{$conf->{pending}}) {
4924 next if $selection && !$selection->{$opt};
4925 my $value = $conf->{pending}->{$opt};
4926 eval {
4927 if ($opt eq 'hotplug') {
4928 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/);
4929 } elsif ($opt eq 'tablet') {
4930 die "skip\n" if !$hotplug_features->{usb};
4931 if ($value == 1) {
4932 vm_deviceplug($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
4933 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
4934 if $arch eq 'aarch64';
4935 } elsif ($value == 0) {
4936 vm_deviceunplug($vmid, $conf, 'tablet');
4937 vm_deviceunplug($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
4938 }
4939 } elsif ($opt =~ m/^usb\d+$/) {
4940 die "skip\n";
4941 # since we cannot reliably hot unplug usb devices
4942 # we are disabling it
4943 die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
4944 my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format}, $value) };
4945 die "skip\n" if !$d;
4946 qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d, $arch, $machine_type);
4947 } elsif ($opt eq 'vcpus') {
4948 die "skip\n" if !$hotplug_features->{cpu};
4949 qemu_cpu_hotplug($vmid, $conf, $value);
4950 } elsif ($opt eq 'balloon') {
4951 # enable/disable balloning device is not hotpluggable
4952 my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon});
4953 my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon});
4954 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
4955
4956 # allow manual ballooning if shares is set to zero
4957 if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
4958 my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
4959 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
4960 }
4961 } elsif ($opt =~ m/^net(\d+)$/) {
4962 # some changes can be done without hotplug
4963 vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
4964 $vmid, $opt, $value, $arch, $machine_type);
4965 } elsif (is_valid_drivename($opt)) {
4966 die "skip\n" if $opt eq 'efidisk0';
4967 # some changes can be done without hotplug
4968 my $drive = parse_drive($opt, $value);
4969 if (drive_is_cloudinit($drive)) {
4970 &$apply_pending_cloudinit($opt, $value);
4971 }
4972 vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
4973 $vmid, $opt, $value, $arch, $machine_type);
4974 } elsif ($opt =~ m/^memory$/) { #dimms
4975 die "skip\n" if !$hotplug_features->{memory};
4976 $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
4977 } elsif ($opt eq 'cpuunits') {
4978 cgroups_write("cpu", $vmid, "cpu.shares", $conf->{pending}->{$opt});
4979 } elsif ($opt eq 'cpulimit') {
4980 my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
4981 cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", $cpulimit);
4982 } else {
4983 die "skip\n"; # skip non-hot-pluggable options
4984 }
4985 };
4986 if (my $err = $@) {
4987 &$add_error($opt, $err) if $err ne "skip\n";
4988 } else {
4989 $conf->{$opt} = $value;
4990 delete $conf->{pending}->{$opt};
4991 }
4992 }
4993
4994 PVE::QemuConfig->write_config($vmid, $conf);
4995}
4996
4997sub try_deallocate_drive {
4998 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
4999
5000 if (($force || $key =~ /^unused/) && !drive_is_cdrom($drive, 1)) {
5001 my $volid = $drive->{file};
5002 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
5003 my $sid = PVE::Storage::parse_volume_id($volid);
5004 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
5005
5006 # check if the disk is really unused
5007 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
5008 if is_volume_in_use($storecfg, $conf, $key, $volid);
5009 PVE::Storage::vdisk_free($storecfg, $volid);
5010 return 1;
5011 } else {
5012 # If vm is not owner of this disk remove from config
5013 return 1;
5014 }
5015 }
5016
5017 return undef;
5018}
5019
5020sub vmconfig_delete_or_detach_drive {
5021 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
5022
5023 my $drive = parse_drive($opt, $conf->{$opt});
5024
5025 my $rpcenv = PVE::RPCEnvironment::get();
5026 my $authuser = $rpcenv->get_user();
5027
5028 if ($force) {
5029 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
5030 try_deallocate_drive($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
5031 } else {
5032 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $drive);
5033 }
5034}
5035
5036
5037
5038sub vmconfig_apply_pending {
5039 my ($vmid, $conf, $storecfg, $errors) = @_;
5040
5041 my $add_apply_error = sub {
5042 my ($opt, $msg) = @_;
5043 my $err_msg = "unable to apply pending change $opt : $msg";
5044 $errors->{$opt} = $err_msg;
5045 warn $err_msg;
5046 };
5047
5048 # cold plug
5049
5050 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
5051 foreach my $opt (sort keys %$pending_delete_hash) {
5052 my $force = $pending_delete_hash->{$opt}->{force};
5053 eval {
5054 if ($opt =~ m/^unused/) {
5055 die "internal error";
5056 } elsif (defined($conf->{$opt}) && is_valid_drivename($opt)) {
5057 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
5058 }
5059 };
5060 if (my $err = $@) {
5061 $add_apply_error->($opt, $err);
5062 } else {
5063 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
5064 delete $conf->{$opt};
5065 }
5066 }
5067
5068 PVE::QemuConfig->cleanup_pending($conf);
5069
5070 foreach my $opt (keys %{$conf->{pending}}) { # add/change
5071 next if $opt eq 'delete'; # just to be sure
5072 eval {
5073 if (defined($conf->{$opt}) && is_valid_drivename($opt)) {
5074 vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
5075 }
5076 };
5077 if (my $err = $@) {
5078 $add_apply_error->($opt, $err);
5079 } else {
5080 $conf->{$opt} = delete $conf->{pending}->{$opt};
5081 }
5082 }
5083
5084 # write all changes at once to avoid unnecessary i/o
5085 PVE::QemuConfig->write_config($vmid, $conf);
5086}
5087
5088my $safe_num_ne = sub {
5089 my ($a, $b) = @_;
5090
5091 return 0 if !defined($a) && !defined($b);
5092 return 1 if !defined($a);
5093 return 1 if !defined($b);
5094
5095 return $a != $b;
5096};
5097
5098my $safe_string_ne = sub {
5099 my ($a, $b) = @_;
5100
5101 return 0 if !defined($a) && !defined($b);
5102 return 1 if !defined($a);
5103 return 1 if !defined($b);
5104
5105 return $a ne $b;
5106};
5107
5108sub vmconfig_update_net {
5109 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5110
5111 my $newnet = parse_net($value);
5112
5113 if ($conf->{$opt}) {
5114 my $oldnet = parse_net($conf->{$opt});
5115
5116 if (&$safe_string_ne($oldnet->{model}, $newnet->{model}) ||
5117 &$safe_string_ne($oldnet->{macaddr}, $newnet->{macaddr}) ||
5118 &$safe_num_ne($oldnet->{queues}, $newnet->{queues}) ||
5119 !($newnet->{bridge} && $oldnet->{bridge})) { # bridge/nat mode change
5120
5121 # for non online change, we try to hot-unplug
5122 die "skip\n" if !$hotplug;
5123 vm_deviceunplug($vmid, $conf, $opt);
5124 } else {
5125
5126 die "internal error" if $opt !~ m/net(\d+)/;
5127 my $iface = "tap${vmid}i$1";
5128
5129 if (&$safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
5130 &$safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
5131 &$safe_string_ne($oldnet->{trunks}, $newnet->{trunks}) ||
5132 &$safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
5133 PVE::Network::tap_unplug($iface);
5134 PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
5135 } elsif (&$safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
5136 # Rate can be applied on its own but any change above needs to
5137 # include the rate in tap_plug since OVS resets everything.
5138 PVE::Network::tap_rate_limit($iface, $newnet->{rate});
5139 }
5140
5141 if (&$safe_string_ne($oldnet->{link_down}, $newnet->{link_down})) {
5142 qemu_set_link_status($vmid, $opt, !$newnet->{link_down});
5143 }
5144
5145 return 1;
5146 }
5147 }
5148
5149 if ($hotplug) {
5150 vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
5151 } else {
5152 die "skip\n";
5153 }
5154}
5155
5156sub vmconfig_update_disk {
5157 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
5158
5159 my $drive = parse_drive($opt, $value);
5160
5161 if ($conf->{$opt}) {
5162
5163 if (my $old_drive = parse_drive($opt, $conf->{$opt})) {
5164
5165 my $media = $drive->{media} || 'disk';
5166 my $oldmedia = $old_drive->{media} || 'disk';
5167 die "unable to change media type\n" if $media ne $oldmedia;
5168
5169 if (!drive_is_cdrom($old_drive)) {
5170
5171 if ($drive->{file} ne $old_drive->{file}) {
5172
5173 die "skip\n" if !$hotplug;
5174
5175 # unplug and register as unused
5176 vm_deviceunplug($vmid, $conf, $opt);
5177 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive)
5178
5179 } else {
5180 # update existing disk
5181
5182 # skip non hotpluggable value
5183 if (&$safe_string_ne($drive->{discard}, $old_drive->{discard}) ||
5184 &$safe_string_ne($drive->{iothread}, $old_drive->{iothread}) ||
5185 &$safe_string_ne($drive->{queues}, $old_drive->{queues}) ||
5186 &$safe_string_ne($drive->{cache}, $old_drive->{cache}) ||
5187 &$safe_string_ne($drive->{ssd}, $old_drive->{ssd})) {
5188 die "skip\n";
5189 }
5190
5191 # apply throttle
5192 if (&$safe_num_ne($drive->{mbps}, $old_drive->{mbps}) ||
5193 &$safe_num_ne($drive->{mbps_rd}, $old_drive->{mbps_rd}) ||
5194 &$safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) ||
5195 &$safe_num_ne($drive->{iops}, $old_drive->{iops}) ||
5196 &$safe_num_ne($drive->{iops_rd}, $old_drive->{iops_rd}) ||
5197 &$safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr}) ||
5198 &$safe_num_ne($drive->{mbps_max}, $old_drive->{mbps_max}) ||
5199 &$safe_num_ne($drive->{mbps_rd_max}, $old_drive->{mbps_rd_max}) ||
5200 &$safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) ||
5201 &$safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) ||
5202 &$safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) ||
5203 &$safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max}) ||
5204 &$safe_num_ne($drive->{bps_max_length}, $old_drive->{bps_max_length}) ||
5205 &$safe_num_ne($drive->{bps_rd_max_length}, $old_drive->{bps_rd_max_length}) ||
5206 &$safe_num_ne($drive->{bps_wr_max_length}, $old_drive->{bps_wr_max_length}) ||
5207 &$safe_num_ne($drive->{iops_max_length}, $old_drive->{iops_max_length}) ||
5208 &$safe_num_ne($drive->{iops_rd_max_length}, $old_drive->{iops_rd_max_length}) ||
5209 &$safe_num_ne($drive->{iops_wr_max_length}, $old_drive->{iops_wr_max_length})) {
5210
5211 qemu_block_set_io_throttle($vmid,"drive-$opt",
5212 ($drive->{mbps} || 0)*1024*1024,
5213 ($drive->{mbps_rd} || 0)*1024*1024,
5214 ($drive->{mbps_wr} || 0)*1024*1024,
5215 $drive->{iops} || 0,
5216 $drive->{iops_rd} || 0,
5217 $drive->{iops_wr} || 0,
5218 ($drive->{mbps_max} || 0)*1024*1024,
5219 ($drive->{mbps_rd_max} || 0)*1024*1024,
5220 ($drive->{mbps_wr_max} || 0)*1024*1024,
5221 $drive->{iops_max} || 0,
5222 $drive->{iops_rd_max} || 0,
5223 $drive->{iops_wr_max} || 0,
5224 $drive->{bps_max_length} || 1,
5225 $drive->{bps_rd_max_length} || 1,
5226 $drive->{bps_wr_max_length} || 1,
5227 $drive->{iops_max_length} || 1,
5228 $drive->{iops_rd_max_length} || 1,
5229 $drive->{iops_wr_max_length} || 1);
5230
5231 }
5232
5233 return 1;
5234 }
5235
5236 } else { # cdrom
5237
5238 if ($drive->{file} eq 'none') {
5239 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
5240 if (drive_is_cloudinit($old_drive)) {
5241 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive);
5242 }
5243 } else {
5244 my $path = get_iso_path($storecfg, $vmid, $drive->{file});
5245
5246 # force eject if locked
5247 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
5248
5249 if ($path) {
5250 mon_cmd($vmid, "blockdev-change-medium",
5251 id => "$opt", filename => "$path");
5252 }
5253 }
5254
5255 return 1;
5256 }
5257 }
5258 }
5259
5260 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
5261 # hotplug new disks
5262 PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
5263 vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
5264}
5265
5266sub vm_start {
5267 my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused,
5268 $forcemachine, $spice_ticket, $migration_network, $migration_type, $targetstorage, $timeout) = @_;
5269
5270 PVE::QemuConfig->lock_config($vmid, sub {
5271 my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom);
5272
5273 die "you can't start a vm if it's a template\n" if PVE::QemuConfig->is_template($conf);
5274
5275 my $is_suspended = PVE::QemuConfig->has_lock($conf, 'suspended');
5276
5277 PVE::QemuConfig->check_lock($conf)
5278 if !($skiplock || $is_suspended);
5279
5280 die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
5281
5282 # clean up leftover reboot request files
5283 eval { clear_reboot_request($vmid); };
5284 warn $@ if $@;
5285
5286 if (!$statefile && scalar(keys %{$conf->{pending}})) {
5287 vmconfig_apply_pending($vmid, $conf, $storecfg);
5288 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
5289 }
5290
5291 PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
5292
5293 my $defaults = load_defaults();
5294
5295 # set environment variable useful inside network script
5296 $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
5297
5298 my $local_volumes = {};
5299
5300 if ($targetstorage) {
5301 foreach_drive($conf, sub {
5302 my ($ds, $drive) = @_;
5303
5304 return if drive_is_cdrom($drive);
5305
5306 my $volid = $drive->{file};
5307
5308 return if !$volid;
5309
5310 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
5311
5312 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5313 return if $scfg->{shared};
5314 $local_volumes->{$ds} = [$volid, $storeid, $volname];
5315 });
5316
5317 my $format = undef;
5318
5319 foreach my $opt (sort keys %$local_volumes) {
5320
5321 my ($volid, $storeid, $volname) = @{$local_volumes->{$opt}};
5322 my $drive = parse_drive($opt, $conf->{$opt});
5323
5324 # If a remote storage is specified and the format of the original
5325 # volume is not available there, fall back to the default format.
5326 # Otherwise use the same format as the original.
5327 if ($targetstorage && $targetstorage ne "1") {
5328 $storeid = $targetstorage;
5329 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
5330 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5331 my $fileFormat = qemu_img_format($scfg, $volname);
5332 $format = (grep {$fileFormat eq $_} @{$validFormats}) ? $fileFormat : $defFormat;
5333 } else {
5334 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5335 $format = qemu_img_format($scfg, $volname);
5336 }
5337
5338 my $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $format, undef, ($drive->{size}/1024));
5339 my $newdrive = $drive;
5340 $newdrive->{format} = $format;
5341 $newdrive->{file} = $newvolid;
5342 my $drivestr = print_drive($newdrive);
5343 $local_volumes->{$opt} = $drivestr;
5344 #pass drive to conf for command line
5345 $conf->{$opt} = $drivestr;
5346 }
5347 }
5348
5349 PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-start', 1);
5350
5351 if ($is_suspended) {
5352 # enforce machine type on suspended vm to ensure HW compatibility
5353 $forcemachine = $conf->{runningmachine};
5354 print "Resuming suspended VM\n";
5355 }
5356
5357 my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
5358
5359 my $migration_ip;
5360 my $get_migration_ip = sub {
5361 my ($cidr, $nodename) = @_;
5362
5363 return $migration_ip if defined($migration_ip);
5364
5365 if (!defined($cidr)) {
5366 my $dc_conf = PVE::Cluster::cfs_read_file('datacenter.cfg');
5367 $cidr = $dc_conf->{migration}->{network};
5368 }
5369
5370 if (defined($cidr)) {
5371 my $ips = PVE::Network::get_local_ip_from_cidr($cidr);
5372
5373 die "could not get IP: no address configured on local " .
5374 "node for network '$cidr'\n" if scalar(@$ips) == 0;
5375
5376 die "could not get IP: multiple addresses configured on local " .
5377 "node for network '$cidr'\n" if scalar(@$ips) > 1;
5378
5379 $migration_ip = @$ips[0];
5380 }
5381
5382 $migration_ip = PVE::Cluster::remote_node_ip($nodename, 1)
5383 if !defined($migration_ip);
5384
5385 return $migration_ip;
5386 };
5387
5388 my $migrate_uri;
5389 if ($statefile) {
5390 if ($statefile eq 'tcp') {
5391 my $localip = "localhost";
5392 my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter.cfg');
5393 my $nodename = nodename();
5394
5395 if (!defined($migration_type)) {
5396 if (defined($datacenterconf->{migration}->{type})) {
5397 $migration_type = $datacenterconf->{migration}->{type};
5398 } else {
5399 $migration_type = 'secure';
5400 }
5401 }
5402
5403 if ($migration_type eq 'insecure') {
5404 $localip = $get_migration_ip->($migration_network, $nodename);
5405 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5406 }
5407
5408 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5409 my $migrate_port = PVE::Tools::next_migrate_port($pfamily);
5410 $migrate_uri = "tcp:${localip}:${migrate_port}";
5411 push @$cmd, '-incoming', $migrate_uri;
5412 push @$cmd, '-S';
5413
5414 } elsif ($statefile eq 'unix') {
5415 # should be default for secure migrations as a ssh TCP forward
5416 # tunnel is not deterministic reliable ready and fails regurarly
5417 # to set up in time, so use UNIX socket forwards
5418 my $socket_addr = "/run/qemu-server/$vmid.migrate";
5419 unlink $socket_addr;
5420
5421 $migrate_uri = "unix:$socket_addr";
5422
5423 push @$cmd, '-incoming', $migrate_uri;
5424 push @$cmd, '-S';
5425
5426 } elsif (-e $statefile) {
5427 push @$cmd, '-loadstate', $statefile;
5428 } else {
5429 my $statepath = PVE::Storage::path($storecfg, $statefile);
5430 push @$vollist, $statefile;
5431 push @$cmd, '-loadstate', $statepath;
5432 }
5433 } elsif ($paused) {
5434 push @$cmd, '-S';
5435 }
5436
5437 # host pci devices
5438 for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) {
5439 my $d = parse_hostpci($conf->{"hostpci$i"});
5440 next if !$d;
5441 my $pcidevices = $d->{pciid};
5442 foreach my $pcidevice (@$pcidevices) {
5443 my $pciid = $pcidevice->{id};
5444
5445 my $info = PVE::SysFSTools::pci_device_info("$pciid");
5446 die "IOMMU not present\n" if !PVE::SysFSTools::check_iommu_support();
5447 die "no pci device info for device '$pciid'\n" if !$info;
5448
5449 if ($d->{mdev}) {
5450 my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $i);
5451 PVE::SysFSTools::pci_create_mdev_device($pciid, $uuid, $d->{mdev});
5452 } else {
5453 die "can't unbind/bind pci group to vfio '$pciid'\n"
5454 if !PVE::SysFSTools::pci_dev_group_bind_to_vfio($pciid);
5455 die "can't reset pci device '$pciid'\n"
5456 if $info->{has_fl_reset} and !PVE::SysFSTools::pci_dev_reset($info);
5457 }
5458 }
5459 }
5460
5461 PVE::Storage::activate_volumes($storecfg, $vollist);
5462
5463 eval {
5464 run_command(['/bin/systemctl', 'stop', "$vmid.scope"],
5465 outfunc => sub {}, errfunc => sub {});
5466 };
5467 # Issues with the above 'stop' not being fully completed are extremely rare, a very low
5468 # timeout should be more than enough here...
5469 PVE::Systemd::wait_for_unit_removed("$vmid.scope", 5);
5470
5471 my $cpuunits = defined($conf->{cpuunits}) ? $conf->{cpuunits}
5472 : $defaults->{cpuunits};
5473
5474 my $start_timeout = $timeout // config_aware_timeout($conf, $is_suspended);
5475 my %run_params = (
5476 timeout => $statefile ? undef : $start_timeout,
5477 umask => 0077,
5478 noerr => 1,
5479 );
5480
5481 # when migrating, prefix QEMU output so other side can pick up any
5482 # errors that might occur and show the user
5483 if ($migratedfrom) {
5484 $run_params{quiet} = 1;
5485 $run_params{logfunc} = sub { print "QEMU: $_[0]\n" };
5486 }
5487
5488 my %properties = (
5489 Slice => 'qemu.slice',
5490 KillMode => 'none',
5491 CPUShares => $cpuunits
5492 );
5493
5494 if (my $cpulimit = $conf->{cpulimit}) {
5495 $properties{CPUQuota} = int($cpulimit * 100);
5496 }
5497 $properties{timeout} = 10 if $statefile; # setting up the scope shoul be quick
5498
5499 my $run_qemu = sub {
5500 PVE::Tools::run_fork sub {
5501 PVE::Systemd::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %properties);
5502
5503 my $exitcode = run_command($cmd, %run_params);
5504 die "QEMU exited with code $exitcode\n" if $exitcode;
5505 };
5506 };
5507
5508 if ($conf->{hugepages}) {
5509
5510 my $code = sub {
5511 my $hugepages_topology = PVE::QemuServer::Memory::hugepages_topology($conf);
5512 my $hugepages_host_topology = PVE::QemuServer::Memory::hugepages_host_topology();
5513
5514 PVE::QemuServer::Memory::hugepages_mount();
5515 PVE::QemuServer::Memory::hugepages_allocate($hugepages_topology, $hugepages_host_topology);
5516
5517 eval { $run_qemu->() };
5518 if (my $err = $@) {
5519 PVE::QemuServer::Memory::hugepages_reset($hugepages_host_topology);
5520 die $err;
5521 }
5522
5523 PVE::QemuServer::Memory::hugepages_pre_deallocate($hugepages_topology);
5524 };
5525 eval { PVE::QemuServer::Memory::hugepages_update_locked($code); };
5526
5527 } else {
5528 eval { $run_qemu->() };
5529 }
5530
5531 if (my $err = $@) {
5532 # deactivate volumes if start fails
5533 eval { PVE::Storage::deactivate_volumes($storecfg, $vollist); };
5534 die "start failed: $err";
5535 }
5536
5537 print "migration listens on $migrate_uri\n" if $migrate_uri;
5538
5539 if ($statefile && $statefile ne 'tcp' && $statefile ne 'unix') {
5540 eval { mon_cmd($vmid, "cont"); };
5541 warn $@ if $@;
5542 }
5543
5544 #start nbd server for storage migration
5545 if ($targetstorage) {
5546 my $nodename = nodename();
5547 my $localip = $get_migration_ip->($migration_network, $nodename);
5548 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5549 my $storage_migrate_port = PVE::Tools::next_migrate_port($pfamily);
5550
5551 mon_cmd($vmid, "nbd-server-start", addr => { type => 'inet', data => { host => "${localip}", port => "${storage_migrate_port}" } } );
5552
5553 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5554
5555 foreach my $opt (sort keys %$local_volumes) {
5556 my $drivestr = $local_volumes->{$opt};
5557 mon_cmd($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
5558 my $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}:exportname=drive-$opt";
5559 print "storage migration listens on $migrate_storage_uri volume:$drivestr\n";
5560 }
5561 }
5562
5563 if ($migratedfrom) {
5564 eval {
5565 set_migration_caps($vmid);
5566 };
5567 warn $@ if $@;
5568
5569 if ($spice_port) {
5570 print "spice listens on port $spice_port\n";
5571 if ($spice_ticket) {
5572 mon_cmd($vmid, "set_password", protocol => 'spice', password => $spice_ticket);
5573 mon_cmd($vmid, "expire_password", protocol => 'spice', time => "+30");
5574 }
5575 }
5576
5577 } else {
5578 mon_cmd($vmid, "balloon", value => $conf->{balloon}*1024*1024)
5579 if !$statefile && $conf->{balloon};
5580
5581 foreach my $opt (keys %$conf) {
5582 next if $opt !~ m/^net\d+$/;
5583 my $nicconf = parse_net($conf->{$opt});
5584 qemu_set_link_status($vmid, $opt, 0) if $nicconf->{link_down};
5585 }
5586 }
5587
5588 mon_cmd($vmid, 'qom-set',
5589 path => "machine/peripheral/balloon0",
5590 property => "guest-stats-polling-interval",
5591 value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
5592
5593 if ($is_suspended) {
5594 print "Resumed VM, removing state\n";
5595 if (my $vmstate = $conf->{vmstate}) {
5596 PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
5597 PVE::Storage::vdisk_free($storecfg, $vmstate);
5598 }
5599 delete $conf->@{qw(lock vmstate runningmachine)};
5600 PVE::QemuConfig->write_config($vmid, $conf);
5601 }
5602
5603 PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'post-start');
5604 });
5605}
5606
5607sub vm_commandline {
5608 my ($storecfg, $vmid, $snapname) = @_;
5609
5610 my $conf = PVE::QemuConfig->load_config($vmid);
5611 my $forcemachine;
5612
5613 if ($snapname) {
5614 my $snapshot = $conf->{snapshots}->{$snapname};
5615 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
5616
5617 # check for a 'runningmachine' in snapshot
5618 $forcemachine = $snapshot->{runningmachine} if $snapshot->{runningmachine};
5619
5620 $snapshot->{digest} = $conf->{digest}; # keep file digest for API
5621
5622 $conf = $snapshot;
5623 }
5624
5625 my $defaults = load_defaults();
5626
5627 my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine);
5628
5629 return PVE::Tools::cmd2string($cmd);
5630}
5631
5632sub vm_reset {
5633 my ($vmid, $skiplock) = @_;
5634
5635 PVE::QemuConfig->lock_config($vmid, sub {
5636
5637 my $conf = PVE::QemuConfig->load_config($vmid);
5638
5639 PVE::QemuConfig->check_lock($conf) if !$skiplock;
5640
5641 mon_cmd($vmid, "system_reset");
5642 });
5643}
5644
5645sub get_vm_volumes {
5646 my ($conf) = @_;
5647
5648 my $vollist = [];
5649 foreach_volid($conf, sub {
5650 my ($volid, $attr) = @_;
5651
5652 return if $volid =~ m|^/|;
5653
5654 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
5655 return if !$sid;
5656
5657 push @$vollist, $volid;
5658 });
5659
5660 return $vollist;
5661}
5662
5663sub vm_stop_cleanup {
5664 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
5665
5666 eval {
5667
5668 if (!$keepActive) {
5669 my $vollist = get_vm_volumes($conf);
5670 PVE::Storage::deactivate_volumes($storecfg, $vollist);
5671 }
5672
5673 foreach my $ext (qw(mon qmp pid vnc qga)) {
5674 unlink "/var/run/qemu-server/${vmid}.$ext";
5675 }
5676
5677 if ($conf->{ivshmem}) {
5678 my $ivshmem = PVE::JSONSchema::parse_property_string($ivshmem_fmt, $conf->{ivshmem});
5679 # just delete it for now, VMs which have this already open do not
5680 # are affected, but new VMs will get a separated one. If this
5681 # becomes an issue we either add some sort of ref-counting or just
5682 # add a "don't delete on stop" flag to the ivshmem format.
5683 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name} // $vmid);
5684 }
5685
5686 foreach my $key (keys %$conf) {
5687 next if $key !~ m/^hostpci(\d+)$/;
5688 my $hostpciindex = $1;
5689 my $d = parse_hostpci($conf->{$key});
5690 my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $hostpciindex);
5691
5692 foreach my $pci (@{$d->{pciid}}) {
5693 my $pciid = $pci->{id};
5694 PVE::SysFSTools::pci_cleanup_mdev_device($pciid, $uuid);
5695 }
5696 }
5697
5698 vmconfig_apply_pending($vmid, $conf, $storecfg) if $apply_pending_changes;
5699 };
5700 warn $@ if $@; # avoid errors - just warn
5701}
5702
5703# call only in locked context
5704sub _do_vm_stop {
5705 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
5706
5707 my $pid = check_running($vmid, $nocheck);
5708 return if !$pid;
5709
5710 my $conf;
5711 if (!$nocheck) {
5712 $conf = PVE::QemuConfig->load_config($vmid);
5713 PVE::QemuConfig->check_lock($conf) if !$skiplock;
5714 if (!defined($timeout) && $shutdown && $conf->{startup}) {
5715 my $opts = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
5716 $timeout = $opts->{down} if $opts->{down};
5717 }
5718 PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-stop');
5719 }
5720
5721 eval {
5722 if ($shutdown) {
5723 if (defined($conf) && parse_guest_agent($conf)->{enabled}) {
5724 mon_cmd($vmid, "guest-shutdown", timeout => $timeout);
5725 } else {
5726 mon_cmd($vmid, "system_powerdown");
5727 }
5728 } else {
5729 mon_cmd($vmid, "quit");
5730 }
5731 };
5732 my $err = $@;
5733
5734 if (!$err) {
5735 $timeout = 60 if !defined($timeout);
5736
5737 my $count = 0;
5738 while (($count < $timeout) && check_running($vmid, $nocheck)) {
5739 $count++;
5740 sleep 1;
5741 }
5742
5743 if ($count >= $timeout) {
5744 if ($force) {
5745 warn "VM still running - terminating now with SIGTERM\n";
5746 kill 15, $pid;
5747 } else {
5748 die "VM quit/powerdown failed - got timeout\n";
5749 }
5750 } else {
5751 vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5752 return;
5753 }
5754 } else {
5755 if ($force) {
5756 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
5757 kill 15, $pid;
5758 } else {
5759 die "VM quit/powerdown failed\n";
5760 }
5761 }
5762
5763 # wait again
5764 $timeout = 10;
5765
5766 my $count = 0;
5767 while (($count < $timeout) && check_running($vmid, $nocheck)) {
5768 $count++;
5769 sleep 1;
5770 }
5771
5772 if ($count >= $timeout) {
5773 warn "VM still running - terminating now with SIGKILL\n";
5774 kill 9, $pid;
5775 sleep 1;
5776 }
5777
5778 vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
5779}
5780
5781# Note: use $nocheck to skip tests if VM configuration file exists.
5782# We need that when migration VMs to other nodes (files already moved)
5783# Note: we set $keepActive in vzdump stop mode - volumes need to stay active
5784sub vm_stop {
5785 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
5786
5787 $force = 1 if !defined($force) && !$shutdown;
5788
5789 if ($migratedfrom){
5790 my $pid = check_running($vmid, $nocheck, $migratedfrom);
5791 kill 15, $pid if $pid;
5792 my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom);
5793 vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 0);
5794 return;
5795 }
5796
5797 PVE::QemuConfig->lock_config($vmid, sub {
5798 _do_vm_stop($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
5799 });
5800}
5801
5802sub vm_reboot {
5803 my ($vmid, $timeout) = @_;
5804
5805 PVE::QemuConfig->lock_config($vmid, sub {
5806 eval {
5807
5808 # only reboot if running, as qmeventd starts it again on a stop event
5809 return if !check_running($vmid);
5810
5811 create_reboot_request($vmid);
5812
5813 my $storecfg = PVE::Storage::config();
5814 _do_vm_stop($storecfg, $vmid, undef, undef, $timeout, 1);
5815
5816 };
5817 if (my $err = $@) {
5818 # avoid that the next normal shutdown will be confused for a reboot
5819 clear_reboot_request($vmid);
5820 die $err;
5821 }
5822 });
5823}
5824
5825# note: if using the statestorage parameter, the caller has to check privileges
5826sub vm_suspend {
5827 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
5828
5829 my $conf;
5830 my $path;
5831 my $storecfg;
5832 my $vmstate;
5833
5834 PVE::QemuConfig->lock_config($vmid, sub {
5835
5836 $conf = PVE::QemuConfig->load_config($vmid);
5837
5838 my $is_backing_up = PVE::QemuConfig->has_lock($conf, 'backup');
5839 PVE::QemuConfig->check_lock($conf)
5840 if !($skiplock || $is_backing_up);
5841
5842 die "cannot suspend to disk during backup\n"
5843 if $is_backing_up && $includestate;
5844
5845 if ($includestate) {
5846 $conf->{lock} = 'suspending';
5847 my $date = strftime("%Y-%m-%d", localtime(time()));
5848 $storecfg = PVE::Storage::config();
5849 if (!$statestorage) {
5850 $statestorage = find_vmstate_storage($conf, $storecfg);
5851 # check permissions for the storage
5852 my $rpcenv = PVE::RPCEnvironment::get();
5853 if ($rpcenv->{type} ne 'cli') {
5854 my $authuser = $rpcenv->get_user();
5855 $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
5856 }
5857 }
5858
5859
5860 $vmstate = PVE::QemuConfig->__snapshot_save_vmstate($vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
5861 $path = PVE::Storage::path($storecfg, $vmstate);
5862 PVE::QemuConfig->write_config($vmid, $conf);
5863 } else {
5864 mon_cmd($vmid, "stop");
5865 }
5866 });
5867
5868 if ($includestate) {
5869 # save vm state
5870 PVE::Storage::activate_volumes($storecfg, [$vmstate]);
5871
5872 eval {
5873 mon_cmd($vmid, "savevm-start", statefile => $path);
5874 for(;;) {
5875 my $state = mon_cmd($vmid, "query-savevm");
5876 if (!$state->{status}) {
5877 die "savevm not active\n";
5878 } elsif ($state->{status} eq 'active') {
5879 sleep(1);
5880 next;
5881 } elsif ($state->{status} eq 'completed') {
5882 print "State saved, quitting\n";
5883 last;
5884 } elsif ($state->{status} eq 'failed' && $state->{error}) {
5885 die "query-savevm failed with error '$state->{error}'\n"
5886 } else {
5887 die "query-savevm returned status '$state->{status}'\n";
5888 }
5889 }
5890 };
5891 my $err = $@;
5892
5893 PVE::QemuConfig->lock_config($vmid, sub {
5894 $conf = PVE::QemuConfig->load_config($vmid);
5895 if ($err) {
5896 # cleanup, but leave suspending lock, to indicate something went wrong
5897 eval {
5898 mon_cmd($vmid, "savevm-end");
5899 PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
5900 PVE::Storage::vdisk_free($storecfg, $vmstate);
5901 delete $conf->@{qw(vmstate runningmachine)};
5902 PVE::QemuConfig->write_config($vmid, $conf);
5903 };
5904 warn $@ if $@;
5905 die $err;
5906 }
5907
5908 die "lock changed unexpectedly\n"
5909 if !PVE::QemuConfig->has_lock($conf, 'suspending');
5910
5911 mon_cmd($vmid, "quit");
5912 $conf->{lock} = 'suspended';
5913 PVE::QemuConfig->write_config($vmid, $conf);
5914 });
5915 }
5916}
5917
5918sub vm_resume {
5919 my ($vmid, $skiplock, $nocheck) = @_;
5920
5921 PVE::QemuConfig->lock_config($vmid, sub {
5922 my $res = mon_cmd($vmid, 'query-status');
5923 my $resume_cmd = 'cont';
5924
5925 if ($res->{status} && $res->{status} eq 'suspended') {
5926 $resume_cmd = 'system_wakeup';
5927 }
5928
5929 if (!$nocheck) {
5930
5931 my $conf = PVE::QemuConfig->load_config($vmid);
5932
5933 PVE::QemuConfig->check_lock($conf)
5934 if !($skiplock || PVE::QemuConfig->has_lock($conf, 'backup'));
5935 }
5936
5937 mon_cmd($vmid, $resume_cmd);
5938 });
5939}
5940
5941sub vm_sendkey {
5942 my ($vmid, $skiplock, $key) = @_;
5943
5944 PVE::QemuConfig->lock_config($vmid, sub {
5945
5946 my $conf = PVE::QemuConfig->load_config($vmid);
5947
5948 # there is no qmp command, so we use the human monitor command
5949 my $res = PVE::QemuServer::Monitor::hmp_cmd($vmid, "sendkey $key");
5950 die $res if $res ne '';
5951 });
5952}
5953
5954# vzdump restore implementaion
5955
5956sub tar_archive_read_firstfile {
5957 my $archive = shift;
5958
5959 die "ERROR: file '$archive' does not exist\n" if ! -f $archive;
5960
5961 # try to detect archive type first
5962 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
5963 die "unable to open file '$archive'\n";
5964 my $firstfile = <$fh>;
5965 kill 15, $pid;
5966 close $fh;
5967
5968 die "ERROR: archive contaions no data\n" if !$firstfile;
5969 chomp $firstfile;
5970
5971 return $firstfile;
5972}
5973
5974sub tar_restore_cleanup {
5975 my ($storecfg, $statfile) = @_;
5976
5977 print STDERR "starting cleanup\n";
5978
5979 if (my $fd = IO::File->new($statfile, "r")) {
5980 while (defined(my $line = <$fd>)) {
5981 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
5982 my $volid = $2;
5983 eval {
5984 if ($volid =~ m|^/|) {
5985 unlink $volid || die 'unlink failed\n';
5986 } else {
5987 PVE::Storage::vdisk_free($storecfg, $volid);
5988 }
5989 print STDERR "temporary volume '$volid' sucessfuly removed\n";
5990 };
5991 print STDERR "unable to cleanup '$volid' - $@" if $@;
5992 } else {
5993 print STDERR "unable to parse line in statfile - $line";
5994 }
5995 }
5996 $fd->close();
5997 }
5998}
5999
6000sub restore_archive {
6001 my ($archive, $vmid, $user, $opts) = @_;
6002
6003 my $format = $opts->{format};
6004 my $comp;
6005
6006 if ($archive =~ m/\.tgz$/ || $archive =~ m/\.tar\.gz$/) {
6007 $format = 'tar' if !$format;
6008 $comp = 'gzip';
6009 } elsif ($archive =~ m/\.tar$/) {
6010 $format = 'tar' if !$format;
6011 } elsif ($archive =~ m/.tar.lzo$/) {
6012 $format = 'tar' if !$format;
6013 $comp = 'lzop';
6014 } elsif ($archive =~ m/\.vma$/) {
6015 $format = 'vma' if !$format;
6016 } elsif ($archive =~ m/\.vma\.gz$/) {
6017 $format = 'vma' if !$format;
6018 $comp = 'gzip';
6019 } elsif ($archive =~ m/\.vma\.lzo$/) {
6020 $format = 'vma' if !$format;
6021 $comp = 'lzop';
6022 } else {
6023 $format = 'vma' if !$format; # default
6024 }
6025
6026 # try to detect archive format
6027 if ($format eq 'tar') {
6028 return restore_tar_archive($archive, $vmid, $user, $opts);
6029 } else {
6030 return restore_vma_archive($archive, $vmid, $user, $opts, $comp);
6031 }
6032}
6033
6034sub restore_update_config_line {
6035 my ($outfd, $cookie, $vmid, $map, $line, $unique) = @_;
6036
6037 return if $line =~ m/^\#qmdump\#/;
6038 return if $line =~ m/^\#vzdump\#/;
6039 return if $line =~ m/^lock:/;
6040 return if $line =~ m/^unused\d+:/;
6041 return if $line =~ m/^parent:/;
6042
6043 my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
6044 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
6045 # try to convert old 1.X settings
6046 my ($id, $ind, $ethcfg) = ($1, $2, $3);
6047 foreach my $devconfig (PVE::Tools::split_list($ethcfg)) {
6048 my ($model, $macaddr) = split(/\=/, $devconfig);
6049 $macaddr = PVE::Tools::random_ether_addr($dc->{mac_prefix}) if !$macaddr || $unique;
6050 my $net = {
6051 model => $model,
6052 bridge => "vmbr$ind",
6053 macaddr => $macaddr,
6054 };
6055 my $netstr = print_net($net);
6056
6057 print $outfd "net$cookie->{netcount}: $netstr\n";
6058 $cookie->{netcount}++;
6059 }
6060 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
6061 my ($id, $netstr) = ($1, $2);
6062 my $net = parse_net($netstr);
6063 $net->{macaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix}) if $net->{macaddr};
6064 $netstr = print_net($net);
6065 print $outfd "$id: $netstr\n";
6066 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk)\d+):\s*(\S+)\s*$/) {
6067 my $virtdev = $1;
6068 my $value = $3;
6069 my $di = parse_drive($virtdev, $value);
6070 if (defined($di->{backup}) && !$di->{backup}) {
6071 print $outfd "#$line";
6072 } elsif ($map->{$virtdev}) {
6073 delete $di->{format}; # format can change on restore
6074 $di->{file} = $map->{$virtdev};
6075 $value = print_drive($di);
6076 print $outfd "$virtdev: $value\n";
6077 } else {
6078 print $outfd $line;
6079 }
6080 } elsif (($line =~ m/^vmgenid: (.*)/)) {
6081 my $vmgenid = $1;
6082 if ($vmgenid ne '0') {
6083 # always generate a new vmgenid if there was a valid one setup
6084 $vmgenid = generate_uuid();
6085 }
6086 print $outfd "vmgenid: $vmgenid\n";
6087 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
6088 my ($uuid, $uuid_str);
6089 UUID::generate($uuid);
6090 UUID::unparse($uuid, $uuid_str);
6091 my $smbios1 = parse_smbios1($2);
6092 $smbios1->{uuid} = $uuid_str;
6093 print $outfd $1.print_smbios1($smbios1)."\n";
6094 } else {
6095 print $outfd $line;
6096 }
6097}
6098
6099sub scan_volids {
6100 my ($cfg, $vmid) = @_;
6101
6102 my $info = PVE::Storage::vdisk_list($cfg, undef, $vmid);
6103
6104 my $volid_hash = {};
6105 foreach my $storeid (keys %$info) {
6106 foreach my $item (@{$info->{$storeid}}) {
6107 next if !($item->{volid} && $item->{size});
6108 $item->{path} = PVE::Storage::path($cfg, $item->{volid});
6109 $volid_hash->{$item->{volid}} = $item;
6110 }
6111 }
6112
6113 return $volid_hash;
6114}
6115
6116sub is_volume_in_use {
6117 my ($storecfg, $conf, $skip_drive, $volid) = @_;
6118
6119 my $path = PVE::Storage::path($storecfg, $volid);
6120
6121 my $scan_config = sub {
6122 my ($cref, $snapname) = @_;
6123
6124 foreach my $key (keys %$cref) {
6125 my $value = $cref->{$key};
6126 if (is_valid_drivename($key)) {
6127 next if $skip_drive && $key eq $skip_drive;
6128 my $drive = parse_drive($key, $value);
6129 next if !$drive || !$drive->{file} || drive_is_cdrom($drive);
6130 return 1 if $volid eq $drive->{file};
6131 if ($drive->{file} =~ m!^/!) {
6132 return 1 if $drive->{file} eq $path;
6133 } else {
6134 my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1);
6135 next if !$storeid;
6136 my $scfg = PVE::Storage::storage_config($storecfg, $storeid, 1);
6137 next if !$scfg;
6138 return 1 if $path eq PVE::Storage::path($storecfg, $drive->{file}, $snapname);
6139 }
6140 }
6141 }
6142
6143 return 0;
6144 };
6145
6146 return 1 if &$scan_config($conf);
6147
6148 undef $skip_drive;
6149
6150 foreach my $snapname (keys %{$conf->{snapshots}}) {
6151 return 1 if &$scan_config($conf->{snapshots}->{$snapname}, $snapname);
6152 }
6153
6154 return 0;
6155}
6156
6157sub update_disksize {
6158 my ($drive, $volid_hash) = @_;
6159
6160 my $volid = $drive->{file};
6161 return undef if !defined($volid);
6162
6163 my $oldsize = $drive->{size};
6164 my $newsize = $volid_hash->{$volid}->{size};
6165
6166 if (defined($newsize) && defined($oldsize) && $newsize != $oldsize) {
6167 $drive->{size} = $newsize;
6168
6169 my $old_fmt = PVE::JSONSchema::format_size($oldsize);
6170 my $new_fmt = PVE::JSONSchema::format_size($newsize);
6171
6172 return wantarray ? ($drive, $old_fmt, $new_fmt) : $drive;
6173 }
6174
6175 return undef;
6176}
6177
6178sub update_disk_config {
6179 my ($vmid, $conf, $volid_hash) = @_;
6180
6181 my $changes;
6182 my $prefix = "VM $vmid:";
6183
6184 # used and unused disks
6185 my $referenced = {};
6186
6187 # Note: it is allowed to define multiple storages with same path (alias), so
6188 # we need to check both 'volid' and real 'path' (two different volid can point
6189 # to the same path).
6190
6191 my $referencedpath = {};
6192
6193 # update size info
6194 foreach my $opt (keys %$conf) {
6195 if (is_valid_drivename($opt)) {
6196 my $drive = parse_drive($opt, $conf->{$opt});
6197 my $volid = $drive->{file};
6198 next if !$volid;
6199
6200 # mark volid as "in-use" for next step
6201 $referenced->{$volid} = 1;
6202 if ($volid_hash->{$volid} &&
6203 (my $path = $volid_hash->{$volid}->{path})) {
6204 $referencedpath->{$path} = 1;
6205 }
6206
6207 next if drive_is_cdrom($drive);
6208 next if !$volid_hash->{$volid};
6209
6210 my ($updated, $old_size, $new_size) = update_disksize($drive, $volid_hash);
6211 if (defined($updated)) {
6212 $changes = 1;
6213 $conf->{$opt} = print_drive($updated);
6214 print "$prefix size of disk '$volid' ($opt) updated from $old_size to $new_size\n";
6215 }
6216 }
6217 }
6218
6219 # remove 'unusedX' entry if volume is used
6220 foreach my $opt (keys %$conf) {
6221 next if $opt !~ m/^unused\d+$/;
6222 my $volid = $conf->{$opt};
6223 my $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid};
6224 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
6225 print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
6226 $changes = 1;
6227 delete $conf->{$opt};
6228 }
6229
6230 $referenced->{$volid} = 1;
6231 $referencedpath->{$path} = 1 if $path;
6232 }
6233
6234 foreach my $volid (sort keys %$volid_hash) {
6235 next if $volid =~ m/vm-$vmid-state-/;
6236 next if $referenced->{$volid};
6237 my $path = $volid_hash->{$volid}->{path};
6238 next if !$path; # just to be sure
6239 next if $referencedpath->{$path};
6240 $changes = 1;
6241 my $key = PVE::QemuConfig->add_unused_volume($conf, $volid);
6242 print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
6243 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
6244 }
6245
6246 return $changes;
6247}
6248
6249sub rescan {
6250 my ($vmid, $nolock, $dryrun) = @_;
6251
6252 my $cfg = PVE::Storage::config();
6253
6254 # FIXME: Remove once our RBD plugin can handle CT and VM on a single storage
6255 # see: https://pve.proxmox.com/pipermail/pve-devel/2018-July/032900.html
6256 foreach my $stor (keys %{$cfg->{ids}}) {
6257 delete($cfg->{ids}->{$stor}) if ! $cfg->{ids}->{$stor}->{content}->{images};
6258 }
6259
6260 print "rescan volumes...\n";
6261 my $volid_hash = scan_volids($cfg, $vmid);
6262
6263 my $updatefn = sub {
6264 my ($vmid) = @_;
6265
6266 my $conf = PVE::QemuConfig->load_config($vmid);
6267
6268 PVE::QemuConfig->check_lock($conf);
6269
6270 my $vm_volids = {};
6271 foreach my $volid (keys %$volid_hash) {
6272 my $info = $volid_hash->{$volid};
6273 $vm_volids->{$volid} = $info if $info->{vmid} && $info->{vmid} == $vmid;
6274 }
6275
6276 my $changes = update_disk_config($vmid, $conf, $vm_volids);
6277
6278 PVE::QemuConfig->write_config($vmid, $conf) if $changes && !$dryrun;
6279 };
6280
6281 if (defined($vmid)) {
6282 if ($nolock) {
6283 &$updatefn($vmid);
6284 } else {
6285 PVE::QemuConfig->lock_config($vmid, $updatefn, $vmid);
6286 }
6287 } else {
6288 my $vmlist = config_list();
6289 foreach my $vmid (keys %$vmlist) {
6290 if ($nolock) {
6291 &$updatefn($vmid);
6292 } else {
6293 PVE::QemuConfig->lock_config($vmid, $updatefn, $vmid);
6294 }
6295 }
6296 }
6297}
6298
6299sub restore_vma_archive {
6300 my ($archive, $vmid, $user, $opts, $comp) = @_;
6301
6302 my $readfrom = $archive;
6303
6304 my $cfg = PVE::Storage::config();
6305 my $commands = [];
6306 my $bwlimit = $opts->{bwlimit};
6307
6308 my $dbg_cmdstring = '';
6309 my $add_pipe = sub {
6310 my ($cmd) = @_;
6311 push @$commands, $cmd;
6312 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
6313 $dbg_cmdstring .= PVE::Tools::cmd2string($cmd);
6314 $readfrom = '-';
6315 };
6316
6317 my $input = undef;
6318 if ($archive eq '-') {
6319 $input = '<&STDIN';
6320 } else {
6321 # If we use a backup from a PVE defined storage we also consider that
6322 # storage's rate limit:
6323 my (undef, $volid) = PVE::Storage::path_to_volume_id($cfg, $archive);
6324 if (defined($volid)) {
6325 my ($sid, undef) = PVE::Storage::parse_volume_id($volid);
6326 my $readlimit = PVE::Storage::get_bandwidth_limit('restore', [$sid], $bwlimit);
6327 if ($readlimit) {
6328 print STDERR "applying read rate limit: $readlimit\n";
6329 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
6330 $add_pipe->($cstream);
6331 }
6332 }
6333 }
6334
6335 if ($comp) {
6336 my $cmd;
6337 if ($comp eq 'gzip') {
6338 $cmd = ['zcat', $readfrom];
6339 } elsif ($comp eq 'lzop') {
6340 $cmd = ['lzop', '-d', '-c', $readfrom];
6341 } else {
6342 die "unknown compression method '$comp'\n";
6343 }
6344 $add_pipe->($cmd);
6345 }
6346
6347 my $tmpdir = "/var/tmp/vzdumptmp$$";
6348 rmtree $tmpdir;
6349
6350 # disable interrupts (always do cleanups)
6351 local $SIG{INT} =
6352 local $SIG{TERM} =
6353 local $SIG{QUIT} =
6354 local $SIG{HUP} = sub { warn "got interrupt - ignored\n"; };
6355
6356 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
6357 POSIX::mkfifo($mapfifo, 0600);
6358 my $fifofh;
6359
6360 my $openfifo = sub {
6361 open($fifofh, '>', $mapfifo) || die $!;
6362 };
6363
6364 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
6365
6366 my $oldtimeout;
6367 my $timeout = 5;
6368
6369 my $devinfo = {};
6370
6371 my $rpcenv = PVE::RPCEnvironment::get();
6372
6373 my $conffile = PVE::QemuConfig->config_file($vmid);
6374 my $tmpfn = "$conffile.$$.tmp";
6375
6376 # Note: $oldconf is undef if VM does not exist
6377 my $cfs_path = PVE::QemuConfig->cfs_config_path($vmid);
6378 my $oldconf = PVE::Cluster::cfs_read_file($cfs_path);
6379
6380 my %storage_limits;
6381
6382 my $print_devmap = sub {
6383 my $virtdev_hash = {};
6384
6385 my $cfgfn = "$tmpdir/qemu-server.conf";
6386
6387 # we can read the config - that is already extracted
6388 my $fh = IO::File->new($cfgfn, "r") ||
6389 "unable to read qemu-server.conf - $!\n";
6390
6391 my $fwcfgfn = "$tmpdir/qemu-server.fw";
6392 if (-f $fwcfgfn) {
6393 my $pve_firewall_dir = '/etc/pve/firewall';
6394 mkdir $pve_firewall_dir; # make sure the dir exists
6395 PVE::Tools::file_copy($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
6396 }
6397
6398 while (defined(my $line = <$fh>)) {
6399 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
6400 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
6401 die "archive does not contain data for drive '$virtdev'\n"
6402 if !$devinfo->{$devname};
6403 if (defined($opts->{storage})) {
6404 $storeid = $opts->{storage} || 'local';
6405 } elsif (!$storeid) {
6406 $storeid = 'local';
6407 }
6408 $format = 'raw' if !$format;
6409 $devinfo->{$devname}->{devname} = $devname;
6410 $devinfo->{$devname}->{virtdev} = $virtdev;
6411 $devinfo->{$devname}->{format} = $format;
6412 $devinfo->{$devname}->{storeid} = $storeid;
6413
6414 # check permission on storage
6415 my $pool = $opts->{pool}; # todo: do we need that?
6416 if ($user ne 'root@pam') {
6417 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']);
6418 }
6419
6420 $storage_limits{$storeid} = $bwlimit;
6421
6422 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
6423 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
6424 my $virtdev = $1;
6425 my $drive = parse_drive($virtdev, $2);
6426 if (drive_is_cloudinit($drive)) {
6427 my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
6428 my $scfg = PVE::Storage::storage_config($cfg, $storeid);
6429 my $format = qemu_img_format($scfg, $volname); # has 'raw' fallback
6430
6431 my $d = {
6432 format => $format,
6433 storeid => $opts->{storage} // $storeid,
6434 size => PVE::QemuServer::Cloudinit::CLOUDINIT_DISK_SIZE,
6435 file => $drive->{file}, # to make drive_is_cloudinit check possible
6436 name => "vm-$vmid-cloudinit",
6437 is_cloudinit => 1,
6438 };
6439 $virtdev_hash->{$virtdev} = $d;
6440 }
6441 }
6442 }
6443
6444 foreach my $key (keys %storage_limits) {
6445 my $limit = PVE::Storage::get_bandwidth_limit('restore', [$key], $bwlimit);
6446 next if !$limit;
6447 print STDERR "rate limit for storage $key: $limit KiB/s\n";
6448 $storage_limits{$key} = $limit * 1024;
6449 }
6450
6451 foreach my $devname (keys %$devinfo) {
6452 die "found no device mapping information for device '$devname'\n"
6453 if !$devinfo->{$devname}->{virtdev};
6454 }
6455
6456 # create empty/temp config
6457 if ($oldconf) {
6458 PVE::Tools::file_set_contents($conffile, "memory: 128\n");
6459 foreach_drive($oldconf, sub {
6460 my ($ds, $drive) = @_;
6461
6462 return if drive_is_cdrom($drive, 1);
6463
6464 my $volid = $drive->{file};
6465 return if !$volid || $volid =~ m|^/|;
6466
6467 my ($path, $owner) = PVE::Storage::path($cfg, $volid);
6468 return if !$path || !$owner || ($owner != $vmid);
6469
6470 # Note: only delete disk we want to restore
6471 # other volumes will become unused
6472 if ($virtdev_hash->{$ds}) {
6473 eval { PVE::Storage::vdisk_free($cfg, $volid); };
6474 if (my $err = $@) {
6475 warn $err;
6476 }
6477 }
6478 });
6479
6480 # delete vmstate files, after the restore we have no snapshots anymore
6481 foreach my $snapname (keys %{$oldconf->{snapshots}}) {
6482 my $snap = $oldconf->{snapshots}->{$snapname};
6483 if ($snap->{vmstate}) {
6484 eval { PVE::Storage::vdisk_free($cfg, $snap->{vmstate}); };
6485 if (my $err = $@) {
6486 warn $err;
6487 }
6488 }
6489 }
6490 }
6491
6492 my $map = {};
6493 foreach my $virtdev (sort keys %$virtdev_hash) {
6494 my $d = $virtdev_hash->{$virtdev};
6495 my $alloc_size = int(($d->{size} + 1024 - 1)/1024);
6496 my $storeid = $d->{storeid};
6497 my $scfg = PVE::Storage::storage_config($cfg, $storeid);
6498
6499 my $map_opts = '';
6500 if (my $limit = $storage_limits{$storeid}) {
6501 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
6502 }
6503
6504 # test if requested format is supported
6505 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($cfg, $storeid);
6506 my $supported = grep { $_ eq $d->{format} } @$validFormats;
6507 $d->{format} = $defFormat if !$supported;
6508
6509 my $name;
6510 if ($d->{is_cloudinit}) {
6511 $name = $d->{name};
6512 $name .= ".$d->{format}" if $d->{format} ne 'raw';
6513 }
6514
6515 my $volid = PVE::Storage::vdisk_alloc($cfg, $storeid, $vmid, $d->{format}, $name, $alloc_size);
6516 print STDERR "new volume ID is '$volid'\n";
6517 $d->{volid} = $volid;
6518
6519 PVE::Storage::activate_volumes($cfg, [$volid]);
6520
6521 my $write_zeros = 1;
6522 if (PVE::Storage::volume_has_feature($cfg, 'sparseinit', $volid)) {
6523 $write_zeros = 0;
6524 }
6525
6526 if (!$d->{is_cloudinit}) {
6527 my $path = PVE::Storage::path($cfg, $volid);
6528
6529 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
6530
6531 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
6532 }
6533 $map->{$virtdev} = $volid;
6534 }
6535
6536 $fh->seek(0, 0) || die "seek failed - $!\n";
6537
6538 my $outfd = new IO::File ($tmpfn, "w") ||
6539 die "unable to write config for VM $vmid\n";
6540
6541 my $cookie = { netcount => 0 };
6542 while (defined(my $line = <$fh>)) {
6543 restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});
6544 }
6545
6546 $fh->close();
6547 $outfd->close();
6548 };
6549
6550 eval {
6551 # enable interrupts
6552 local $SIG{INT} =
6553 local $SIG{TERM} =
6554 local $SIG{QUIT} =
6555 local $SIG{HUP} =
6556 local $SIG{PIPE} = sub { die "interrupted by signal\n"; };
6557 local $SIG{ALRM} = sub { die "got timeout\n"; };
6558
6559 $oldtimeout = alarm($timeout);
6560
6561 my $parser = sub {
6562 my $line = shift;
6563
6564 print "$line\n";
6565
6566 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
6567 my ($dev_id, $size, $devname) = ($1, $2, $3);
6568 $devinfo->{$devname} = { size => $size, dev_id => $dev_id };
6569 } elsif ($line =~ m/^CTIME: /) {
6570 # we correctly received the vma config, so we can disable
6571 # the timeout now for disk allocation (set to 10 minutes, so
6572 # that we always timeout if something goes wrong)
6573 alarm(600);
6574 &$print_devmap();
6575 print $fifofh "done\n";
6576 my $tmp = $oldtimeout || 0;
6577 $oldtimeout = undef;
6578 alarm($tmp);
6579 close($fifofh);
6580 }
6581 };
6582
6583 print "restore vma archive: $dbg_cmdstring\n";
6584 run_command($commands, input => $input, outfunc => $parser, afterfork => $openfifo);
6585 };
6586 my $err = $@;
6587
6588 alarm($oldtimeout) if $oldtimeout;
6589
6590 my $vollist = [];
6591 foreach my $devname (keys %$devinfo) {
6592 my $volid = $devinfo->{$devname}->{volid};
6593 push @$vollist, $volid if $volid;
6594 }
6595
6596 PVE::Storage::deactivate_volumes($cfg, $vollist);
6597
6598 unlink $mapfifo;
6599
6600 if ($err) {
6601 rmtree $tmpdir;
6602 unlink $tmpfn;
6603
6604 foreach my $devname (keys %$devinfo) {
6605 my $volid = $devinfo->{$devname}->{volid};
6606 next if !$volid;
6607 eval {
6608 if ($volid =~ m|^/|) {
6609 unlink $volid || die 'unlink failed\n';
6610 } else {
6611 PVE::Storage::vdisk_free($cfg, $volid);
6612 }
6613 print STDERR "temporary volume '$volid' sucessfuly removed\n";
6614 };
6615 print STDERR "unable to cleanup '$volid' - $@" if $@;
6616 }
6617 die $err;
6618 }
6619
6620 rmtree $tmpdir;
6621
6622 rename($tmpfn, $conffile) ||
6623 die "unable to commit configuration file '$conffile'\n";
6624
6625 PVE::Cluster::cfs_update(); # make sure we read new file
6626
6627 eval { rescan($vmid, 1); };
6628 warn $@ if $@;
6629}
6630
6631sub restore_tar_archive {
6632 my ($archive, $vmid, $user, $opts) = @_;
6633
6634 if ($archive ne '-') {
6635 my $firstfile = tar_archive_read_firstfile($archive);
6636 die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n"
6637 if $firstfile ne 'qemu-server.conf';
6638 }
6639
6640 my $storecfg = PVE::Storage::config();
6641
6642 # avoid zombie disks when restoring over an existing VM -> cleanup first
6643 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
6644 # skiplock=1 because qmrestore has set the 'create' lock itself already
6645 my $vmcfgfn = PVE::QemuConfig->config_file($vmid);
6646 destroy_vm($storecfg, $vmid, 1, { lock => 'restore' }) if -f $vmcfgfn;
6647
6648 my $tocmd = "/usr/lib/qemu-server/qmextract";
6649
6650 $tocmd .= " --storage " . PVE::Tools::shellquote($opts->{storage}) if $opts->{storage};
6651 $tocmd .= " --pool " . PVE::Tools::shellquote($opts->{pool}) if $opts->{pool};
6652 $tocmd .= ' --prealloc' if $opts->{prealloc};
6653 $tocmd .= ' --info' if $opts->{info};
6654
6655 # tar option "xf" does not autodetect compression when read from STDIN,
6656 # so we pipe to zcat
6657 my $cmd = "zcat -f|tar xf " . PVE::Tools::shellquote($archive) . " " .
6658 PVE::Tools::shellquote("--to-command=$tocmd");
6659
6660 my $tmpdir = "/var/tmp/vzdumptmp$$";
6661 mkpath $tmpdir;
6662
6663 local $ENV{VZDUMP_TMPDIR} = $tmpdir;
6664 local $ENV{VZDUMP_VMID} = $vmid;
6665 local $ENV{VZDUMP_USER} = $user;
6666
6667 my $conffile = PVE::QemuConfig->config_file($vmid);
6668 my $tmpfn = "$conffile.$$.tmp";
6669
6670 # disable interrupts (always do cleanups)
6671 local $SIG{INT} =
6672 local $SIG{TERM} =
6673 local $SIG{QUIT} =
6674 local $SIG{HUP} = sub { print STDERR "got interrupt - ignored\n"; };
6675
6676 eval {
6677 # enable interrupts
6678 local $SIG{INT} =
6679 local $SIG{TERM} =
6680 local $SIG{QUIT} =
6681 local $SIG{HUP} =
6682 local $SIG{PIPE} = sub { die "interrupted by signal\n"; };
6683
6684 if ($archive eq '-') {
6685 print "extracting archive from STDIN\n";
6686 run_command($cmd, input => "<&STDIN");
6687 } else {
6688 print "extracting archive '$archive'\n";
6689 run_command($cmd);
6690 }
6691
6692 return if $opts->{info};
6693
6694 # read new mapping
6695 my $map = {};
6696 my $statfile = "$tmpdir/qmrestore.stat";
6697 if (my $fd = IO::File->new($statfile, "r")) {
6698 while (defined (my $line = <$fd>)) {
6699 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6700 $map->{$1} = $2 if $1;
6701 } else {
6702 print STDERR "unable to parse line in statfile - $line\n";
6703 }
6704 }
6705 $fd->close();
6706 }
6707
6708 my $confsrc = "$tmpdir/qemu-server.conf";
6709
6710 my $srcfd = new IO::File($confsrc, "r") ||
6711 die "unable to open file '$confsrc'\n";
6712
6713 my $outfd = new IO::File ($tmpfn, "w") ||
6714 die "unable to write config for VM $vmid\n";
6715
6716 my $cookie = { netcount => 0 };
6717 while (defined (my $line = <$srcfd>)) {
6718 restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique});
6719 }
6720
6721 $srcfd->close();
6722 $outfd->close();
6723 };
6724 if (my $err = $@) {
6725 unlink $tmpfn;
6726 tar_restore_cleanup($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info};
6727 die $err;
6728 }
6729
6730 rmtree $tmpdir;
6731
6732 rename $tmpfn, $conffile ||
6733 die "unable to commit configuration file '$conffile'\n";
6734
6735 PVE::Cluster::cfs_update(); # make sure we read new file
6736
6737 eval { rescan($vmid, 1); };
6738 warn $@ if $@;
6739};
6740
6741sub foreach_storage_used_by_vm {
6742 my ($conf, $func) = @_;
6743
6744 my $sidhash = {};
6745
6746 foreach_drive($conf, sub {
6747 my ($ds, $drive) = @_;
6748 return if drive_is_cdrom($drive);
6749
6750 my $volid = $drive->{file};
6751
6752 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
6753 $sidhash->{$sid} = $sid if $sid;
6754 });
6755
6756 foreach my $sid (sort keys %$sidhash) {
6757 &$func($sid);
6758 }
6759}
6760
6761my $qemu_snap_storage = {
6762 rbd => 1,
6763};
6764sub do_snapshots_with_qemu {
6765 my ($storecfg, $volid) = @_;
6766
6767 my $storage_name = PVE::Storage::parse_volume_id($volid);
6768 my $scfg = $storecfg->{ids}->{$storage_name};
6769
6770 if ($qemu_snap_storage->{$scfg->{type}} && !$scfg->{krbd}){
6771 return 1;
6772 }
6773
6774 if ($volid =~ m/\.(qcow2|qed)$/){
6775 return 1;
6776 }
6777
6778 return undef;
6779}
6780
6781sub qga_check_running {
6782 my ($vmid, $nowarn) = @_;
6783
6784 eval { mon_cmd($vmid, "guest-ping", timeout => 3); };
6785 if ($@) {
6786 warn "Qemu Guest Agent is not running - $@" if !$nowarn;
6787 return 0;
6788 }
6789 return 1;
6790}
6791
6792sub template_create {
6793 my ($vmid, $conf, $disk) = @_;
6794
6795 my $storecfg = PVE::Storage::config();
6796
6797 foreach_drive($conf, sub {
6798 my ($ds, $drive) = @_;
6799
6800 return if drive_is_cdrom($drive);
6801 return if $disk && $ds ne $disk;
6802
6803 my $volid = $drive->{file};
6804 return if !PVE::Storage::volume_has_feature($storecfg, 'template', $volid);
6805
6806 my $voliddst = PVE::Storage::vdisk_create_base($storecfg, $volid);
6807 $drive->{file} = $voliddst;
6808 $conf->{$ds} = print_drive($drive);
6809 PVE::QemuConfig->write_config($vmid, $conf);
6810 });
6811}
6812
6813sub convert_iscsi_path {
6814 my ($path) = @_;
6815
6816 if ($path =~ m|^iscsi://([^/]+)/([^/]+)/(.+)$|) {
6817 my $portal = $1;
6818 my $target = $2;
6819 my $lun = $3;
6820
6821 my $initiator_name = get_initiator_name();
6822
6823 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
6824 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
6825 }
6826
6827 die "cannot convert iscsi path '$path', unkown format\n";
6828}
6829
6830sub qemu_img_convert {
6831 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
6832
6833 my $storecfg = PVE::Storage::config();
6834 my ($src_storeid, $src_volname) = PVE::Storage::parse_volume_id($src_volid, 1);
6835 my ($dst_storeid, $dst_volname) = PVE::Storage::parse_volume_id($dst_volid, 1);
6836
6837 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
6838
6839 my $cachemode;
6840 my $src_path;
6841 my $src_is_iscsi = 0;
6842 my $src_format;
6843
6844 if ($src_storeid) {
6845 PVE::Storage::activate_volumes($storecfg, [$src_volid], $snapname);
6846 my $src_scfg = PVE::Storage::storage_config($storecfg, $src_storeid);
6847 $src_format = qemu_img_format($src_scfg, $src_volname);
6848 $src_path = PVE::Storage::path($storecfg, $src_volid, $snapname);
6849 $src_is_iscsi = ($src_path =~ m|^iscsi://|);
6850 $cachemode = 'none' if $src_scfg->{type} eq 'zfspool';
6851 } elsif (-f $src_volid) {
6852 $src_path = $src_volid;
6853 if ($src_path =~ m/\.($QEMU_FORMAT_RE)$/) {
6854 $src_format = $1;
6855 }
6856 }
6857
6858 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
6859
6860 my $dst_scfg = PVE::Storage::storage_config($storecfg, $dst_storeid);
6861 my $dst_format = qemu_img_format($dst_scfg, $dst_volname);
6862 my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
6863 my $dst_is_iscsi = ($dst_path =~ m|^iscsi://|);
6864
6865 my $cmd = [];
6866 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
6867 push @$cmd, '-l', "snapshot.name=$snapname"
6868 if $snapname && $src_format && $src_format eq "qcow2";
6869 push @$cmd, '-t', 'none' if $dst_scfg->{type} eq 'zfspool';
6870 push @$cmd, '-T', $cachemode if defined($cachemode);
6871
6872 if ($src_is_iscsi) {
6873 push @$cmd, '--image-opts';
6874 $src_path = convert_iscsi_path($src_path);
6875 } elsif ($src_format) {
6876 push @$cmd, '-f', $src_format;
6877 }
6878
6879 if ($dst_is_iscsi) {
6880 push @$cmd, '--target-image-opts';
6881 $dst_path = convert_iscsi_path($dst_path);
6882 } else {
6883 push @$cmd, '-O', $dst_format;
6884 }
6885
6886 push @$cmd, $src_path;
6887
6888 if (!$dst_is_iscsi && $is_zero_initialized) {
6889 push @$cmd, "zeroinit:$dst_path";
6890 } else {
6891 push @$cmd, $dst_path;
6892 }
6893
6894 my $parser = sub {
6895 my $line = shift;
6896 if($line =~ m/\((\S+)\/100\%\)/){
6897 my $percent = $1;
6898 my $transferred = int($size * $percent / 100);
6899 my $remaining = $size - $transferred;
6900
6901 print "transferred: $transferred bytes remaining: $remaining bytes total: $size bytes progression: $percent %\n";
6902 }
6903
6904 };
6905
6906 eval { run_command($cmd, timeout => undef, outfunc => $parser); };
6907 my $err = $@;
6908 die "copy failed: $err" if $err;
6909}
6910
6911sub qemu_img_format {
6912 my ($scfg, $volname) = @_;
6913
6914 if ($scfg->{path} && $volname =~ m/\.($QEMU_FORMAT_RE)$/) {
6915 return $1;
6916 } else {
6917 return "raw";
6918 }
6919}
6920
6921sub qemu_drive_mirror {
6922 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $skipcomplete, $qga, $bwlimit) = @_;
6923
6924 $jobs = {} if !$jobs;
6925
6926 my $qemu_target;
6927 my $format;
6928 $jobs->{"drive-$drive"} = {};
6929
6930 if ($dst_volid =~ /^nbd:/) {
6931 $qemu_target = $dst_volid;
6932 $format = "nbd";
6933 } else {
6934 my $storecfg = PVE::Storage::config();
6935 my ($dst_storeid, $dst_volname) = PVE::Storage::parse_volume_id($dst_volid);
6936
6937 my $dst_scfg = PVE::Storage::storage_config($storecfg, $dst_storeid);
6938
6939 $format = qemu_img_format($dst_scfg, $dst_volname);
6940
6941 my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
6942
6943 $qemu_target = $is_zero_initialized ? "zeroinit:$dst_path" : $dst_path;
6944 }
6945
6946 my $opts = { timeout => 10, device => "drive-$drive", mode => "existing", sync => "full", target => $qemu_target };
6947 $opts->{format} = $format if $format;
6948
6949 if (defined($bwlimit)) {
6950 $opts->{speed} = $bwlimit * 1024;
6951 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
6952 } else {
6953 print "drive mirror is starting for drive-$drive\n";
6954 }
6955
6956 # if a job already runs for this device we get an error, catch it for cleanup
6957 eval { mon_cmd($vmid, "drive-mirror", %$opts); };
6958 if (my $err = $@) {
6959 eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
6960 warn "$@\n" if $@;
6961 die "mirroring error: $err\n";
6962 }
6963
6964 qemu_drive_mirror_monitor ($vmid, $vmiddst, $jobs, $skipcomplete, $qga);
6965}
6966
6967sub qemu_drive_mirror_monitor {
6968 my ($vmid, $vmiddst, $jobs, $skipcomplete, $qga) = @_;
6969
6970 eval {
6971 my $err_complete = 0;
6972
6973 while (1) {
6974 die "storage migration timed out\n" if $err_complete > 300;
6975
6976 my $stats = mon_cmd($vmid, "query-block-jobs");
6977
6978 my $running_mirror_jobs = {};
6979 foreach my $stat (@$stats) {
6980 next if $stat->{type} ne 'mirror';
6981 $running_mirror_jobs->{$stat->{device}} = $stat;
6982 }
6983
6984 my $readycounter = 0;
6985
6986 foreach my $job (keys %$jobs) {
6987
6988 if(defined($jobs->{$job}->{complete}) && !defined($running_mirror_jobs->{$job})) {
6989 print "$job : finished\n";
6990 delete $jobs->{$job};
6991 next;
6992 }
6993
6994 die "$job: mirroring has been cancelled\n" if !defined($running_mirror_jobs->{$job});
6995
6996 my $busy = $running_mirror_jobs->{$job}->{busy};
6997 my $ready = $running_mirror_jobs->{$job}->{ready};
6998 if (my $total = $running_mirror_jobs->{$job}->{len}) {
6999 my $transferred = $running_mirror_jobs->{$job}->{offset} || 0;
7000 my $remaining = $total - $transferred;
7001 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
7002
7003 print "$job: transferred: $transferred bytes remaining: $remaining bytes total: $total bytes progression: $percent % busy: $busy ready: $ready \n";
7004 }
7005
7006 $readycounter++ if $running_mirror_jobs->{$job}->{ready};
7007 }
7008
7009 last if scalar(keys %$jobs) == 0;
7010
7011 if ($readycounter == scalar(keys %$jobs)) {
7012 print "all mirroring jobs are ready \n";
7013 last if $skipcomplete; #do the complete later
7014
7015 if ($vmiddst && $vmiddst != $vmid) {
7016 my $agent_running = $qga && qga_check_running($vmid);
7017 if ($agent_running) {
7018 print "freeze filesystem\n";
7019 eval { mon_cmd($vmid, "guest-fsfreeze-freeze"); };
7020 } else {
7021 print "suspend vm\n";
7022 eval { PVE::QemuServer::vm_suspend($vmid, 1); };
7023 }
7024
7025 # if we clone a disk for a new target vm, we don't switch the disk
7026 PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs);
7027
7028 if ($agent_running) {
7029 print "unfreeze filesystem\n";
7030 eval { mon_cmd($vmid, "guest-fsfreeze-thaw"); };
7031 } else {
7032 print "resume vm\n";
7033 eval { PVE::QemuServer::vm_resume($vmid, 1, 1); };
7034 }
7035
7036 last;
7037 } else {
7038
7039 foreach my $job (keys %$jobs) {
7040 # try to switch the disk if source and destination are on the same guest
7041 print "$job: Completing block job...\n";
7042
7043 eval { mon_cmd($vmid, "block-job-complete", device => $job) };
7044 if ($@ =~ m/cannot be completed/) {
7045 print "$job: Block job cannot be completed, try again.\n";
7046 $err_complete++;
7047 }else {
7048 print "$job: Completed successfully.\n";
7049 $jobs->{$job}->{complete} = 1;
7050 }
7051 }
7052 }
7053 }
7054 sleep 1;
7055 }
7056 };
7057 my $err = $@;
7058
7059 if ($err) {
7060 eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
7061 die "mirroring error: $err";
7062 }
7063
7064}
7065
7066sub qemu_blockjobs_cancel {
7067 my ($vmid, $jobs) = @_;
7068
7069 foreach my $job (keys %$jobs) {
7070 print "$job: Cancelling block job\n";
7071 eval { mon_cmd($vmid, "block-job-cancel", device => $job); };
7072 $jobs->{$job}->{cancel} = 1;
7073 }
7074
7075 while (1) {
7076 my $stats = mon_cmd($vmid, "query-block-jobs");
7077
7078 my $running_jobs = {};
7079 foreach my $stat (@$stats) {
7080 $running_jobs->{$stat->{device}} = $stat;
7081 }
7082
7083 foreach my $job (keys %$jobs) {
7084
7085 if (defined($jobs->{$job}->{cancel}) && !defined($running_jobs->{$job})) {
7086 print "$job: Done.\n";
7087 delete $jobs->{$job};
7088 }
7089 }
7090
7091 last if scalar(keys %$jobs) == 0;
7092
7093 sleep 1;
7094 }
7095}
7096
7097sub clone_disk {
7098 my ($storecfg, $vmid, $running, $drivename, $drive, $snapname,
7099 $newvmid, $storage, $format, $full, $newvollist, $jobs, $skipcomplete, $qga, $bwlimit) = @_;
7100
7101 my $newvolid;
7102
7103 if (!$full) {
7104 print "create linked clone of drive $drivename ($drive->{file})\n";
7105 $newvolid = PVE::Storage::vdisk_clone($storecfg, $drive->{file}, $newvmid, $snapname);
7106 push @$newvollist, $newvolid;
7107 } else {
7108
7109 my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
7110 $storeid = $storage if $storage;
7111
7112 my $dst_format = resolve_dst_disk_format($storecfg, $storeid, $volname, $format);
7113 my ($size) = PVE::Storage::volume_size_info($storecfg, $drive->{file}, 3);
7114
7115 print "create full clone of drive $drivename ($drive->{file})\n";
7116 my $name = undef;
7117 if (drive_is_cloudinit($drive)) {
7118 $name = "vm-$newvmid-cloudinit";
7119 $name .= ".$dst_format" if $dst_format ne 'raw';
7120 $snapname = undef;
7121 $size = PVE::QemuServer::Cloudinit::CLOUDINIT_DISK_SIZE;
7122 }
7123 $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024));
7124 push @$newvollist, $newvolid;
7125
7126 PVE::Storage::activate_volumes($storecfg, [$newvolid]);
7127
7128 if (drive_is_cloudinit($drive)) {
7129 goto no_data_clone;
7130 }
7131
7132 my $sparseinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $newvolid);
7133 if (!$running || $snapname) {
7134 # TODO: handle bwlimits
7135 qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit);
7136 } else {
7137
7138 my $kvmver = get_running_qemu_version ($vmid);
7139 if (!min_version($kvmver, 2, 7)) {
7140 die "drive-mirror with iothread requires qemu version 2.7 or higher\n"
7141 if $drive->{iothread};
7142 }
7143
7144 qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit, $jobs, $skipcomplete, $qga, $bwlimit);
7145 }
7146 }
7147
7148no_data_clone:
7149 my ($size) = PVE::Storage::volume_size_info($storecfg, $newvolid, 3);
7150
7151 my $disk = $drive;
7152 $disk->{format} = undef;
7153 $disk->{file} = $newvolid;
7154 $disk->{size} = $size;
7155
7156 return $disk;
7157}
7158
7159sub get_running_qemu_version {
7160 my ($vmid) = @_;
7161 my $res = mon_cmd($vmid, "query-version");
7162 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
7163}
7164
7165sub qemu_use_old_bios_files {
7166 my ($machine_type) = @_;
7167
7168 return if !$machine_type;
7169
7170 my $use_old_bios_files = undef;
7171
7172 if ($machine_type =~ m/^(\S+)\.pxe$/) {
7173 $machine_type = $1;
7174 $use_old_bios_files = 1;
7175 } else {
7176 my $version = PVE::QemuServer::Machine::extract_version($machine_type, kvm_user_version());
7177 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
7178 # load new efi bios files on migration. So this hack is required to allow
7179 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
7180 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
7181 $use_old_bios_files = !min_version($version, 2, 4);
7182 }
7183
7184 return ($use_old_bios_files, $machine_type);
7185}
7186
7187sub create_efidisk($$$$$) {
7188 my ($storecfg, $storeid, $vmid, $fmt, $arch) = @_;
7189
7190 my (undef, $ovmf_vars) = get_ovmf_files($arch);
7191 die "EFI vars default image not found\n" if ! -f $ovmf_vars;
7192
7193 my $vars_size_b = -s $ovmf_vars;
7194 my $vars_size = PVE::Tools::convert_size($vars_size_b, 'b' => 'kb');
7195 my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
7196 PVE::Storage::activate_volumes($storecfg, [$volid]);
7197
7198 qemu_img_convert($ovmf_vars, $volid, $vars_size_b, undef, 0);
7199 my ($size) = PVE::Storage::volume_size_info($storecfg, $volid, 3);
7200
7201 return ($volid, $size/1024);
7202}
7203
7204sub vm_iothreads_list {
7205 my ($vmid) = @_;
7206
7207 my $res = mon_cmd($vmid, 'query-iothreads');
7208
7209 my $iothreads = {};
7210 foreach my $iothread (@$res) {
7211 $iothreads->{ $iothread->{id} } = $iothread->{"thread-id"};
7212 }
7213
7214 return $iothreads;
7215}
7216
7217sub scsihw_infos {
7218 my ($conf, $drive) = @_;
7219
7220 my $maxdev = 0;
7221
7222 if (!$conf->{scsihw} || ($conf->{scsihw} =~ m/^lsi/)) {
7223 $maxdev = 7;
7224 } elsif ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
7225 $maxdev = 1;
7226 } else {
7227 $maxdev = 256;
7228 }
7229
7230 my $controller = int($drive->{index} / $maxdev);
7231 my $controller_prefix = ($conf->{scsihw} && $conf->{scsihw} eq 'virtio-scsi-single') ? "virtioscsi" : "scsihw";
7232
7233 return ($maxdev, $controller, $controller_prefix);
7234}
7235
7236sub windows_version {
7237 my ($ostype) = @_;
7238
7239 return 0 if !$ostype;
7240
7241 my $winversion = 0;
7242
7243 if($ostype eq 'wxp' || $ostype eq 'w2k3' || $ostype eq 'w2k') {
7244 $winversion = 5;
7245 } elsif($ostype eq 'w2k8' || $ostype eq 'wvista') {
7246 $winversion = 6;
7247 } elsif ($ostype =~ m/^win(\d+)$/) {
7248 $winversion = $1;
7249 }
7250
7251 return $winversion;
7252}
7253
7254sub resolve_dst_disk_format {
7255 my ($storecfg, $storeid, $src_volname, $format) = @_;
7256 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
7257
7258 if (!$format) {
7259 # if no target format is specified, use the source disk format as hint
7260 if ($src_volname) {
7261 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
7262 $format = qemu_img_format($scfg, $src_volname);
7263 } else {
7264 return $defFormat;
7265 }
7266 }
7267
7268 # test if requested format is supported - else use default
7269 my $supported = grep { $_ eq $format } @$validFormats;
7270 $format = $defFormat if !$supported;
7271 return $format;
7272}
7273
7274sub resolve_first_disk {
7275 my $conf = shift;
7276 my @disks = PVE::QemuServer::valid_drive_names();
7277 my $firstdisk;
7278 foreach my $ds (reverse @disks) {
7279 next if !$conf->{$ds};
7280 my $disk = PVE::QemuServer::parse_drive($ds, $conf->{$ds});
7281 next if PVE::QemuServer::drive_is_cdrom($disk);
7282 $firstdisk = $ds;
7283 }
7284 return $firstdisk;
7285}
7286
7287# NOTE: if this logic changes, please update docs & possibly gui logic
7288sub find_vmstate_storage {
7289 my ($conf, $storecfg) = @_;
7290
7291 # first, return storage from conf if set
7292 return $conf->{vmstatestorage} if $conf->{vmstatestorage};
7293
7294 my ($target, $shared, $local);
7295
7296 foreach_storage_used_by_vm($conf, sub {
7297 my ($sid) = @_;
7298 my $scfg = PVE::Storage::storage_config($storecfg, $sid);
7299 my $dst = $scfg->{shared} ? \$shared : \$local;
7300 $$dst = $sid if !$$dst || $scfg->{path}; # prefer file based storage
7301 });
7302
7303 # second, use shared storage where VM has at least one disk
7304 # third, use local storage where VM has at least one disk
7305 # fall back to local storage
7306 $target = $shared // $local // 'local';
7307
7308 return $target;
7309}
7310
7311sub generate_uuid {
7312 my ($uuid, $uuid_str);
7313 UUID::generate($uuid);
7314 UUID::unparse($uuid, $uuid_str);
7315 return $uuid_str;
7316}
7317
7318sub generate_smbios1_uuid {
7319 return "uuid=".generate_uuid();
7320}
7321
7322sub nbd_stop {
7323 my ($vmid) = @_;
7324
7325 mon_cmd($vmid, 'nbd-server-stop');
7326}
7327
7328sub create_reboot_request {
7329 my ($vmid) = @_;
7330 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
7331 or die "failed to create reboot trigger file: $!\n";
7332 close($fh);
7333}
7334
7335sub clear_reboot_request {
7336 my ($vmid) = @_;
7337 my $path = "/run/qemu-server/$vmid.reboot";
7338 my $res = 0;
7339
7340 $res = unlink($path);
7341 die "could not remove reboot request for $vmid: $!"
7342 if !$res && $! != POSIX::ENOENT;
7343
7344 return $res;
7345}
7346
7347# bash completion helper
7348
7349sub complete_backup_archives {
7350 my ($cmdname, $pname, $cvalue) = @_;
7351
7352 my $cfg = PVE::Storage::config();
7353
7354 my $storeid;
7355
7356 if ($cvalue =~ m/^([^:]+):/) {
7357 $storeid = $1;
7358 }
7359
7360 my $data = PVE::Storage::template_list($cfg, $storeid, 'backup');
7361
7362 my $res = [];
7363 foreach my $id (keys %$data) {
7364 foreach my $item (@{$data->{$id}}) {
7365 next if $item->{format} !~ m/^vma\.(gz|lzo)$/;
7366 push @$res, $item->{volid} if defined($item->{volid});
7367 }
7368 }
7369
7370 return $res;
7371}
7372
7373my $complete_vmid_full = sub {
7374 my ($running) = @_;
7375
7376 my $idlist = vmstatus();
7377
7378 my $res = [];
7379
7380 foreach my $id (keys %$idlist) {
7381 my $d = $idlist->{$id};
7382 if (defined($running)) {
7383 next if $d->{template};
7384 next if $running && $d->{status} ne 'running';
7385 next if !$running && $d->{status} eq 'running';
7386 }
7387 push @$res, $id;
7388
7389 }
7390 return $res;
7391};
7392
7393sub complete_vmid {
7394 return &$complete_vmid_full();
7395}
7396
7397sub complete_vmid_stopped {
7398 return &$complete_vmid_full(0);
7399}
7400
7401sub complete_vmid_running {
7402 return &$complete_vmid_full(1);
7403}
7404
7405sub complete_storage {
7406
7407 my $cfg = PVE::Storage::config();
7408 my $ids = $cfg->{ids};
7409
7410 my $res = [];
7411 foreach my $sid (keys %$ids) {
7412 next if !PVE::Storage::storage_check_enabled($cfg, $sid, undef, 1);
7413 next if !$ids->{$sid}->{content}->{images};
7414 push @$res, $sid;
7415 }
7416
7417 return $res;
7418}
7419
7420sub complete_migration_storage {
7421 my ($cmd, $param, $current_value, $all_args) = @_;
7422
7423 my $targetnode = @$all_args[1];
7424
7425 my $cfg = PVE::Storage::config();
7426 my $ids = $cfg->{ids};
7427
7428 my $res = [];
7429 foreach my $sid (keys %$ids) {
7430 next if !PVE::Storage::storage_check_enabled($cfg, $sid, $targetnode, 1);
7431 next if !$ids->{$sid}->{content}->{images};
7432 push @$res, $sid;
7433 }
7434
7435 return $res;
7436}
7437
74381;