]> git.proxmox.com Git - qemu-server.git/blob - PVE/QemuServer.pm
bump version to 8.0.10
[qemu-server.git] / PVE / QemuServer.pm
1 package PVE::QemuServer;
2
3 use strict;
4 use warnings;
5
6 use Cwd 'abs_path';
7 use Digest::SHA;
8 use Fcntl ':flock';
9 use Fcntl;
10 use File::Basename;
11 use File::Copy qw(copy);
12 use File::Path;
13 use File::stat;
14 use Getopt::Long;
15 use IO::Dir;
16 use IO::File;
17 use IO::Handle;
18 use IO::Select;
19 use IO::Socket::UNIX;
20 use IPC::Open3;
21 use JSON;
22 use List::Util qw(first);
23 use MIME::Base64;
24 use POSIX;
25 use Storable qw(dclone);
26 use Time::HiRes qw(gettimeofday usleep);
27 use URI::Escape;
28 use UUID;
29
30 use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file);
31 use PVE::CGroup;
32 use PVE::CpuSet;
33 use PVE::DataCenterConfig;
34 use PVE::Exception qw(raise raise_param_exc);
35 use PVE::Format qw(render_duration render_bytes);
36 use PVE::GuestHelpers qw(safe_string_ne safe_num_ne safe_boolean_ne);
37 use PVE::Mapping::PCI;
38 use PVE::Mapping::USB;
39 use PVE::INotify;
40 use PVE::JSONSchema qw(get_standard_option parse_property_string);
41 use PVE::ProcFSTools;
42 use PVE::PBSClient;
43 use PVE::RESTEnvironment qw(log_warn);
44 use PVE::RPCEnvironment;
45 use PVE::Storage;
46 use PVE::SysFSTools;
47 use PVE::Systemd;
48 use PVE::Tools qw(run_command file_read_firstline file_get_contents dir_glob_foreach get_host_arch $IPV6RE);
49
50 use PVE::QMPClient;
51 use PVE::QemuConfig;
52 use PVE::QemuServer::Helpers qw(config_aware_timeout min_version windows_version);
53 use PVE::QemuServer::Cloudinit;
54 use PVE::QemuServer::CGroup;
55 use PVE::QemuServer::CPUConfig qw(print_cpu_device get_cpu_options);
56 use PVE::QemuServer::Drive qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom drive_is_read_only parse_drive print_drive);
57 use PVE::QemuServer::Machine;
58 use PVE::QemuServer::Memory qw(get_current_memory);
59 use PVE::QemuServer::Monitor qw(mon_cmd);
60 use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port parse_hostpci);
61 use PVE::QemuServer::QMPHelpers qw(qemu_deviceadd qemu_devicedel qemu_objectadd qemu_objectdel);
62 use PVE::QemuServer::USB;
63
64 my $have_sdn;
65 eval {
66 require PVE::Network::SDN::Zones;
67 require PVE::Network::SDN::Vnets;
68 $have_sdn = 1;
69 };
70
71 my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
72 my $OVMF = {
73 x86_64 => {
74 '4m-no-smm' => [
75 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
76 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
77 ],
78 '4m-no-smm-ms' => [
79 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
80 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
81 ],
82 '4m' => [
83 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
84 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
85 ],
86 '4m-ms' => [
87 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
88 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
89 ],
90 # FIXME: These are legacy 2MB-sized images that modern OVMF doesn't supports to build
91 # anymore. how can we deperacate this sanely without breaking existing instances, or using
92 # older backups and snapshot?
93 default => [
94 "$EDK2_FW_BASE/OVMF_CODE.fd",
95 "$EDK2_FW_BASE/OVMF_VARS.fd",
96 ],
97 },
98 aarch64 => {
99 default => [
100 "$EDK2_FW_BASE/AAVMF_CODE.fd",
101 "$EDK2_FW_BASE/AAVMF_VARS.fd",
102 ],
103 },
104 };
105
106 my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
107
108 # Note about locking: we use flock on the config file protect against concurent actions.
109 # Aditionaly, we have a 'lock' setting in the config file. This can be set to 'migrate',
110 # 'backup', 'snapshot' or 'rollback'. Most actions are not allowed when such lock is set.
111 # But you can ignore this kind of lock with the --skiplock flag.
112
113 cfs_register_file(
114 '/qemu-server/',
115 \&parse_vm_config,
116 \&write_vm_config
117 );
118
119 PVE::JSONSchema::register_standard_option('pve-qm-stateuri', {
120 description => "Some command save/restore state from this location.",
121 type => 'string',
122 maxLength => 128,
123 optional => 1,
124 });
125
126 PVE::JSONSchema::register_standard_option('pve-qemu-machine', {
127 description => "Specifies the QEMU machine type.",
128 type => 'string',
129 pattern => '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
130 maxLength => 40,
131 optional => 1,
132 });
133
134 # FIXME: remove in favor of just using the INotify one, it's cached there exactly the same way
135 my $nodename_cache;
136 sub nodename {
137 $nodename_cache //= PVE::INotify::nodename();
138 return $nodename_cache;
139 }
140
141 my $watchdog_fmt = {
142 model => {
143 default_key => 1,
144 type => 'string',
145 enum => [qw(i6300esb ib700)],
146 description => "Watchdog type to emulate.",
147 default => 'i6300esb',
148 optional => 1,
149 },
150 action => {
151 type => 'string',
152 enum => [qw(reset shutdown poweroff pause debug none)],
153 description => "The action to perform if after activation the guest fails to poll the watchdog in time.",
154 optional => 1,
155 },
156 };
157 PVE::JSONSchema::register_format('pve-qm-watchdog', $watchdog_fmt);
158
159 my $agent_fmt = {
160 enabled => {
161 description => "Enable/disable communication with a QEMU Guest Agent (QGA) running in the VM.",
162 type => 'boolean',
163 default => 0,
164 default_key => 1,
165 },
166 fstrim_cloned_disks => {
167 description => "Run fstrim after moving a disk or migrating the VM.",
168 type => 'boolean',
169 optional => 1,
170 default => 0,
171 },
172 'freeze-fs-on-backup' => {
173 description => "Freeze/thaw guest filesystems on backup for consistency.",
174 type => 'boolean',
175 optional => 1,
176 default => 1,
177 },
178 type => {
179 description => "Select the agent type",
180 type => 'string',
181 default => 'virtio',
182 optional => 1,
183 enum => [qw(virtio isa)],
184 },
185 };
186
187 my $vga_fmt = {
188 type => {
189 description => "Select the VGA type.",
190 type => 'string',
191 default => 'std',
192 optional => 1,
193 default_key => 1,
194 enum => [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio virtio-gl vmware)],
195 },
196 memory => {
197 description => "Sets the VGA memory (in MiB). Has no effect with serial display.",
198 type => 'integer',
199 optional => 1,
200 minimum => 4,
201 maximum => 512,
202 },
203 clipboard => {
204 description => 'Enable a specific clipboard. If not set, depending on'
205 .' the display type the SPICE one will be added.',
206 type => 'string',
207 enum => ['vnc'],
208 optional => 1,
209 },
210 };
211
212 my $ivshmem_fmt = {
213 size => {
214 type => 'integer',
215 minimum => 1,
216 description => "The size of the file in MB.",
217 },
218 name => {
219 type => 'string',
220 pattern => '[a-zA-Z0-9\-]+',
221 optional => 1,
222 format_description => 'string',
223 description => "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
224 },
225 };
226
227 my $audio_fmt = {
228 device => {
229 type => 'string',
230 enum => [qw(ich9-intel-hda intel-hda AC97)],
231 description => "Configure an audio device."
232 },
233 driver => {
234 type => 'string',
235 enum => ['spice', 'none'],
236 default => 'spice',
237 optional => 1,
238 description => "Driver backend for the audio device."
239 },
240 };
241
242 my $spice_enhancements_fmt = {
243 foldersharing => {
244 type => 'boolean',
245 optional => 1,
246 default => '0',
247 description => "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
248 },
249 videostreaming => {
250 type => 'string',
251 enum => ['off', 'all', 'filter'],
252 default => 'off',
253 optional => 1,
254 description => "Enable video streaming. Uses compression for detected video streams."
255 },
256 };
257
258 my $rng_fmt = {
259 source => {
260 type => 'string',
261 enum => ['/dev/urandom', '/dev/random', '/dev/hwrng'],
262 default_key => 1,
263 description => "The file on the host to gather entropy from. In most cases '/dev/urandom'"
264 ." should be preferred over '/dev/random' to avoid entropy-starvation issues on the"
265 ." host. Using urandom does *not* decrease security in any meaningful way, as it's"
266 ." still seeded from real entropy, and the bytes provided will most likely be mixed"
267 ." with real entropy on the guest as well. '/dev/hwrng' can be used to pass through"
268 ." a hardware RNG from the host.",
269 },
270 max_bytes => {
271 type => 'integer',
272 description => "Maximum bytes of entropy allowed to get injected into the guest every"
273 ." 'period' milliseconds. Prefer a lower value when using '/dev/random' as source. Use"
274 ." `0` to disable limiting (potentially dangerous!).",
275 optional => 1,
276
277 # default is 1 KiB/s, provides enough entropy to the guest to avoid boot-starvation issues
278 # (e.g. systemd etc...) while allowing no chance of overwhelming the host, provided we're
279 # reading from /dev/urandom
280 default => 1024,
281 },
282 period => {
283 type => 'integer',
284 description => "Every 'period' milliseconds the entropy-injection quota is reset, allowing"
285 ." the guest to retrieve another 'max_bytes' of entropy.",
286 optional => 1,
287 default => 1000,
288 },
289 };
290
291 my $meta_info_fmt = {
292 'ctime' => {
293 type => 'integer',
294 description => "The guest creation timestamp as UNIX epoch time",
295 minimum => 0,
296 optional => 1,
297 },
298 'creation-qemu' => {
299 type => 'string',
300 description => "The QEMU (machine) version from the time this VM was created.",
301 pattern => '\d+(\.\d+)+',
302 optional => 1,
303 },
304 };
305
306 my $confdesc = {
307 onboot => {
308 optional => 1,
309 type => 'boolean',
310 description => "Specifies whether a VM will be started during system bootup.",
311 default => 0,
312 },
313 autostart => {
314 optional => 1,
315 type => 'boolean',
316 description => "Automatic restart after crash (currently ignored).",
317 default => 0,
318 },
319 hotplug => {
320 optional => 1,
321 type => 'string', format => 'pve-hotplug-features',
322 description => "Selectively enable hotplug features. This is a comma separated list of"
323 ." hotplug features: 'network', 'disk', 'cpu', 'memory', 'usb' and 'cloudinit'. Use '0' to disable"
324 ." hotplug completely. Using '1' as value is an alias for the default `network,disk,usb`."
325 ." USB hotplugging is possible for guests with machine version >= 7.1 and ostype l26 or"
326 ." windows > 7.",
327 default => 'network,disk,usb',
328 },
329 reboot => {
330 optional => 1,
331 type => 'boolean',
332 description => "Allow reboot. If set to '0' the VM exit on reboot.",
333 default => 1,
334 },
335 lock => {
336 optional => 1,
337 type => 'string',
338 description => "Lock/unlock the VM.",
339 enum => [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
340 },
341 cpulimit => {
342 optional => 1,
343 type => 'number',
344 description => "Limit of CPU usage.",
345 verbose_description => "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has"
346 ." total of '2' CPU time. Value '0' indicates no CPU limit.",
347 minimum => 0,
348 maximum => 128,
349 default => 0,
350 },
351 cpuunits => {
352 optional => 1,
353 type => 'integer',
354 description => "CPU weight for a VM, will be clamped to [1, 10000] in cgroup v2.",
355 verbose_description => "CPU weight for a VM. Argument is used in the kernel fair scheduler."
356 ." The larger the number is, the more CPU time this VM gets. Number is relative to"
357 ." weights of all the other running VMs.",
358 minimum => 1,
359 maximum => 262144,
360 default => 'cgroup v1: 1024, cgroup v2: 100',
361 },
362 memory => {
363 optional => 1,
364 type => 'string',
365 description => "Memory properties.",
366 format => $PVE::QemuServer::Memory::memory_fmt
367 },
368 balloon => {
369 optional => 1,
370 type => 'integer',
371 description => "Amount of target RAM for the VM in MiB. Using zero disables the ballon driver.",
372 minimum => 0,
373 },
374 shares => {
375 optional => 1,
376 type => 'integer',
377 description => "Amount of memory shares for auto-ballooning. The larger the number is, the"
378 ." more memory this VM gets. Number is relative to weights of all other running VMs."
379 ." Using zero disables auto-ballooning. Auto-ballooning is done by pvestatd.",
380 minimum => 0,
381 maximum => 50000,
382 default => 1000,
383 },
384 keyboard => {
385 optional => 1,
386 type => 'string',
387 description => "Keyboard layout for VNC server. This option is generally not required and"
388 ." is often better handled from within the guest OS.",
389 enum => PVE::Tools::kvmkeymaplist(),
390 default => undef,
391 },
392 name => {
393 optional => 1,
394 type => 'string', format => 'dns-name',
395 description => "Set a name for the VM. Only used on the configuration web interface.",
396 },
397 scsihw => {
398 optional => 1,
399 type => 'string',
400 description => "SCSI controller model",
401 enum => [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
402 default => 'lsi',
403 },
404 description => {
405 optional => 1,
406 type => 'string',
407 description => "Description for the VM. Shown in the web-interface VM's summary."
408 ." This is saved as comment inside the configuration file.",
409 maxLength => 1024 * 8,
410 },
411 ostype => {
412 optional => 1,
413 type => 'string',
414 enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 win11 l24 l26 solaris)],
415 description => "Specify guest operating system.",
416 verbose_description => <<EODESC,
417 Specify guest operating system. This is used to enable special
418 optimization/features for specific operating systems:
419
420 [horizontal]
421 other;; unspecified OS
422 wxp;; Microsoft Windows XP
423 w2k;; Microsoft Windows 2000
424 w2k3;; Microsoft Windows 2003
425 w2k8;; Microsoft Windows 2008
426 wvista;; Microsoft Windows Vista
427 win7;; Microsoft Windows 7
428 win8;; Microsoft Windows 8/2012/2012r2
429 win10;; Microsoft Windows 10/2016/2019
430 win11;; Microsoft Windows 11/2022
431 l24;; Linux 2.4 Kernel
432 l26;; Linux 2.6 - 6.X Kernel
433 solaris;; Solaris/OpenSolaris/OpenIndiania kernel
434 EODESC
435 },
436 boot => {
437 optional => 1,
438 type => 'string', format => 'pve-qm-boot',
439 description => "Specify guest boot order. Use the 'order=' sub-property as usage with no"
440 ." key or 'legacy=' is deprecated.",
441 },
442 bootdisk => {
443 optional => 1,
444 type => 'string', format => 'pve-qm-bootdisk',
445 description => "Enable booting from specified disk. Deprecated: Use 'boot: order=foo;bar' instead.",
446 pattern => '(ide|sata|scsi|virtio)\d+',
447 },
448 smp => {
449 optional => 1,
450 type => 'integer',
451 description => "The number of CPUs. Please use option -sockets instead.",
452 minimum => 1,
453 default => 1,
454 },
455 sockets => {
456 optional => 1,
457 type => 'integer',
458 description => "The number of CPU sockets.",
459 minimum => 1,
460 default => 1,
461 },
462 cores => {
463 optional => 1,
464 type => 'integer',
465 description => "The number of cores per socket.",
466 minimum => 1,
467 default => 1,
468 },
469 numa => {
470 optional => 1,
471 type => 'boolean',
472 description => "Enable/disable NUMA.",
473 default => 0,
474 },
475 hugepages => {
476 optional => 1,
477 type => 'string',
478 description => "Enable/disable hugepages memory.",
479 enum => [qw(any 2 1024)],
480 },
481 keephugepages => {
482 optional => 1,
483 type => 'boolean',
484 default => 0,
485 description => "Use together with hugepages. If enabled, hugepages will not not be deleted"
486 ." after VM shutdown and can be used for subsequent starts.",
487 },
488 vcpus => {
489 optional => 1,
490 type => 'integer',
491 description => "Number of hotplugged vcpus.",
492 minimum => 1,
493 default => 0,
494 },
495 acpi => {
496 optional => 1,
497 type => 'boolean',
498 description => "Enable/disable ACPI.",
499 default => 1,
500 },
501 agent => {
502 optional => 1,
503 description => "Enable/disable communication with the QEMU Guest Agent and its properties.",
504 type => 'string',
505 format => $agent_fmt,
506 },
507 kvm => {
508 optional => 1,
509 type => 'boolean',
510 description => "Enable/disable KVM hardware virtualization.",
511 default => 1,
512 },
513 tdf => {
514 optional => 1,
515 type => 'boolean',
516 description => "Enable/disable time drift fix.",
517 default => 0,
518 },
519 localtime => {
520 optional => 1,
521 type => 'boolean',
522 description => "Set the real time clock (RTC) to local time. This is enabled by default if"
523 ." the `ostype` indicates a Microsoft Windows OS.",
524 },
525 freeze => {
526 optional => 1,
527 type => 'boolean',
528 description => "Freeze CPU at startup (use 'c' monitor command to start execution).",
529 },
530 vga => {
531 optional => 1,
532 type => 'string', format => $vga_fmt,
533 description => "Configure the VGA hardware.",
534 verbose_description => "Configure the VGA Hardware. If you want to use high resolution"
535 ." modes (>= 1280x1024x16) you may need to increase the vga memory option. Since QEMU"
536 ." 2.9 the default VGA display type is 'std' for all OS types besides some Windows"
537 ." versions (XP and older) which use 'cirrus'. The 'qxl' option enables the SPICE"
538 ." display server. For win* OS you can select how many independent displays you want,"
539 ." Linux guests can add displays them self.\nYou can also run without any graphic card,"
540 ." using a serial device as terminal.",
541 },
542 watchdog => {
543 optional => 1,
544 type => 'string', format => 'pve-qm-watchdog',
545 description => "Create a virtual hardware watchdog device.",
546 verbose_description => "Create a virtual hardware watchdog device. Once enabled (by a guest"
547 ." action), the watchdog must be periodically polled by an agent inside the guest or"
548 ." else the watchdog will reset the guest (or execute the respective action specified)",
549 },
550 startdate => {
551 optional => 1,
552 type => 'string',
553 typetext => "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
554 description => "Set the initial date of the real time clock. Valid format for date are:"
555 ."'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
556 pattern => '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
557 default => 'now',
558 },
559 startup => get_standard_option('pve-startup-order'),
560 template => {
561 optional => 1,
562 type => 'boolean',
563 description => "Enable/disable Template.",
564 default => 0,
565 },
566 args => {
567 optional => 1,
568 type => 'string',
569 description => "Arbitrary arguments passed to kvm.",
570 verbose_description => <<EODESCR,
571 Arbitrary arguments passed to kvm, for example:
572
573 args: -no-reboot -smbios 'type=0,vendor=FOO'
574
575 NOTE: this option is for experts only.
576 EODESCR
577 },
578 tablet => {
579 optional => 1,
580 type => 'boolean',
581 default => 1,
582 description => "Enable/disable the USB tablet device.",
583 verbose_description => "Enable/disable the USB tablet device. This device is usually needed"
584 ." to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with"
585 ." normal VNC clients. If you're running lots of console-only guests on one host, you"
586 ." may consider disabling this to save some context switches. This is turned off by"
587 ." default if you use spice (`qm set <vmid> --vga qxl`).",
588 },
589 migrate_speed => {
590 optional => 1,
591 type => 'integer',
592 description => "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
593 minimum => 0,
594 default => 0,
595 },
596 migrate_downtime => {
597 optional => 1,
598 type => 'number',
599 description => "Set maximum tolerated downtime (in seconds) for migrations.",
600 minimum => 0,
601 default => 0.1,
602 },
603 cdrom => {
604 optional => 1,
605 type => 'string', format => 'pve-qm-ide',
606 typetext => '<volume>',
607 description => "This is an alias for option -ide2",
608 },
609 cpu => {
610 optional => 1,
611 description => "Emulated CPU type.",
612 type => 'string',
613 format => 'pve-vm-cpu-conf',
614 },
615 parent => get_standard_option('pve-snapshot-name', {
616 optional => 1,
617 description => "Parent snapshot name. This is used internally, and should not be modified.",
618 }),
619 snaptime => {
620 optional => 1,
621 description => "Timestamp for snapshots.",
622 type => 'integer',
623 minimum => 0,
624 },
625 vmstate => {
626 optional => 1,
627 type => 'string', format => 'pve-volume-id',
628 description => "Reference to a volume which stores the VM state. This is used internally"
629 ." for snapshots.",
630 },
631 vmstatestorage => get_standard_option('pve-storage-id', {
632 description => "Default storage for VM state volumes/files.",
633 optional => 1,
634 }),
635 runningmachine => get_standard_option('pve-qemu-machine', {
636 description => "Specifies the QEMU machine type of the running vm. This is used internally"
637 ." for snapshots.",
638 }),
639 runningcpu => {
640 description => "Specifies the QEMU '-cpu' parameter of the running vm. This is used"
641 ." internally for snapshots.",
642 optional => 1,
643 type => 'string',
644 pattern => $PVE::QemuServer::CPUConfig::qemu_cmdline_cpu_re,
645 format_description => 'QEMU -cpu parameter'
646 },
647 machine => get_standard_option('pve-qemu-machine'),
648 arch => {
649 description => "Virtual processor architecture. Defaults to the host.",
650 optional => 1,
651 type => 'string',
652 enum => [qw(x86_64 aarch64)],
653 },
654 smbios1 => {
655 description => "Specify SMBIOS type 1 fields.",
656 type => 'string', format => 'pve-qm-smbios1',
657 maxLength => 512,
658 optional => 1,
659 },
660 protection => {
661 optional => 1,
662 type => 'boolean',
663 description => "Sets the protection flag of the VM. This will disable the remove VM and"
664 ." remove disk operations.",
665 default => 0,
666 },
667 bios => {
668 optional => 1,
669 type => 'string',
670 enum => [ qw(seabios ovmf) ],
671 description => "Select BIOS implementation.",
672 default => 'seabios',
673 },
674 vmgenid => {
675 type => 'string',
676 pattern => '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
677 format_description => 'UUID',
678 description => "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0'"
679 ." to disable explicitly.",
680 verbose_description => "The VM generation ID (vmgenid) device exposes a 128-bit integer"
681 ." value identifier to the guest OS. This allows to notify the guest operating system"
682 ." when the virtual machine is executed with a different configuration (e.g. snapshot"
683 ." execution or creation from a template). The guest operating system notices the"
684 ." change, and is then able to react as appropriate by marking its copies of"
685 ." distributed databases as dirty, re-initializing its random number generator, etc.\n"
686 ."Note that auto-creation only works when done through API/CLI create or update methods"
687 .", but not when manually editing the config file.",
688 default => "1 (autogenerated)",
689 optional => 1,
690 },
691 hookscript => {
692 type => 'string',
693 format => 'pve-volume-id',
694 optional => 1,
695 description => "Script that will be executed during various steps in the vms lifetime.",
696 },
697 ivshmem => {
698 type => 'string',
699 format => $ivshmem_fmt,
700 description => "Inter-VM shared memory. Useful for direct communication between VMs, or to"
701 ." the host.",
702 optional => 1,
703 },
704 audio0 => {
705 type => 'string',
706 format => $audio_fmt,
707 description => "Configure a audio device, useful in combination with QXL/Spice.",
708 optional => 1
709 },
710 spice_enhancements => {
711 type => 'string',
712 format => $spice_enhancements_fmt,
713 description => "Configure additional enhancements for SPICE.",
714 optional => 1
715 },
716 tags => {
717 type => 'string', format => 'pve-tag-list',
718 description => 'Tags of the VM. This is only meta information.',
719 optional => 1,
720 },
721 rng0 => {
722 type => 'string',
723 format => $rng_fmt,
724 description => "Configure a VirtIO-based Random Number Generator.",
725 optional => 1,
726 },
727 meta => {
728 type => 'string',
729 format => $meta_info_fmt,
730 description => "Some (read-only) meta-information about this guest.",
731 optional => 1,
732 },
733 affinity => {
734 type => 'string', format => 'pve-cpuset',
735 description => "List of host cores used to execute guest processes, for example: 0,5,8-11",
736 optional => 1,
737 },
738 };
739
740 my $cicustom_fmt = {
741 meta => {
742 type => 'string',
743 optional => 1,
744 description => 'Specify a custom file containing all meta data passed to the VM via"
745 ." cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
746 format => 'pve-volume-id',
747 format_description => 'volume',
748 },
749 network => {
750 type => 'string',
751 optional => 1,
752 description => 'To pass a custom file containing all network data to the VM via cloud-init.',
753 format => 'pve-volume-id',
754 format_description => 'volume',
755 },
756 user => {
757 type => 'string',
758 optional => 1,
759 description => 'To pass a custom file containing all user data to the VM via cloud-init.',
760 format => 'pve-volume-id',
761 format_description => 'volume',
762 },
763 vendor => {
764 type => 'string',
765 optional => 1,
766 description => 'To pass a custom file containing all vendor data to the VM via cloud-init.',
767 format => 'pve-volume-id',
768 format_description => 'volume',
769 },
770 };
771 PVE::JSONSchema::register_format('pve-qm-cicustom', $cicustom_fmt);
772
773 # any new option might need to be added to $cloudinitoptions in PVE::API2::Qemu
774 my $confdesc_cloudinit = {
775 citype => {
776 optional => 1,
777 type => 'string',
778 description => 'Specifies the cloud-init configuration format. The default depends on the'
779 .' configured operating system type (`ostype`. We use the `nocloud` format for Linux,'
780 .' and `configdrive2` for windows.',
781 enum => ['configdrive2', 'nocloud', 'opennebula'],
782 },
783 ciuser => {
784 optional => 1,
785 type => 'string',
786 description => "cloud-init: User name to change ssh keys and password for instead of the"
787 ." image's configured default user.",
788 },
789 cipassword => {
790 optional => 1,
791 type => 'string',
792 description => 'cloud-init: Password to assign the user. Using this is generally not'
793 .' recommended. Use ssh keys instead. Also note that older cloud-init versions do not'
794 .' support hashed passwords.',
795 },
796 ciupgrade => {
797 optional => 1,
798 type => 'boolean',
799 description => 'cloud-init: do an automatic package upgrade after the first boot.',
800 default => 1,
801 },
802 cicustom => {
803 optional => 1,
804 type => 'string',
805 description => 'cloud-init: Specify custom files to replace the automatically generated'
806 .' ones at start.',
807 format => 'pve-qm-cicustom',
808 },
809 searchdomain => {
810 optional => 1,
811 type => 'string',
812 description => 'cloud-init: Sets DNS search domains for a container. Create will'
813 .' automatically use the setting from the host if neither searchdomain nor nameserver'
814 .' are set.',
815 },
816 nameserver => {
817 optional => 1,
818 type => 'string', format => 'address-list',
819 description => 'cloud-init: Sets DNS server IP address for a container. Create will'
820 .' automatically use the setting from the host if neither searchdomain nor nameserver'
821 .' are set.',
822 },
823 sshkeys => {
824 optional => 1,
825 type => 'string',
826 format => 'urlencoded',
827 description => "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
828 },
829 };
830
831 # what about other qemu settings ?
832 #cpu => 'string',
833 #machine => 'string',
834 #fda => 'file',
835 #fdb => 'file',
836 #mtdblock => 'file',
837 #sd => 'file',
838 #pflash => 'file',
839 #snapshot => 'bool',
840 #bootp => 'file',
841 ##tftp => 'dir',
842 ##smb => 'dir',
843 #kernel => 'file',
844 #append => 'string',
845 #initrd => 'file',
846 ##soundhw => 'string',
847
848 while (my ($k, $v) = each %$confdesc) {
849 PVE::JSONSchema::register_standard_option("pve-qm-$k", $v);
850 }
851
852 my $MAX_NETS = 32;
853 my $MAX_SERIAL_PORTS = 4;
854 my $MAX_PARALLEL_PORTS = 3;
855
856 for (my $i = 0; $i < $PVE::QemuServer::Memory::MAX_NUMA; $i++) {
857 $confdesc->{"numa$i"} = $PVE::QemuServer::Memory::numadesc;
858 }
859
860 my $nic_model_list = [
861 'e1000',
862 'e1000-82540em',
863 'e1000-82544gc',
864 'e1000-82545em',
865 'e1000e',
866 'i82551',
867 'i82557b',
868 'i82559er',
869 'ne2k_isa',
870 'ne2k_pci',
871 'pcnet',
872 'rtl8139',
873 'virtio',
874 'vmxnet3',
875 ];
876 my $nic_model_list_txt = join(' ', sort @$nic_model_list);
877
878 my $net_fmt_bridge_descr = <<__EOD__;
879 Bridge to attach the network device to. The Proxmox VE standard bridge
880 is called 'vmbr0'.
881
882 If you do not specify a bridge, we create a kvm user (NATed) network
883 device, which provides DHCP and DNS services. The following addresses
884 are used:
885
886 10.0.2.2 Gateway
887 10.0.2.3 DNS Server
888 10.0.2.4 SMB Server
889
890 The DHCP server assign addresses to the guest starting from 10.0.2.15.
891 __EOD__
892
893 my $net_fmt = {
894 macaddr => get_standard_option('mac-addr', {
895 description => "MAC address. That address must be unique withing your network. This is"
896 ." automatically generated if not specified.",
897 }),
898 model => {
899 type => 'string',
900 description => "Network Card Model. The 'virtio' model provides the best performance with"
901 ." very low CPU overhead. If your guest does not support this driver, it is usually"
902 ." best to use 'e1000'.",
903 enum => $nic_model_list,
904 default_key => 1,
905 },
906 (map { $_ => { keyAlias => 'model', alias => 'macaddr' }} @$nic_model_list),
907 bridge => get_standard_option('pve-bridge-id', {
908 description => $net_fmt_bridge_descr,
909 optional => 1,
910 }),
911 queues => {
912 type => 'integer',
913 minimum => 0, maximum => 64,
914 description => 'Number of packet queues to be used on the device.',
915 optional => 1,
916 },
917 rate => {
918 type => 'number',
919 minimum => 0,
920 description => "Rate limit in mbps (megabytes per second) as floating point number.",
921 optional => 1,
922 },
923 tag => {
924 type => 'integer',
925 minimum => 1, maximum => 4094,
926 description => 'VLAN tag to apply to packets on this interface.',
927 optional => 1,
928 },
929 trunks => {
930 type => 'string',
931 pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
932 description => 'VLAN trunks to pass through this interface.',
933 format_description => 'vlanid[;vlanid...]',
934 optional => 1,
935 },
936 firewall => {
937 type => 'boolean',
938 description => 'Whether this interface should be protected by the firewall.',
939 optional => 1,
940 },
941 link_down => {
942 type => 'boolean',
943 description => 'Whether this interface should be disconnected (like pulling the plug).',
944 optional => 1,
945 },
946 mtu => {
947 type => 'integer',
948 minimum => 1, maximum => 65520,
949 description => "Force MTU, for VirtIO only. Set to '1' to use the bridge MTU",
950 optional => 1,
951 },
952 };
953
954 my $netdesc = {
955 optional => 1,
956 type => 'string', format => $net_fmt,
957 description => "Specify network devices.",
958 };
959
960 PVE::JSONSchema::register_standard_option("pve-qm-net", $netdesc);
961
962 my $ipconfig_fmt = {
963 ip => {
964 type => 'string',
965 format => 'pve-ipv4-config',
966 format_description => 'IPv4Format/CIDR',
967 description => 'IPv4 address in CIDR format.',
968 optional => 1,
969 default => 'dhcp',
970 },
971 gw => {
972 type => 'string',
973 format => 'ipv4',
974 format_description => 'GatewayIPv4',
975 description => 'Default gateway for IPv4 traffic.',
976 optional => 1,
977 requires => 'ip',
978 },
979 ip6 => {
980 type => 'string',
981 format => 'pve-ipv6-config',
982 format_description => 'IPv6Format/CIDR',
983 description => 'IPv6 address in CIDR format.',
984 optional => 1,
985 default => 'dhcp',
986 },
987 gw6 => {
988 type => 'string',
989 format => 'ipv6',
990 format_description => 'GatewayIPv6',
991 description => 'Default gateway for IPv6 traffic.',
992 optional => 1,
993 requires => 'ip6',
994 },
995 };
996 PVE::JSONSchema::register_format('pve-qm-ipconfig', $ipconfig_fmt);
997 my $ipconfigdesc = {
998 optional => 1,
999 type => 'string', format => 'pve-qm-ipconfig',
1000 description => <<'EODESCR',
1001 cloud-init: Specify IP addresses and gateways for the corresponding interface.
1002
1003 IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
1004
1005 The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit
1006 gateway should be provided.
1007 For IPv6 the special string 'auto' can be used to use stateless autoconfiguration. This requires
1008 cloud-init 19.4 or newer.
1009
1010 If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using
1011 dhcp on IPv4.
1012 EODESCR
1013 };
1014 PVE::JSONSchema::register_standard_option("pve-qm-ipconfig", $netdesc);
1015
1016 for (my $i = 0; $i < $MAX_NETS; $i++) {
1017 $confdesc->{"net$i"} = $netdesc;
1018 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
1019 }
1020
1021 foreach my $key (keys %$confdesc_cloudinit) {
1022 $confdesc->{$key} = $confdesc_cloudinit->{$key};
1023 }
1024
1025 PVE::JSONSchema::register_format('pve-cpuset', \&pve_verify_cpuset);
1026 sub pve_verify_cpuset {
1027 my ($set_text, $noerr) = @_;
1028
1029 my ($count, $members) = eval { PVE::CpuSet::parse_cpuset($set_text) };
1030
1031 if ($@) {
1032 return if $noerr;
1033 die "unable to parse cpuset option\n";
1034 }
1035
1036 return PVE::CpuSet->new($members)->short_string();
1037 }
1038
1039 PVE::JSONSchema::register_format('pve-volume-id-or-qm-path', \&verify_volume_id_or_qm_path);
1040 sub verify_volume_id_or_qm_path {
1041 my ($volid, $noerr) = @_;
1042
1043 return $volid if $volid eq 'none' || $volid eq 'cdrom';
1044
1045 return verify_volume_id_or_absolute_path($volid, $noerr);
1046 }
1047
1048 PVE::JSONSchema::register_format('pve-volume-id-or-absolute-path', \&verify_volume_id_or_absolute_path);
1049 sub verify_volume_id_or_absolute_path {
1050 my ($volid, $noerr) = @_;
1051
1052 return $volid if $volid =~ m|^/|;
1053
1054 $volid = eval { PVE::JSONSchema::check_format('pve-volume-id', $volid, '') };
1055 if ($@) {
1056 return if $noerr;
1057 die $@;
1058 }
1059 return $volid;
1060 }
1061
1062 my $serialdesc = {
1063 optional => 1,
1064 type => 'string',
1065 pattern => '(/dev/.+|socket)',
1066 description => "Create a serial device inside the VM (n is 0 to 3)",
1067 verbose_description => <<EODESCR,
1068 Create a serial device inside the VM (n is 0 to 3), and pass through a
1069 host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1070 host side (use 'qm terminal' to open a terminal connection).
1071
1072 NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines -
1073 use with special care.
1074
1075 CAUTION: Experimental! User reported problems with this option.
1076 EODESCR
1077 };
1078
1079 my $paralleldesc= {
1080 optional => 1,
1081 type => 'string',
1082 pattern => '/dev/parport\d+|/dev/usb/lp\d+',
1083 description => "Map host parallel devices (n is 0 to 2).",
1084 verbose_description => <<EODESCR,
1085 Map host parallel devices (n is 0 to 2).
1086
1087 NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1088 machines - use with special care.
1089
1090 CAUTION: Experimental! User reported problems with this option.
1091 EODESCR
1092 };
1093
1094 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1095 $confdesc->{"parallel$i"} = $paralleldesc;
1096 }
1097
1098 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1099 $confdesc->{"serial$i"} = $serialdesc;
1100 }
1101
1102 for (my $i = 0; $i < $PVE::QemuServer::PCI::MAX_HOSTPCI_DEVICES; $i++) {
1103 $confdesc->{"hostpci$i"} = $PVE::QemuServer::PCI::hostpcidesc;
1104 }
1105
1106 for my $key (keys %{$PVE::QemuServer::Drive::drivedesc_hash}) {
1107 $confdesc->{$key} = $PVE::QemuServer::Drive::drivedesc_hash->{$key};
1108 }
1109
1110 for (my $i = 0; $i < $PVE::QemuServer::USB::MAX_USB_DEVICES; $i++) {
1111 $confdesc->{"usb$i"} = $PVE::QemuServer::USB::usbdesc;
1112 }
1113
1114 my $boot_fmt = {
1115 legacy => {
1116 optional => 1,
1117 default_key => 1,
1118 type => 'string',
1119 description => "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)."
1120 . " Deprecated, use 'order=' instead.",
1121 pattern => '[acdn]{1,4}',
1122 format_description => "[acdn]{1,4}",
1123
1124 # note: this is also the fallback if boot: is not given at all
1125 default => 'cdn',
1126 },
1127 order => {
1128 optional => 1,
1129 type => 'string',
1130 format => 'pve-qm-bootdev-list',
1131 format_description => "device[;device...]",
1132 description => <<EODESC,
1133 The guest will attempt to boot from devices in the order they appear here.
1134
1135 Disks, optical drives and passed-through storage USB devices will be directly
1136 booted from, NICs will load PXE, and PCIe devices will either behave like disks
1137 (e.g. NVMe) or load an option ROM (e.g. RAID controller, hardware NIC).
1138
1139 Note that only devices in this list will be marked as bootable and thus loaded
1140 by the guest firmware (BIOS/UEFI). If you require multiple disks for booting
1141 (e.g. software-raid), you need to specify all of them here.
1142
1143 Overrides the deprecated 'legacy=[acdn]*' value when given.
1144 EODESC
1145 },
1146 };
1147 PVE::JSONSchema::register_format('pve-qm-boot', $boot_fmt);
1148
1149 PVE::JSONSchema::register_format('pve-qm-bootdev', \&verify_bootdev);
1150 sub verify_bootdev {
1151 my ($dev, $noerr) = @_;
1152
1153 my $special = $dev =~ m/^efidisk/ || $dev =~ m/^tpmstate/;
1154 return $dev if PVE::QemuServer::Drive::is_valid_drivename($dev) && !$special;
1155
1156 my $check = sub {
1157 my ($base) = @_;
1158 return 0 if $dev !~ m/^$base\d+$/;
1159 return 0 if !$confdesc->{$dev};
1160 return 1;
1161 };
1162
1163 return $dev if $check->("net");
1164 return $dev if $check->("usb");
1165 return $dev if $check->("hostpci");
1166
1167 return if $noerr;
1168 die "invalid boot device '$dev'\n";
1169 }
1170
1171 sub print_bootorder {
1172 my ($devs) = @_;
1173 return "" if !@$devs;
1174 my $data = { order => join(';', @$devs) };
1175 return PVE::JSONSchema::print_property_string($data, $boot_fmt);
1176 }
1177
1178 my $kvm_api_version = 0;
1179
1180 sub kvm_version {
1181 return $kvm_api_version if $kvm_api_version;
1182
1183 open my $fh, '<', '/dev/kvm' or return;
1184
1185 # 0xae00 => KVM_GET_API_VERSION
1186 $kvm_api_version = ioctl($fh, 0xae00, 0);
1187 close($fh);
1188
1189 return $kvm_api_version;
1190 }
1191
1192 my $kvm_user_version = {};
1193 my $kvm_mtime = {};
1194
1195 sub kvm_user_version {
1196 my ($binary) = @_;
1197
1198 $binary //= get_command_for_arch(get_host_arch()); # get the native arch by default
1199 my $st = stat($binary);
1200
1201 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1202 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1203 $cachedmtime == $st->mtime;
1204
1205 $kvm_user_version->{$binary} = 'unknown';
1206 $kvm_mtime->{$binary} = $st->mtime;
1207
1208 my $code = sub {
1209 my $line = shift;
1210 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1211 $kvm_user_version->{$binary} = $2;
1212 }
1213 };
1214
1215 eval { run_command([$binary, '--version'], outfunc => $code); };
1216 warn $@ if $@;
1217
1218 return $kvm_user_version->{$binary};
1219
1220 }
1221 my sub extract_version {
1222 my ($machine_type, $version) = @_;
1223 $version = kvm_user_version() if !defined($version);
1224 return PVE::QemuServer::Machine::extract_version($machine_type, $version)
1225 }
1226
1227 sub kernel_has_vhost_net {
1228 return -c '/dev/vhost-net';
1229 }
1230
1231 sub option_exists {
1232 my $key = shift;
1233 return defined($confdesc->{$key});
1234 }
1235
1236 my $cdrom_path;
1237 sub get_cdrom_path {
1238
1239 return $cdrom_path if defined($cdrom_path);
1240
1241 $cdrom_path = first { -l $_ } map { "/dev/cdrom$_" } ('', '1', '2');
1242
1243 if (!defined($cdrom_path)) {
1244 log_warn("no physical CD-ROM available, ignoring");
1245 $cdrom_path = '';
1246 }
1247
1248 return $cdrom_path;
1249 }
1250
1251 sub get_iso_path {
1252 my ($storecfg, $vmid, $cdrom) = @_;
1253
1254 if ($cdrom eq 'cdrom') {
1255 return get_cdrom_path();
1256 } elsif ($cdrom eq 'none') {
1257 return '';
1258 } elsif ($cdrom =~ m|^/|) {
1259 return $cdrom;
1260 } else {
1261 return PVE::Storage::path($storecfg, $cdrom);
1262 }
1263 }
1264
1265 # try to convert old style file names to volume IDs
1266 sub filename_to_volume_id {
1267 my ($vmid, $file, $media) = @_;
1268
1269 if (!($file eq 'none' || $file eq 'cdrom' ||
1270 $file =~ m|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
1271
1272 return if $file =~ m|/|;
1273
1274 if ($media && $media eq 'cdrom') {
1275 $file = "local:iso/$file";
1276 } else {
1277 $file = "local:$vmid/$file";
1278 }
1279 }
1280
1281 return $file;
1282 }
1283
1284 sub verify_media_type {
1285 my ($opt, $vtype, $media) = @_;
1286
1287 return if !$media;
1288
1289 my $etype;
1290 if ($media eq 'disk') {
1291 $etype = 'images';
1292 } elsif ($media eq 'cdrom') {
1293 $etype = 'iso';
1294 } else {
1295 die "internal error";
1296 }
1297
1298 return if ($vtype eq $etype);
1299
1300 raise_param_exc({ $opt => "unexpected media type ($vtype != $etype)" });
1301 }
1302
1303 sub cleanup_drive_path {
1304 my ($opt, $storecfg, $drive) = @_;
1305
1306 # try to convert filesystem paths to volume IDs
1307
1308 if (($drive->{file} !~ m/^(cdrom|none)$/) &&
1309 ($drive->{file} !~ m|^/dev/.+|) &&
1310 ($drive->{file} !~ m/^([^:]+):(.+)$/) &&
1311 ($drive->{file} !~ m/^\d+$/)) {
1312 my ($vtype, $volid) = PVE::Storage::path_to_volume_id($storecfg, $drive->{file});
1313 raise_param_exc({ $opt => "unable to associate path '$drive->{file}' to any storage"})
1314 if !$vtype;
1315 $drive->{media} = 'cdrom' if !$drive->{media} && $vtype eq 'iso';
1316 verify_media_type($opt, $vtype, $drive->{media});
1317 $drive->{file} = $volid;
1318 }
1319
1320 $drive->{media} = 'cdrom' if !$drive->{media} && $drive->{file} =~ m/^(cdrom|none)$/;
1321 }
1322
1323 sub parse_hotplug_features {
1324 my ($data) = @_;
1325
1326 my $res = {};
1327
1328 return $res if $data eq '0';
1329
1330 $data = $confdesc->{hotplug}->{default} if $data eq '1';
1331
1332 foreach my $feature (PVE::Tools::split_list($data)) {
1333 if ($feature =~ m/^(network|disk|cpu|memory|usb|cloudinit)$/) {
1334 $res->{$1} = 1;
1335 } else {
1336 die "invalid hotplug feature '$feature'\n";
1337 }
1338 }
1339 return $res;
1340 }
1341
1342 PVE::JSONSchema::register_format('pve-hotplug-features', \&pve_verify_hotplug_features);
1343 sub pve_verify_hotplug_features {
1344 my ($value, $noerr) = @_;
1345
1346 return $value if parse_hotplug_features($value);
1347
1348 return if $noerr;
1349
1350 die "unable to parse hotplug option\n";
1351 }
1352
1353 sub assert_clipboard_config {
1354 my ($vga) = @_;
1355
1356 my $clipboard_regex = qr/^(std|cirrus|vmware|virtio|qxl)/;
1357
1358 if (
1359 $vga->{'clipboard'}
1360 && $vga->{'clipboard'} eq 'vnc'
1361 && $vga->{type}
1362 && $vga->{type} !~ $clipboard_regex
1363 ) {
1364 die "vga type $vga->{type} is not compatible with VNC clipboard\n";
1365 }
1366 }
1367
1368 sub scsi_inquiry {
1369 my($fh, $noerr) = @_;
1370
1371 my $SG_IO = 0x2285;
1372 my $SG_GET_VERSION_NUM = 0x2282;
1373
1374 my $versionbuf = "\x00" x 8;
1375 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1376 if (!$ret) {
1377 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
1378 return;
1379 }
1380 my $version = unpack("I", $versionbuf);
1381 if ($version < 30000) {
1382 die "scsi generic interface too old\n" if !$noerr;
1383 return;
1384 }
1385
1386 my $buf = "\x00" x 36;
1387 my $sensebuf = "\x00" x 8;
1388 my $cmd = pack("C x3 C x1", 0x12, 36);
1389
1390 # see /usr/include/scsi/sg.h
1391 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";
1392
1393 my $packet = pack(
1394 $sg_io_hdr_t, ord('S'), -3, length($cmd), length($sensebuf), 0, length($buf), $buf, $cmd, $sensebuf, 6000
1395 );
1396
1397 $ret = ioctl($fh, $SG_IO, $packet);
1398 if (!$ret) {
1399 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
1400 return;
1401 }
1402
1403 my @res = unpack($sg_io_hdr_t, $packet);
1404 if ($res[17] || $res[18]) {
1405 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
1406 return;
1407 }
1408
1409 my $res = {};
1410 $res->@{qw(type removable vendor product revision)} = unpack("C C x6 A8 A16 A4", $buf);
1411
1412 $res->{removable} = $res->{removable} & 128 ? 1 : 0;
1413 $res->{type} &= 0x1F;
1414
1415 return $res;
1416 }
1417
1418 sub path_is_scsi {
1419 my ($path) = @_;
1420
1421 my $fh = IO::File->new("+<$path") || return;
1422 my $res = scsi_inquiry($fh, 1);
1423 close($fh);
1424
1425 return $res;
1426 }
1427
1428 sub print_tabletdevice_full {
1429 my ($conf, $arch) = @_;
1430
1431 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
1432
1433 # we use uhci for old VMs because tablet driver was buggy in older qemu
1434 my $usbbus;
1435 if ($q35 || $arch eq 'aarch64') {
1436 $usbbus = 'ehci';
1437 } else {
1438 $usbbus = 'uhci';
1439 }
1440
1441 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1442 }
1443
1444 sub print_keyboarddevice_full {
1445 my ($conf, $arch) = @_;
1446
1447 return if $arch ne 'aarch64';
1448
1449 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1450 }
1451
1452 my sub get_drive_id {
1453 my ($drive) = @_;
1454 return "$drive->{interface}$drive->{index}";
1455 }
1456
1457 sub print_drivedevice_full {
1458 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
1459
1460 my $device = '';
1461 my $maxdev = 0;
1462
1463 my $drive_id = get_drive_id($drive);
1464 if ($drive->{interface} eq 'virtio') {
1465 my $pciaddr = print_pci_addr("$drive_id", $bridges, $arch, $machine_type);
1466 $device = "virtio-blk-pci,drive=drive-$drive_id,id=${drive_id}${pciaddr}";
1467 $device .= ",iothread=iothread-$drive_id" if $drive->{iothread};
1468 } elsif ($drive->{interface} eq 'scsi') {
1469
1470 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive);
1471 my $unit = $drive->{index} % $maxdev;
1472 my $devicetype = 'hd';
1473 my $path = '';
1474 if (drive_is_cdrom($drive)) {
1475 $devicetype = 'cd';
1476 } else {
1477 if ($drive->{file} =~ m|^/|) {
1478 $path = $drive->{file};
1479 if (my $info = path_is_scsi($path)) {
1480 if ($info->{type} == 0 && $drive->{scsiblock}) {
1481 $devicetype = 'block';
1482 } elsif ($info->{type} == 1) { # tape
1483 $devicetype = 'generic';
1484 }
1485 }
1486 } else {
1487 $path = PVE::Storage::path($storecfg, $drive->{file});
1488 }
1489
1490 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
1491 my $version = extract_version($machine_type, kvm_user_version());
1492 if ($path =~ m/^iscsi\:\/\// &&
1493 !min_version($version, 4, 1)) {
1494 $devicetype = 'generic';
1495 }
1496 }
1497
1498 if (!$conf->{scsihw} || $conf->{scsihw} =~ m/^lsi/ || $conf->{scsihw} eq 'pvscsi') {
1499 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
1500 } else {
1501 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
1502 .",lun=$drive->{index}";
1503 }
1504 $device .= ",drive=drive-$drive_id,id=$drive_id";
1505
1506 if ($drive->{ssd} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1507 $device .= ",rotation_rate=1";
1508 }
1509 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn};
1510
1511 } elsif ($drive->{interface} eq 'ide' || $drive->{interface} eq 'sata') {
1512 my $maxdev = ($drive->{interface} eq 'sata') ? $PVE::QemuServer::Drive::MAX_SATA_DISKS : 2;
1513 my $controller = int($drive->{index} / $maxdev);
1514 my $unit = $drive->{index} % $maxdev;
1515
1516 # machine type q35 only supports unit=0 for IDE rather than 2 units. This wasn't handled
1517 # correctly before, so e.g. index=2 was mapped to controller=1,unit=0 rather than
1518 # controller=2,unit=0. Note that odd indices never worked, as they would be mapped to
1519 # unit=1, so to keep backwards compat for migration, it suffices to keep even ones as they
1520 # were before. Move odd ones up by 2 where they don't clash.
1521 if (PVE::QemuServer::Machine::machine_type_is_q35($conf) && $drive->{interface} eq 'ide') {
1522 $controller += 2 * ($unit % 2);
1523 $unit = 0;
1524 }
1525
1526 my $devicetype = ($drive->{media} && $drive->{media} eq 'cdrom') ? "cd" : "hd";
1527
1528 $device = "ide-$devicetype";
1529 if ($drive->{interface} eq 'ide') {
1530 $device .= ",bus=ide.$controller,unit=$unit";
1531 } else {
1532 $device .= ",bus=ahci$controller.$unit";
1533 }
1534 $device .= ",drive=drive-$drive_id,id=$drive_id";
1535
1536 if ($devicetype eq 'hd') {
1537 if (my $model = $drive->{model}) {
1538 $model = URI::Escape::uri_unescape($model);
1539 $device .= ",model=$model";
1540 }
1541 if ($drive->{ssd}) {
1542 $device .= ",rotation_rate=1";
1543 }
1544 }
1545 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn};
1546 } elsif ($drive->{interface} eq 'usb') {
1547 die "implement me";
1548 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1549 } else {
1550 die "unsupported interface type";
1551 }
1552
1553 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex};
1554
1555 if (my $serial = $drive->{serial}) {
1556 $serial = URI::Escape::uri_unescape($serial);
1557 $device .= ",serial=$serial";
1558 }
1559
1560
1561 return $device;
1562 }
1563
1564 sub get_initiator_name {
1565 my $initiator;
1566
1567 my $fh = IO::File->new('/etc/iscsi/initiatorname.iscsi') || return;
1568 while (defined(my $line = <$fh>)) {
1569 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
1570 $initiator = $1;
1571 last;
1572 }
1573 $fh->close();
1574
1575 return $initiator;
1576 }
1577
1578 my sub storage_allows_io_uring_default {
1579 my ($scfg, $cache_direct) = @_;
1580
1581 # io_uring with cache mode writeback or writethrough on krbd will hang...
1582 return if $scfg && $scfg->{type} eq 'rbd' && $scfg->{krbd} && !$cache_direct;
1583
1584 # io_uring with cache mode writeback or writethrough on LVM will hang, without cache only
1585 # sometimes, just plain disable...
1586 return if $scfg && $scfg->{type} eq 'lvm';
1587
1588 # io_uring causes problems when used with CIFS since kernel 5.15
1589 # Some discussion: https://www.spinics.net/lists/linux-cifs/msg26734.html
1590 return if $scfg && $scfg->{type} eq 'cifs';
1591
1592 return 1;
1593 }
1594
1595 my sub drive_uses_cache_direct {
1596 my ($drive, $scfg) = @_;
1597
1598 my $cache_direct = 0;
1599
1600 if (my $cache = $drive->{cache}) {
1601 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
1602 } elsif (!drive_is_cdrom($drive) && !($scfg && $scfg->{type} eq 'btrfs' && !$scfg->{nocow})) {
1603 $cache_direct = 1;
1604 }
1605
1606 return $cache_direct;
1607 }
1608
1609 sub print_drive_commandline_full {
1610 my ($storecfg, $vmid, $drive, $pbs_name, $io_uring) = @_;
1611
1612 my $path;
1613 my $volid = $drive->{file};
1614 my $format = $drive->{format};
1615 my $drive_id = get_drive_id($drive);
1616
1617 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
1618 my $scfg = $storeid ? PVE::Storage::storage_config($storecfg, $storeid) : undef;
1619
1620 if (drive_is_cdrom($drive)) {
1621 $path = get_iso_path($storecfg, $vmid, $volid);
1622 die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
1623 } else {
1624 if ($storeid) {
1625 $path = PVE::Storage::path($storecfg, $volid);
1626 $format //= qemu_img_format($scfg, $volname);
1627 } else {
1628 $path = $volid;
1629 $format //= "raw";
1630 }
1631 }
1632
1633 my $is_rbd = $path =~ m/^rbd:/;
1634
1635 my $opts = '';
1636 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1637 foreach my $o (@qemu_drive_options) {
1638 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
1639 }
1640
1641 # snapshot only accepts on|off
1642 if (defined($drive->{snapshot})) {
1643 my $v = $drive->{snapshot} ? 'on' : 'off';
1644 $opts .= ",snapshot=$v";
1645 }
1646
1647 if (defined($drive->{ro})) { # ro maps to QEMUs `readonly`, which accepts `on` or `off` only
1648 $opts .= ",readonly=" . ($drive->{ro} ? 'on' : 'off');
1649 }
1650
1651 foreach my $type (['', '-total'], [_rd => '-read'], [_wr => '-write']) {
1652 my ($dir, $qmpname) = @$type;
1653 if (my $v = $drive->{"mbps$dir"}) {
1654 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1655 }
1656 if (my $v = $drive->{"mbps${dir}_max"}) {
1657 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1658 }
1659 if (my $v = $drive->{"bps${dir}_max_length"}) {
1660 $opts .= ",throttling.bps$qmpname-max-length=$v";
1661 }
1662 if (my $v = $drive->{"iops${dir}"}) {
1663 $opts .= ",throttling.iops$qmpname=$v";
1664 }
1665 if (my $v = $drive->{"iops${dir}_max"}) {
1666 $opts .= ",throttling.iops$qmpname-max=$v";
1667 }
1668 if (my $v = $drive->{"iops${dir}_max_length"}) {
1669 $opts .= ",throttling.iops$qmpname-max-length=$v";
1670 }
1671 }
1672
1673 if ($pbs_name) {
1674 $format = "rbd" if $is_rbd;
1675 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1676 if !$format;
1677 $opts .= ",format=alloc-track,file.driver=$format";
1678 } elsif ($format) {
1679 $opts .= ",format=$format";
1680 }
1681
1682 my $cache_direct = drive_uses_cache_direct($drive, $scfg);
1683
1684 $opts .= ",cache=none" if !$drive->{cache} && $cache_direct;
1685
1686 if (!$drive->{aio}) {
1687 if ($io_uring && storage_allows_io_uring_default($scfg, $cache_direct)) {
1688 # io_uring supports all cache modes
1689 $opts .= ",aio=io_uring";
1690 } else {
1691 # aio native works only with O_DIRECT
1692 if($cache_direct) {
1693 $opts .= ",aio=native";
1694 } else {
1695 $opts .= ",aio=threads";
1696 }
1697 }
1698 }
1699
1700 if (!drive_is_cdrom($drive)) {
1701 my $detectzeroes;
1702 if (defined($drive->{detect_zeroes}) && !$drive->{detect_zeroes}) {
1703 $detectzeroes = 'off';
1704 } elsif ($drive->{discard}) {
1705 $detectzeroes = $drive->{discard} eq 'on' ? 'unmap' : 'on';
1706 } else {
1707 # This used to be our default with discard not being specified:
1708 $detectzeroes = 'on';
1709 }
1710
1711 # note: 'detect-zeroes' works per blockdev and we want it to persist
1712 # after the alloc-track is removed, so put it on 'file' directly
1713 my $dz_param = $pbs_name ? "file.detect-zeroes" : "detect-zeroes";
1714 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
1715 }
1716
1717 if ($pbs_name) {
1718 $opts .= ",backing=$pbs_name";
1719 $opts .= ",auto-remove=on";
1720 }
1721
1722 # my $file_param = $pbs_name ? "file.file.filename" : "file";
1723 my $file_param = "file";
1724 if ($pbs_name) {
1725 # non-rbd drivers require the underlying file to be a seperate block
1726 # node, so add a second .file indirection
1727 $file_param .= ".file" if !$is_rbd;
1728 $file_param .= ".filename";
1729 }
1730 my $pathinfo = $path ? "$file_param=$path," : '';
1731
1732 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1733 }
1734
1735 sub print_pbs_blockdev {
1736 my ($pbs_conf, $pbs_name) = @_;
1737 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1738 $blockdev .= ",repository=$pbs_conf->{repository}";
1739 $blockdev .= ",namespace=$pbs_conf->{namespace}" if $pbs_conf->{namespace};
1740 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1741 $blockdev .= ",archive=$pbs_conf->{archive}";
1742 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile};
1743 return $blockdev;
1744 }
1745
1746 sub print_netdevice_full {
1747 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version) = @_;
1748
1749 my $device = $net->{model};
1750 if ($net->{model} eq 'virtio') {
1751 $device = 'virtio-net-pci';
1752 };
1753
1754 my $pciaddr = print_pci_addr("$netid", $bridges, $arch, $machine_type);
1755 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
1756 if ($net->{queues} && $net->{queues} > 1 && $net->{model} eq 'virtio'){
1757 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1758 # and out of each queue plus one config interrupt and control vector queue
1759 my $vectors = $net->{queues} * 2 + 2;
1760 $tmpstr .= ",vectors=$vectors,mq=on";
1761 if (min_version($machine_version, 7, 1)) {
1762 $tmpstr .= ",packed=on";
1763 }
1764 }
1765
1766 if (min_version($machine_version, 7, 1) && $net->{model} eq 'virtio'){
1767 $tmpstr .= ",rx_queue_size=1024,tx_queue_size=256";
1768 }
1769
1770 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex} ;
1771
1772 if (my $mtu = $net->{mtu}) {
1773 if ($net->{model} eq 'virtio' && $net->{bridge}) {
1774 my $bridge_mtu = PVE::Network::read_bridge_mtu($net->{bridge});
1775 if ($mtu == 1) {
1776 $mtu = $bridge_mtu;
1777 } elsif ($mtu < 576) {
1778 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1779 } elsif ($mtu > $bridge_mtu) {
1780 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1781 }
1782 $tmpstr .= ",host_mtu=$mtu";
1783 } else {
1784 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
1785 }
1786 }
1787
1788 if ($use_old_bios_files) {
1789 my $romfile;
1790 if ($device eq 'virtio-net-pci') {
1791 $romfile = 'pxe-virtio.rom';
1792 } elsif ($device eq 'e1000') {
1793 $romfile = 'pxe-e1000.rom';
1794 } elsif ($device eq 'e1000e') {
1795 $romfile = 'pxe-e1000e.rom';
1796 } elsif ($device eq 'ne2k') {
1797 $romfile = 'pxe-ne2k_pci.rom';
1798 } elsif ($device eq 'pcnet') {
1799 $romfile = 'pxe-pcnet.rom';
1800 } elsif ($device eq 'rtl8139') {
1801 $romfile = 'pxe-rtl8139.rom';
1802 }
1803 $tmpstr .= ",romfile=$romfile" if $romfile;
1804 }
1805
1806 return $tmpstr;
1807 }
1808
1809 sub print_netdev_full {
1810 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
1811
1812 my $i = '';
1813 if ($netid =~ m/^net(\d+)$/) {
1814 $i = int($1);
1815 }
1816
1817 die "got strange net id '$i'\n" if $i >= ${MAX_NETS};
1818
1819 my $ifname = "tap${vmid}i$i";
1820
1821 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1822 die "interface name '$ifname' is too long (max 15 character)\n"
1823 if length($ifname) >= 16;
1824
1825 my $vhostparam = '';
1826 if (is_native($arch)) {
1827 $vhostparam = ',vhost=on' if kernel_has_vhost_net() && $net->{model} eq 'virtio';
1828 }
1829
1830 my $vmname = $conf->{name} || "vm$vmid";
1831
1832 my $netdev = "";
1833 my $script = $hotplug ? "pve-bridge-hotplug" : "pve-bridge";
1834
1835 if ($net->{bridge}) {
1836 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1837 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
1838 } else {
1839 $netdev = "type=user,id=$netid,hostname=$vmname";
1840 }
1841
1842 $netdev .= ",queues=$net->{queues}" if ($net->{queues} && $net->{model} eq 'virtio');
1843
1844 return $netdev;
1845 }
1846
1847 my $vga_map = {
1848 'cirrus' => 'cirrus-vga',
1849 'std' => 'VGA',
1850 'vmware' => 'vmware-svga',
1851 'virtio' => 'virtio-vga',
1852 'virtio-gl' => 'virtio-vga-gl',
1853 };
1854
1855 sub print_vga_device {
1856 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
1857
1858 my $type = $vga_map->{$vga->{type}};
1859 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
1860 $type = 'virtio-gpu';
1861 }
1862 my $vgamem_mb = $vga->{memory};
1863
1864 my $max_outputs = '';
1865 if ($qxlnum) {
1866 $type = $id ? 'qxl' : 'qxl-vga';
1867
1868 if (!$conf->{ostype} || $conf->{ostype} =~ m/^(?:l\d\d)|(?:other)$/) {
1869 # set max outputs so linux can have up to 4 qxl displays with one device
1870 if (min_version($machine_version, 4, 1)) {
1871 $max_outputs = ",max_outputs=4";
1872 }
1873 }
1874 }
1875
1876 die "no devicetype for $vga->{type}\n" if !$type;
1877
1878 my $memory = "";
1879 if ($vgamem_mb) {
1880 if ($vga->{type} =~ /^virtio/) {
1881 my $bytes = PVE::Tools::convert_size($vgamem_mb, "mb" => "b");
1882 $memory = ",max_hostmem=$bytes";
1883 } elsif ($qxlnum) {
1884 # from https://www.spice-space.org/multiple-monitors.html
1885 $memory = ",vgamem_mb=$vga->{memory}";
1886 my $ram = $vgamem_mb * 4;
1887 my $vram = $vgamem_mb * 2;
1888 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1889 } else {
1890 $memory = ",vgamem_mb=$vga->{memory}";
1891 }
1892 } elsif ($qxlnum && $id) {
1893 $memory = ",ram_size=67108864,vram_size=33554432";
1894 }
1895
1896 my $edidoff = "";
1897 if ($type eq 'VGA' && windows_version($conf->{ostype})) {
1898 $edidoff=",edid=off" if (!defined($conf->{bios}) || $conf->{bios} ne 'ovmf');
1899 }
1900
1901 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
1902 my $vgaid = "vga" . ($id // '');
1903 my $pciaddr;
1904 if ($q35 && $vgaid eq 'vga') {
1905 # the first display uses pcie.0 bus on q35 machines
1906 $pciaddr = print_pcie_addr($vgaid, $bridges, $arch, $machine);
1907 } else {
1908 $pciaddr = print_pci_addr($vgaid, $bridges, $arch, $machine);
1909 }
1910
1911 if ($vga->{type} eq 'virtio-gl') {
1912 my $base = '/usr/lib/x86_64-linux-gnu/lib';
1913 die "missing libraries for '$vga->{type}' detected! Please install 'libgl1' and 'libegl1'\n"
1914 if !-e "${base}EGL.so.1" || !-e "${base}GL.so.1";
1915
1916 die "no DRM render node detected (/dev/dri/renderD*), no GPU? - needed for '$vga->{type}' display\n"
1917 if !PVE::Tools::dir_glob_regex('/dev/dri/', "renderD.*");
1918 }
1919
1920 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
1921 }
1922
1923 # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1924 sub parse_net {
1925 my ($data, $disable_mac_autogen) = @_;
1926
1927 my $res = eval { parse_property_string($net_fmt, $data) };
1928 if ($@) {
1929 warn $@;
1930 return;
1931 }
1932 if (!defined($res->{macaddr}) && !$disable_mac_autogen) {
1933 my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
1934 $res->{macaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
1935 }
1936 return $res;
1937 }
1938
1939 # ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1940 sub parse_ipconfig {
1941 my ($data) = @_;
1942
1943 my $res = eval { parse_property_string($ipconfig_fmt, $data) };
1944 if ($@) {
1945 warn $@;
1946 return;
1947 }
1948
1949 if ($res->{gw} && !$res->{ip}) {
1950 warn 'gateway specified without specifying an IP address';
1951 return;
1952 }
1953 if ($res->{gw6} && !$res->{ip6}) {
1954 warn 'IPv6 gateway specified without specifying an IPv6 address';
1955 return;
1956 }
1957 if ($res->{gw} && $res->{ip} eq 'dhcp') {
1958 warn 'gateway specified together with DHCP';
1959 return;
1960 }
1961 if ($res->{gw6} && $res->{ip6} !~ /^$IPV6RE/) {
1962 # gw6 + auto/dhcp
1963 warn "IPv6 gateway specified together with $res->{ip6} address";
1964 return;
1965 }
1966
1967 if (!$res->{ip} && !$res->{ip6}) {
1968 return { ip => 'dhcp', ip6 => 'dhcp' };
1969 }
1970
1971 return $res;
1972 }
1973
1974 sub print_net {
1975 my $net = shift;
1976
1977 return PVE::JSONSchema::print_property_string($net, $net_fmt);
1978 }
1979
1980 sub add_random_macs {
1981 my ($settings) = @_;
1982
1983 foreach my $opt (keys %$settings) {
1984 next if $opt !~ m/^net(\d+)$/;
1985 my $net = parse_net($settings->{$opt});
1986 next if !$net;
1987 $settings->{$opt} = print_net($net);
1988 }
1989 }
1990
1991 sub vm_is_volid_owner {
1992 my ($storecfg, $vmid, $volid) = @_;
1993
1994 if ($volid !~ m|^/|) {
1995 my ($path, $owner);
1996 eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); };
1997 if ($owner && ($owner == $vmid)) {
1998 return 1;
1999 }
2000 }
2001
2002 return;
2003 }
2004
2005 sub vmconfig_register_unused_drive {
2006 my ($storecfg, $vmid, $conf, $drive) = @_;
2007
2008 if (drive_is_cloudinit($drive)) {
2009 eval { PVE::Storage::vdisk_free($storecfg, $drive->{file}) };
2010 warn $@ if $@;
2011 delete $conf->{cloudinit};
2012 } elsif (!drive_is_cdrom($drive)) {
2013 my $volid = $drive->{file};
2014 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
2015 PVE::QemuConfig->add_unused_volume($conf, $volid, $vmid);
2016 }
2017 }
2018 }
2019
2020 # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
2021 my $smbios1_fmt = {
2022 uuid => {
2023 type => 'string',
2024 pattern => '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
2025 format_description => 'UUID',
2026 description => "Set SMBIOS1 UUID.",
2027 optional => 1,
2028 },
2029 version => {
2030 type => 'string',
2031 pattern => '[A-Za-z0-9+\/]+={0,2}',
2032 format_description => 'Base64 encoded string',
2033 description => "Set SMBIOS1 version.",
2034 optional => 1,
2035 },
2036 serial => {
2037 type => 'string',
2038 pattern => '[A-Za-z0-9+\/]+={0,2}',
2039 format_description => 'Base64 encoded string',
2040 description => "Set SMBIOS1 serial number.",
2041 optional => 1,
2042 },
2043 manufacturer => {
2044 type => 'string',
2045 pattern => '[A-Za-z0-9+\/]+={0,2}',
2046 format_description => 'Base64 encoded string',
2047 description => "Set SMBIOS1 manufacturer.",
2048 optional => 1,
2049 },
2050 product => {
2051 type => 'string',
2052 pattern => '[A-Za-z0-9+\/]+={0,2}',
2053 format_description => 'Base64 encoded string',
2054 description => "Set SMBIOS1 product ID.",
2055 optional => 1,
2056 },
2057 sku => {
2058 type => 'string',
2059 pattern => '[A-Za-z0-9+\/]+={0,2}',
2060 format_description => 'Base64 encoded string',
2061 description => "Set SMBIOS1 SKU string.",
2062 optional => 1,
2063 },
2064 family => {
2065 type => 'string',
2066 pattern => '[A-Za-z0-9+\/]+={0,2}',
2067 format_description => 'Base64 encoded string',
2068 description => "Set SMBIOS1 family string.",
2069 optional => 1,
2070 },
2071 base64 => {
2072 type => 'boolean',
2073 description => 'Flag to indicate that the SMBIOS values are base64 encoded',
2074 optional => 1,
2075 },
2076 };
2077
2078 sub parse_smbios1 {
2079 my ($data) = @_;
2080
2081 my $res = eval { parse_property_string($smbios1_fmt, $data) };
2082 warn $@ if $@;
2083 return $res;
2084 }
2085
2086 sub print_smbios1 {
2087 my ($smbios1) = @_;
2088 return PVE::JSONSchema::print_property_string($smbios1, $smbios1_fmt);
2089 }
2090
2091 PVE::JSONSchema::register_format('pve-qm-smbios1', $smbios1_fmt);
2092
2093 sub parse_watchdog {
2094 my ($value) = @_;
2095
2096 return if !$value;
2097
2098 my $res = eval { parse_property_string($watchdog_fmt, $value) };
2099 warn $@ if $@;
2100 return $res;
2101 }
2102
2103 sub parse_guest_agent {
2104 my ($conf) = @_;
2105
2106 return {} if !defined($conf->{agent});
2107
2108 my $res = eval { parse_property_string($agent_fmt, $conf->{agent}) };
2109 warn $@ if $@;
2110
2111 # if the agent is disabled ignore the other potentially set properties
2112 return {} if !$res->{enabled};
2113 return $res;
2114 }
2115
2116 sub get_qga_key {
2117 my ($conf, $key) = @_;
2118 return undef if !defined($conf->{agent});
2119
2120 my $agent = parse_guest_agent($conf);
2121 return $agent->{$key};
2122 }
2123
2124 sub parse_vga {
2125 my ($value) = @_;
2126
2127 return {} if !$value;
2128 my $res = eval { parse_property_string($vga_fmt, $value) };
2129 warn $@ if $@;
2130 return $res;
2131 }
2132
2133 sub parse_rng {
2134 my ($value) = @_;
2135
2136 return if !$value;
2137
2138 my $res = eval { parse_property_string($rng_fmt, $value) };
2139 warn $@ if $@;
2140 return $res;
2141 }
2142
2143 sub parse_meta_info {
2144 my ($value) = @_;
2145
2146 return if !$value;
2147
2148 my $res = eval { parse_property_string($meta_info_fmt, $value) };
2149 warn $@ if $@;
2150 return $res;
2151 }
2152
2153 sub new_meta_info_string {
2154 my () = @_; # for now do not allow to override any value
2155
2156 return PVE::JSONSchema::print_property_string(
2157 {
2158 'creation-qemu' => kvm_user_version(),
2159 ctime => "". int(time()),
2160 },
2161 $meta_info_fmt
2162 );
2163 }
2164
2165 sub qemu_created_version_fixups {
2166 my ($conf, $forcemachine, $kvmver) = @_;
2167
2168 my $meta = parse_meta_info($conf->{meta}) // {};
2169 my $forced_vers = PVE::QemuServer::Machine::extract_version($forcemachine);
2170
2171 # check if we need to apply some handling for VMs that always use the latest machine version but
2172 # had a machine version transition happen that affected HW such that, e.g., an OS config change
2173 # would be required (we do not want to pin machine version for non-windows OS type)
2174 if (
2175 (!defined($conf->{machine}) || $conf->{machine} =~ m/^(?:pc|q35|virt)$/) # non-versioned machine
2176 && (!defined($meta->{'creation-qemu'}) || !min_version($meta->{'creation-qemu'}, 6, 1)) # created before 6.1
2177 && (!$forced_vers || min_version($forced_vers, 6, 1)) # handle snapshot-rollback/migrations
2178 && min_version($kvmver, 6, 1) # only need to apply the change since 6.1
2179 ) {
2180 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
2181 if ($q35 && $conf->{ostype} && $conf->{ostype} eq 'l26') {
2182 # this changed to default-on in Q 6.1 for q35 machines, it will mess with PCI slot view
2183 # and thus with the predictable interface naming of systemd
2184 return ['-global', 'ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off'];
2185 }
2186 }
2187 return;
2188 }
2189
2190 # add JSON properties for create and set function
2191 sub json_config_properties {
2192 my ($prop, $with_disk_alloc) = @_;
2193
2194 my $skip_json_config_opts = {
2195 parent => 1,
2196 snaptime => 1,
2197 vmstate => 1,
2198 runningmachine => 1,
2199 runningcpu => 1,
2200 meta => 1,
2201 };
2202
2203 foreach my $opt (keys %$confdesc) {
2204 next if $skip_json_config_opts->{$opt};
2205
2206 if ($with_disk_alloc && is_valid_drivename($opt)) {
2207 $prop->{$opt} = $PVE::QemuServer::Drive::drivedesc_hash_with_alloc->{$opt};
2208 } else {
2209 $prop->{$opt} = $confdesc->{$opt};
2210 }
2211 }
2212
2213 return $prop;
2214 }
2215
2216 # Properties that we can read from an OVF file
2217 sub json_ovf_properties {
2218 my $prop = {};
2219
2220 for my $device (PVE::QemuServer::Drive::valid_drive_names()) {
2221 $prop->{$device} = {
2222 type => 'string',
2223 format => 'pve-volume-id-or-absolute-path',
2224 description => "Disk image that gets imported to $device",
2225 optional => 1,
2226 };
2227 }
2228
2229 $prop->{cores} = {
2230 type => 'integer',
2231 description => "The number of CPU cores.",
2232 optional => 1,
2233 };
2234 $prop->{memory} = {
2235 type => 'integer',
2236 description => "Amount of RAM for the VM in MB.",
2237 optional => 1,
2238 };
2239 $prop->{name} = {
2240 type => 'string',
2241 description => "Name of the VM.",
2242 optional => 1,
2243 };
2244
2245 return $prop;
2246 }
2247
2248 # return copy of $confdesc_cloudinit to generate documentation
2249 sub cloudinit_config_properties {
2250
2251 return dclone($confdesc_cloudinit);
2252 }
2253
2254 sub cloudinit_pending_properties {
2255 my $p = {
2256 map { $_ => 1 } keys $confdesc_cloudinit->%*,
2257 name => 1,
2258 };
2259 $p->{"net$_"} = 1 for 0..($MAX_NETS-1);
2260 return $p;
2261 }
2262
2263 sub check_type {
2264 my ($key, $value) = @_;
2265
2266 die "unknown setting '$key'\n" if !$confdesc->{$key};
2267
2268 my $type = $confdesc->{$key}->{type};
2269
2270 if (!defined($value)) {
2271 die "got undefined value\n";
2272 }
2273
2274 if ($value =~ m/[\n\r]/) {
2275 die "property contains a line feed\n";
2276 }
2277
2278 if ($type eq 'boolean') {
2279 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2280 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2281 die "type check ('boolean') failed - got '$value'\n";
2282 } elsif ($type eq 'integer') {
2283 return int($1) if $value =~ m/^(\d+)$/;
2284 die "type check ('integer') failed - got '$value'\n";
2285 } elsif ($type eq 'number') {
2286 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2287 die "type check ('number') failed - got '$value'\n";
2288 } elsif ($type eq 'string') {
2289 if (my $fmt = $confdesc->{$key}->{format}) {
2290 PVE::JSONSchema::check_format($fmt, $value);
2291 return $value;
2292 }
2293 $value =~ s/^\"(.*)\"$/$1/;
2294 return $value;
2295 } else {
2296 die "internal error"
2297 }
2298 }
2299
2300 sub destroy_vm {
2301 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
2302
2303 my $conf = PVE::QemuConfig->load_config($vmid);
2304
2305 if (!$skiplock && !PVE::QemuConfig->has_lock($conf, 'suspended')) {
2306 PVE::QemuConfig->check_lock($conf);
2307 }
2308
2309 if ($conf->{template}) {
2310 # check if any base image is still used by a linked clone
2311 PVE::QemuConfig->foreach_volume_full($conf, { include_unused => 1 }, sub {
2312 my ($ds, $drive) = @_;
2313 return if drive_is_cdrom($drive);
2314
2315 my $volid = $drive->{file};
2316 return if !$volid || $volid =~ m|^/|;
2317
2318 die "base volume '$volid' is still in use by linked cloned\n"
2319 if PVE::Storage::volume_is_base_and_used($storecfg, $volid);
2320
2321 });
2322 }
2323
2324 my $volids = {};
2325 my $remove_owned_drive = sub {
2326 my ($ds, $drive) = @_;
2327 return if drive_is_cdrom($drive, 1);
2328
2329 my $volid = $drive->{file};
2330 return if !$volid || $volid =~ m|^/|;
2331 return if $volids->{$volid};
2332
2333 my ($path, $owner) = PVE::Storage::path($storecfg, $volid);
2334 return if !$path || !$owner || ($owner != $vmid);
2335
2336 $volids->{$volid} = 1;
2337 eval { PVE::Storage::vdisk_free($storecfg, $volid) };
2338 warn "Could not remove disk '$volid', check manually: $@" if $@;
2339 };
2340
2341 # only remove disks owned by this VM (referenced in the config)
2342 my $include_opts = {
2343 include_unused => 1,
2344 extra_keys => ['vmstate'],
2345 };
2346 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2347
2348 for my $snap (values %{$conf->{snapshots}}) {
2349 next if !defined($snap->{vmstate});
2350 my $drive = PVE::QemuConfig->parse_volume('vmstate', $snap->{vmstate}, 1);
2351 next if !defined($drive);
2352 $remove_owned_drive->('vmstate', $drive);
2353 }
2354
2355 PVE::QemuConfig->foreach_volume_full($conf->{pending}, $include_opts, $remove_owned_drive);
2356
2357 if ($purge_unreferenced) { # also remove unreferenced disk
2358 my $vmdisks = PVE::Storage::vdisk_list($storecfg, undef, $vmid, undef, 'images');
2359 PVE::Storage::foreach_volid($vmdisks, sub {
2360 my ($volid, $sid, $volname, $d) = @_;
2361 eval { PVE::Storage::vdisk_free($storecfg, $volid) };
2362 warn $@ if $@;
2363 });
2364 }
2365
2366 eval { delete_ifaces_ipams_ips($conf, $vmid)};
2367 warn $@ if $@;
2368
2369 if (defined $replacement_conf) {
2370 PVE::QemuConfig->write_config($vmid, $replacement_conf);
2371 } else {
2372 PVE::QemuConfig->destroy_config($vmid);
2373 }
2374 }
2375
2376 sub parse_vm_config {
2377 my ($filename, $raw, $strict) = @_;
2378
2379 return if !defined($raw);
2380
2381 my $res = {
2382 digest => Digest::SHA::sha1_hex($raw),
2383 snapshots => {},
2384 pending => {},
2385 cloudinit => {},
2386 };
2387
2388 my $handle_error = sub {
2389 my ($msg) = @_;
2390
2391 if ($strict) {
2392 die $msg;
2393 } else {
2394 warn $msg;
2395 }
2396 };
2397
2398 $filename =~ m|/qemu-server/(\d+)\.conf$|
2399 || die "got strange filename '$filename'";
2400
2401 my $vmid = $1;
2402
2403 my $conf = $res;
2404 my $descr;
2405 my $finish_description = sub {
2406 if (defined($descr)) {
2407 $descr =~ s/\s+$//;
2408 $conf->{description} = $descr;
2409 }
2410 $descr = undef;
2411 };
2412 my $section = '';
2413
2414 my @lines = split(/\n/, $raw);
2415 foreach my $line (@lines) {
2416 next if $line =~ m/^\s*$/;
2417
2418 if ($line =~ m/^\[PENDING\]\s*$/i) {
2419 $section = 'pending';
2420 $finish_description->();
2421 $conf = $res->{$section} = {};
2422 next;
2423 } elsif ($line =~ m/^\[special:cloudinit\]\s*$/i) {
2424 $section = 'cloudinit';
2425 $finish_description->();
2426 $conf = $res->{$section} = {};
2427 next;
2428
2429 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
2430 $section = $1;
2431 $finish_description->();
2432 $conf = $res->{snapshots}->{$section} = {};
2433 next;
2434 }
2435
2436 if ($line =~ m/^\#(.*)$/) {
2437 $descr = '' if !defined($descr);
2438 $descr .= PVE::Tools::decode_text($1) . "\n";
2439 next;
2440 }
2441
2442 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
2443 $descr = '' if !defined($descr);
2444 $descr .= PVE::Tools::decode_text($2);
2445 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2446 $conf->{snapstate} = $1;
2447 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2448 my $key = $1;
2449 my $value = $2;
2450 $conf->{$key} = $value;
2451 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
2452 my $value = $1;
2453 if ($section eq 'pending') {
2454 $conf->{delete} = $value; # we parse this later
2455 } else {
2456 $handle_error->("vm $vmid - property 'delete' is only allowed in [PENDING]\n");
2457 }
2458 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
2459 my $key = $1;
2460 my $value = $2;
2461 if ($section eq 'cloudinit') {
2462 # ignore validation only used for informative purpose
2463 $conf->{$key} = $value;
2464 next;
2465 }
2466 eval { $value = check_type($key, $value); };
2467 if ($@) {
2468 $handle_error->("vm $vmid - unable to parse value of '$key' - $@");
2469 } else {
2470 $key = 'ide2' if $key eq 'cdrom';
2471 my $fmt = $confdesc->{$key}->{format};
2472 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
2473 my $v = parse_drive($key, $value);
2474 if (my $volid = filename_to_volume_id($vmid, $v->{file}, $v->{media})) {
2475 $v->{file} = $volid;
2476 $value = print_drive($v);
2477 } else {
2478 $handle_error->("vm $vmid - unable to parse value of '$key'\n");
2479 next;
2480 }
2481 }
2482
2483 $conf->{$key} = $value;
2484 }
2485 } else {
2486 $handle_error->("vm $vmid - unable to parse config: $line\n");
2487 }
2488 }
2489
2490 $finish_description->();
2491 delete $res->{snapstate}; # just to be sure
2492
2493 return $res;
2494 }
2495
2496 sub write_vm_config {
2497 my ($filename, $conf) = @_;
2498
2499 delete $conf->{snapstate}; # just to be sure
2500
2501 if ($conf->{cdrom}) {
2502 die "option ide2 conflicts with cdrom\n" if $conf->{ide2};
2503 $conf->{ide2} = $conf->{cdrom};
2504 delete $conf->{cdrom};
2505 }
2506
2507 # we do not use 'smp' any longer
2508 if ($conf->{sockets}) {
2509 delete $conf->{smp};
2510 } elsif ($conf->{smp}) {
2511 $conf->{sockets} = $conf->{smp};
2512 delete $conf->{cores};
2513 delete $conf->{smp};
2514 }
2515
2516 my $used_volids = {};
2517
2518 my $cleanup_config = sub {
2519 my ($cref, $pending, $snapname) = @_;
2520
2521 foreach my $key (keys %$cref) {
2522 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
2523 $key eq 'snapstate' || $key eq 'pending' || $key eq 'cloudinit';
2524 my $value = $cref->{$key};
2525 if ($key eq 'delete') {
2526 die "propertry 'delete' is only allowed in [PENDING]\n"
2527 if !$pending;
2528 # fixme: check syntax?
2529 next;
2530 }
2531 eval { $value = check_type($key, $value); };
2532 die "unable to parse value of '$key' - $@" if $@;
2533
2534 $cref->{$key} = $value;
2535
2536 if (!$snapname && is_valid_drivename($key)) {
2537 my $drive = parse_drive($key, $value);
2538 $used_volids->{$drive->{file}} = 1 if $drive && $drive->{file};
2539 }
2540 }
2541 };
2542
2543 &$cleanup_config($conf);
2544
2545 &$cleanup_config($conf->{pending}, 1);
2546
2547 foreach my $snapname (keys %{$conf->{snapshots}}) {
2548 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
2549 &$cleanup_config($conf->{snapshots}->{$snapname}, undef, $snapname);
2550 }
2551
2552 # remove 'unusedX' settings if we re-add a volume
2553 foreach my $key (keys %$conf) {
2554 my $value = $conf->{$key};
2555 if ($key =~ m/^unused/ && $used_volids->{$value}) {
2556 delete $conf->{$key};
2557 }
2558 }
2559
2560 my $generate_raw_config = sub {
2561 my ($conf, $pending) = @_;
2562
2563 my $raw = '';
2564
2565 # add description as comment to top of file
2566 if (defined(my $descr = $conf->{description})) {
2567 if ($descr) {
2568 foreach my $cl (split(/\n/, $descr)) {
2569 $raw .= '#' . PVE::Tools::encode_text($cl) . "\n";
2570 }
2571 } else {
2572 $raw .= "#\n" if $pending;
2573 }
2574 }
2575
2576 foreach my $key (sort keys %$conf) {
2577 next if $key =~ /^(digest|description|pending|cloudinit|snapshots)$/;
2578 $raw .= "$key: $conf->{$key}\n";
2579 }
2580 return $raw;
2581 };
2582
2583 my $raw = &$generate_raw_config($conf);
2584
2585 if (scalar(keys %{$conf->{pending}})){
2586 $raw .= "\n[PENDING]\n";
2587 $raw .= &$generate_raw_config($conf->{pending}, 1);
2588 }
2589
2590 if (scalar(keys %{$conf->{cloudinit}}) && PVE::QemuConfig->has_cloudinit($conf)){
2591 $raw .= "\n[special:cloudinit]\n";
2592 $raw .= &$generate_raw_config($conf->{cloudinit});
2593 }
2594
2595 foreach my $snapname (sort keys %{$conf->{snapshots}}) {
2596 $raw .= "\n[$snapname]\n";
2597 $raw .= &$generate_raw_config($conf->{snapshots}->{$snapname});
2598 }
2599
2600 return $raw;
2601 }
2602
2603 sub load_defaults {
2604
2605 my $res = {};
2606
2607 # we use static defaults from our JSON schema configuration
2608 foreach my $key (keys %$confdesc) {
2609 if (defined(my $default = $confdesc->{$key}->{default})) {
2610 $res->{$key} = $default;
2611 }
2612 }
2613
2614 return $res;
2615 }
2616
2617 sub config_list {
2618 my $vmlist = PVE::Cluster::get_vmlist();
2619 my $res = {};
2620 return $res if !$vmlist || !$vmlist->{ids};
2621 my $ids = $vmlist->{ids};
2622 my $nodename = nodename();
2623
2624 foreach my $vmid (keys %$ids) {
2625 my $d = $ids->{$vmid};
2626 next if !$d->{node} || $d->{node} ne $nodename;
2627 next if !$d->{type} || $d->{type} ne 'qemu';
2628 $res->{$vmid}->{exists} = 1;
2629 }
2630 return $res;
2631 }
2632
2633 # test if VM uses local resources (to prevent migration)
2634 sub check_local_resources {
2635 my ($conf, $noerr) = @_;
2636
2637 my @loc_res = ();
2638 my $mapped_res = [];
2639
2640 my $nodelist = PVE::Cluster::get_nodelist();
2641 my $pci_map = PVE::Mapping::PCI::config();
2642 my $usb_map = PVE::Mapping::USB::config();
2643
2644 my $missing_mappings_by_node = { map { $_ => [] } @$nodelist };
2645
2646 my $add_missing_mapping = sub {
2647 my ($type, $key, $id) = @_;
2648 for my $node (@$nodelist) {
2649 my $entry;
2650 if ($type eq 'pci') {
2651 $entry = PVE::Mapping::PCI::get_node_mapping($pci_map, $id, $node);
2652 } elsif ($type eq 'usb') {
2653 $entry = PVE::Mapping::USB::get_node_mapping($usb_map, $id, $node);
2654 }
2655 if (!scalar($entry->@*)) {
2656 push @{$missing_mappings_by_node->{$node}}, $key;
2657 }
2658 }
2659 };
2660
2661 push @loc_res, "hostusb" if $conf->{hostusb}; # old syntax
2662 push @loc_res, "hostpci" if $conf->{hostpci}; # old syntax
2663
2664 push @loc_res, "ivshmem" if $conf->{ivshmem};
2665
2666 foreach my $k (keys %$conf) {
2667 if ($k =~ m/^usb/) {
2668 my $entry = parse_property_string('pve-qm-usb', $conf->{$k});
2669 next if $entry->{host} =~ m/^spice$/i;
2670 if ($entry->{mapping}) {
2671 $add_missing_mapping->('usb', $k, $entry->{mapping});
2672 push @$mapped_res, $k;
2673 }
2674 }
2675 if ($k =~ m/^hostpci/) {
2676 my $entry = parse_property_string('pve-qm-hostpci', $conf->{$k});
2677 if ($entry->{mapping}) {
2678 $add_missing_mapping->('pci', $k, $entry->{mapping});
2679 push @$mapped_res, $k;
2680 }
2681 }
2682 # sockets are safe: they will recreated be on the target side post-migrate
2683 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
2684 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
2685 }
2686
2687 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
2688
2689 return wantarray ? (\@loc_res, $mapped_res, $missing_mappings_by_node) : \@loc_res;
2690 }
2691
2692 # check if used storages are available on all nodes (use by migrate)
2693 sub check_storage_availability {
2694 my ($storecfg, $conf, $node) = @_;
2695
2696 PVE::QemuConfig->foreach_volume($conf, sub {
2697 my ($ds, $drive) = @_;
2698
2699 my $volid = $drive->{file};
2700 return if !$volid;
2701
2702 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2703 return if !$sid;
2704
2705 # check if storage is available on both nodes
2706 my $scfg = PVE::Storage::storage_check_enabled($storecfg, $sid);
2707 PVE::Storage::storage_check_enabled($storecfg, $sid, $node);
2708
2709 my ($vtype) = PVE::Storage::parse_volname($storecfg, $volid);
2710
2711 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2712 if !$scfg->{content}->{$vtype};
2713 });
2714 }
2715
2716 # list nodes where all VM images are available (used by has_feature API)
2717 sub shared_nodes {
2718 my ($conf, $storecfg) = @_;
2719
2720 my $nodelist = PVE::Cluster::get_nodelist();
2721 my $nodehash = { map { $_ => 1 } @$nodelist };
2722 my $nodename = nodename();
2723
2724 PVE::QemuConfig->foreach_volume($conf, sub {
2725 my ($ds, $drive) = @_;
2726
2727 my $volid = $drive->{file};
2728 return if !$volid;
2729
2730 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2731 if ($storeid) {
2732 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
2733 if ($scfg->{disable}) {
2734 $nodehash = {};
2735 } elsif (my $avail = $scfg->{nodes}) {
2736 foreach my $node (keys %$nodehash) {
2737 delete $nodehash->{$node} if !$avail->{$node};
2738 }
2739 } elsif (!$scfg->{shared}) {
2740 foreach my $node (keys %$nodehash) {
2741 delete $nodehash->{$node} if $node ne $nodename
2742 }
2743 }
2744 }
2745 });
2746
2747 return $nodehash
2748 }
2749
2750 sub check_local_storage_availability {
2751 my ($conf, $storecfg) = @_;
2752
2753 my $nodelist = PVE::Cluster::get_nodelist();
2754 my $nodehash = { map { $_ => {} } @$nodelist };
2755
2756 PVE::QemuConfig->foreach_volume($conf, sub {
2757 my ($ds, $drive) = @_;
2758
2759 my $volid = $drive->{file};
2760 return if !$volid;
2761
2762 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2763 if ($storeid) {
2764 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
2765
2766 if ($scfg->{disable}) {
2767 foreach my $node (keys %$nodehash) {
2768 $nodehash->{$node}->{unavailable_storages}->{$storeid} = 1;
2769 }
2770 } elsif (my $avail = $scfg->{nodes}) {
2771 foreach my $node (keys %$nodehash) {
2772 if (!$avail->{$node}) {
2773 $nodehash->{$node}->{unavailable_storages}->{$storeid} = 1;
2774 }
2775 }
2776 }
2777 }
2778 });
2779
2780 foreach my $node (values %$nodehash) {
2781 if (my $unavail = $node->{unavailable_storages}) {
2782 $node->{unavailable_storages} = [ sort keys %$unavail ];
2783 }
2784 }
2785
2786 return $nodehash
2787 }
2788
2789 # Compat only, use assert_config_exists_on_node and vm_running_locally where possible
2790 sub check_running {
2791 my ($vmid, $nocheck, $node) = @_;
2792
2793 # $nocheck is set when called during a migration, in which case the config
2794 # file might still or already reside on the *other* node
2795 # - because rename has already happened, and current node is source
2796 # - because rename hasn't happened yet, and current node is target
2797 # - because rename has happened, current node is target, but hasn't yet
2798 # processed it yet
2799 PVE::QemuConfig::assert_config_exists_on_node($vmid, $node) if !$nocheck;
2800 return PVE::QemuServer::Helpers::vm_running_locally($vmid);
2801 }
2802
2803 sub vzlist {
2804
2805 my $vzlist = config_list();
2806
2807 my $fd = IO::Dir->new($PVE::QemuServer::Helpers::var_run_tmpdir) || return $vzlist;
2808
2809 while (defined(my $de = $fd->read)) {
2810 next if $de !~ m/^(\d+)\.pid$/;
2811 my $vmid = $1;
2812 next if !defined($vzlist->{$vmid});
2813 if (my $pid = check_running($vmid)) {
2814 $vzlist->{$vmid}->{pid} = $pid;
2815 }
2816 }
2817
2818 return $vzlist;
2819 }
2820
2821 our $vmstatus_return_properties = {
2822 vmid => get_standard_option('pve-vmid'),
2823 status => {
2824 description => "QEMU process status.",
2825 type => 'string',
2826 enum => ['stopped', 'running'],
2827 },
2828 maxmem => {
2829 description => "Maximum memory in bytes.",
2830 type => 'integer',
2831 optional => 1,
2832 renderer => 'bytes',
2833 },
2834 maxdisk => {
2835 description => "Root disk size in bytes.",
2836 type => 'integer',
2837 optional => 1,
2838 renderer => 'bytes',
2839 },
2840 name => {
2841 description => "VM name.",
2842 type => 'string',
2843 optional => 1,
2844 },
2845 qmpstatus => {
2846 description => "VM run state from the 'query-status' QMP monitor command.",
2847 type => 'string',
2848 optional => 1,
2849 },
2850 pid => {
2851 description => "PID of running qemu process.",
2852 type => 'integer',
2853 optional => 1,
2854 },
2855 uptime => {
2856 description => "Uptime.",
2857 type => 'integer',
2858 optional => 1,
2859 renderer => 'duration',
2860 },
2861 cpus => {
2862 description => "Maximum usable CPUs.",
2863 type => 'number',
2864 optional => 1,
2865 },
2866 lock => {
2867 description => "The current config lock, if any.",
2868 type => 'string',
2869 optional => 1,
2870 },
2871 tags => {
2872 description => "The current configured tags, if any",
2873 type => 'string',
2874 optional => 1,
2875 },
2876 'running-machine' => {
2877 description => "The currently running machine type (if running).",
2878 type => 'string',
2879 optional => 1,
2880 },
2881 'running-qemu' => {
2882 description => "The currently running QEMU version (if running).",
2883 type => 'string',
2884 optional => 1,
2885 },
2886 };
2887
2888 my $last_proc_pid_stat;
2889
2890 # get VM status information
2891 # This must be fast and should not block ($full == false)
2892 # We only query KVM using QMP if $full == true (this can be slow)
2893 sub vmstatus {
2894 my ($opt_vmid, $full) = @_;
2895
2896 my $res = {};
2897
2898 my $storecfg = PVE::Storage::config();
2899
2900 my $list = vzlist();
2901 my $defaults = load_defaults();
2902
2903 my ($uptime) = PVE::ProcFSTools::read_proc_uptime(1);
2904
2905 my $cpucount = $cpuinfo->{cpus} || 1;
2906
2907 foreach my $vmid (keys %$list) {
2908 next if $opt_vmid && ($vmid ne $opt_vmid);
2909
2910 my $conf = PVE::QemuConfig->load_config($vmid);
2911
2912 my $d = { vmid => int($vmid) };
2913 $d->{pid} = int($list->{$vmid}->{pid}) if $list->{$vmid}->{pid};
2914
2915 # fixme: better status?
2916 $d->{status} = $list->{$vmid}->{pid} ? 'running' : 'stopped';
2917
2918 my $size = PVE::QemuServer::Drive::bootdisk_size($storecfg, $conf);
2919 if (defined($size)) {
2920 $d->{disk} = 0; # no info available
2921 $d->{maxdisk} = $size;
2922 } else {
2923 $d->{disk} = 0;
2924 $d->{maxdisk} = 0;
2925 }
2926
2927 $d->{cpus} = ($conf->{sockets} || $defaults->{sockets})
2928 * ($conf->{cores} || $defaults->{cores});
2929 $d->{cpus} = $cpucount if $d->{cpus} > $cpucount;
2930 $d->{cpus} = $conf->{vcpus} if $conf->{vcpus};
2931
2932 $d->{name} = $conf->{name} || "VM $vmid";
2933 $d->{maxmem} = get_current_memory($conf->{memory})*(1024*1024);
2934
2935 if ($conf->{balloon}) {
2936 $d->{balloon_min} = $conf->{balloon}*(1024*1024);
2937 $d->{shares} = defined($conf->{shares}) ? $conf->{shares}
2938 : $defaults->{shares};
2939 }
2940
2941 $d->{uptime} = 0;
2942 $d->{cpu} = 0;
2943 $d->{mem} = 0;
2944
2945 $d->{netout} = 0;
2946 $d->{netin} = 0;
2947
2948 $d->{diskread} = 0;
2949 $d->{diskwrite} = 0;
2950
2951 $d->{template} = 1 if PVE::QemuConfig->is_template($conf);
2952
2953 $d->{serial} = 1 if conf_has_serial($conf);
2954 $d->{lock} = $conf->{lock} if $conf->{lock};
2955 $d->{tags} = $conf->{tags} if defined($conf->{tags});
2956
2957 $res->{$vmid} = $d;
2958 }
2959
2960 my $netdev = PVE::ProcFSTools::read_proc_net_dev();
2961 foreach my $dev (keys %$netdev) {
2962 next if $dev !~ m/^tap([1-9]\d*)i/;
2963 my $vmid = $1;
2964 my $d = $res->{$vmid};
2965 next if !$d;
2966
2967 $d->{netout} += $netdev->{$dev}->{receive};
2968 $d->{netin} += $netdev->{$dev}->{transmit};
2969
2970 if ($full) {
2971 $d->{nics}->{$dev}->{netout} = int($netdev->{$dev}->{receive});
2972 $d->{nics}->{$dev}->{netin} = int($netdev->{$dev}->{transmit});
2973 }
2974
2975 }
2976
2977 my $ctime = gettimeofday;
2978
2979 foreach my $vmid (keys %$list) {
2980
2981 my $d = $res->{$vmid};
2982 my $pid = $d->{pid};
2983 next if !$pid;
2984
2985 my $pstat = PVE::ProcFSTools::read_proc_pid_stat($pid);
2986 next if !$pstat; # not running
2987
2988 my $used = $pstat->{utime} + $pstat->{stime};
2989
2990 $d->{uptime} = int(($uptime - $pstat->{starttime})/$cpuinfo->{user_hz});
2991
2992 if ($pstat->{vsize}) {
2993 $d->{mem} = int(($pstat->{rss}/$pstat->{vsize})*$d->{maxmem});
2994 }
2995
2996 my $old = $last_proc_pid_stat->{$pid};
2997 if (!$old) {
2998 $last_proc_pid_stat->{$pid} = {
2999 time => $ctime,
3000 used => $used,
3001 cpu => 0,
3002 };
3003 next;
3004 }
3005
3006 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz};
3007
3008 if ($dtime > 1000) {
3009 my $dutime = $used - $old->{used};
3010
3011 $d->{cpu} = (($dutime/$dtime)* $cpucount) / $d->{cpus};
3012 $last_proc_pid_stat->{$pid} = {
3013 time => $ctime,
3014 used => $used,
3015 cpu => $d->{cpu},
3016 };
3017 } else {
3018 $d->{cpu} = $old->{cpu};
3019 }
3020 }
3021
3022 return $res if !$full;
3023
3024 my $qmpclient = PVE::QMPClient->new();
3025
3026 my $ballooncb = sub {
3027 my ($vmid, $resp) = @_;
3028
3029 my $info = $resp->{'return'};
3030 return if !$info->{max_mem};
3031
3032 my $d = $res->{$vmid};
3033
3034 # use memory assigned to VM
3035 $d->{maxmem} = $info->{max_mem};
3036 $d->{balloon} = $info->{actual};
3037
3038 if (defined($info->{total_mem}) && defined($info->{free_mem})) {
3039 $d->{mem} = $info->{total_mem} - $info->{free_mem};
3040 $d->{freemem} = $info->{free_mem};
3041 }
3042
3043 $d->{ballooninfo} = $info;
3044 };
3045
3046 my $blockstatscb = sub {
3047 my ($vmid, $resp) = @_;
3048 my $data = $resp->{'return'} || [];
3049 my $totalrdbytes = 0;
3050 my $totalwrbytes = 0;
3051
3052 for my $blockstat (@$data) {
3053 $totalrdbytes = $totalrdbytes + $blockstat->{stats}->{rd_bytes};
3054 $totalwrbytes = $totalwrbytes + $blockstat->{stats}->{wr_bytes};
3055
3056 $blockstat->{device} =~ s/drive-//;
3057 $res->{$vmid}->{blockstat}->{$blockstat->{device}} = $blockstat->{stats};
3058 }
3059 $res->{$vmid}->{diskread} = $totalrdbytes;
3060 $res->{$vmid}->{diskwrite} = $totalwrbytes;
3061 };
3062
3063 my $machinecb = sub {
3064 my ($vmid, $resp) = @_;
3065 my $data = $resp->{'return'} || [];
3066
3067 $res->{$vmid}->{'running-machine'} =
3068 PVE::QemuServer::Machine::current_from_query_machines($data);
3069 };
3070
3071 my $versioncb = sub {
3072 my ($vmid, $resp) = @_;
3073 my $data = $resp->{'return'} // {};
3074 my $version = 'unknown';
3075
3076 if (my $v = $data->{qemu}) {
3077 $version = $v->{major} . "." . $v->{minor} . "." . $v->{micro};
3078 }
3079
3080 $res->{$vmid}->{'running-qemu'} = $version;
3081 };
3082
3083 my $statuscb = sub {
3084 my ($vmid, $resp) = @_;
3085
3086 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
3087 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
3088 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
3089 # this fails if ballon driver is not loaded, so this must be
3090 # the last commnand (following command are aborted if this fails).
3091 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
3092