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