]> git.proxmox.com Git - qemu-server.git/blame - PVE/QemuServer.pm
cloudinit: avoid unsafe write of VM config
[qemu-server.git] / PVE / QemuServer.pm
CommitLineData
baa4f62d 1package PVE::QemuServer;
1e3baf05
DM
2
3use strict;
990fc5e2 4use warnings;
3ff84d6f 5
5da072fb
TL
6use Cwd 'abs_path';
7use Digest::SHA;
8use Fcntl ':flock';
9use Fcntl;
1e3baf05 10use File::Basename;
5da072fb 11use File::Copy qw(copy);
1e3baf05
DM
12use File::Path;
13use File::stat;
14use Getopt::Long;
5da072fb
TL
15use IO::Dir;
16use IO::File;
17use IO::Handle;
18use IO::Select;
19use IO::Socket::UNIX;
1e3baf05 20use IPC::Open3;
c971c4f2 21use JSON;
1f30ac3a 22use MIME::Base64;
5da072fb
TL
23use POSIX;
24use Storable qw(dclone);
f85951dc 25use Time::HiRes qw(gettimeofday usleep);
5da072fb 26use URI::Escape;
425441e6 27use UUID;
5da072fb 28
82841214 29use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file);
5b65b00d 30use PVE::CGroup;
83870398 31use PVE::CpuSet;
48cf040f 32use PVE::DataCenterConfig;
5da072fb 33use PVE::Exception qw(raise raise_param_exc);
3b56383b 34use PVE::Format qw(render_duration render_bytes);
81d6e4e1 35use PVE::GuestHelpers qw(safe_string_ne safe_num_ne safe_boolean_ne);
1e3baf05 36use PVE::INotify;
4df98f2f 37use PVE::JSONSchema qw(get_standard_option parse_property_string);
1e3baf05 38use PVE::ProcFSTools;
fbec3f89 39use PVE::PBSClient;
34e82fa2 40use PVE::RESTEnvironment qw(log_warn);
91bd6c90 41use PVE::RPCEnvironment;
5da072fb 42use PVE::Storage;
b71351a7 43use PVE::SysFSTools;
d04d6af1 44use PVE::Systemd;
82841214 45use PVE::Tools qw(run_command file_read_firstline file_get_contents dir_glob_foreach get_host_arch $IPV6RE);
5da072fb
TL
46
47use PVE::QMPClient;
48use PVE::QemuConfig;
238af88e 49use PVE::QemuServer::Helpers qw(min_version config_aware_timeout windows_version);
5da072fb 50use PVE::QemuServer::Cloudinit;
5b65b00d 51use PVE::QemuServer::CGroup;
d786a274 52use PVE::QemuServer::CPUConfig qw(print_cpu_device get_cpu_options);
75748d44 53use PVE::QemuServer::Drive qw(is_valid_drivename drive_is_cloudinit drive_is_cdrom drive_is_read_only parse_drive print_drive);
2ea5fb7e 54use PVE::QemuServer::Machine;
5da072fb 55use PVE::QemuServer::Memory;
0a13e08e 56use PVE::QemuServer::Monitor qw(mon_cmd);
74c17b7a 57use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port parse_hostpci);
5da072fb 58use PVE::QemuServer::USB qw(parse_usb_device);
1e3baf05 59
28e129cc
AD
60my $have_sdn;
61eval {
62 require PVE::Network::SDN::Zones;
63 $have_sdn = 1;
64};
65
102cf9d8 66my $EDK2_FW_BASE = '/usr/share/pve-edk2-firmware/';
96ed3574 67my $OVMF = {
b5099b4f 68 x86_64 => {
90b20b15
DC
69 '4m-no-smm' => [
70 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
71 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
72 ],
73 '4m-no-smm-ms' => [
74 "$EDK2_FW_BASE/OVMF_CODE_4M.fd",
75 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
76 ],
b5099b4f
SR
77 '4m' => [
78 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
79 "$EDK2_FW_BASE/OVMF_VARS_4M.fd",
80 ],
81 '4m-ms' => [
82 "$EDK2_FW_BASE/OVMF_CODE_4M.secboot.fd",
83 "$EDK2_FW_BASE/OVMF_VARS_4M.ms.fd",
84 ],
85 default => [
86 "$EDK2_FW_BASE/OVMF_CODE.fd",
87 "$EDK2_FW_BASE/OVMF_VARS.fd",
88 ],
89 },
90 aarch64 => {
91 default => [
92 "$EDK2_FW_BASE/AAVMF_CODE.fd",
93 "$EDK2_FW_BASE/AAVMF_VARS.fd",
94 ],
95 },
96ed3574 96};
2ddc0a5c 97
7f0b5beb 98my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
1e3baf05 99
8d88a594
TL
100# Note about locking: we use flock on the config file protect against concurent actions.
101# Aditionaly, we have a 'lock' setting in the config file. This can be set to 'migrate',
102# 'backup', 'snapshot' or 'rollback'. Most actions are not allowed when such lock is set.
103# But you can ignore this kind of lock with the --skiplock flag.
1e3baf05 104
97d62eb7 105cfs_register_file('/qemu-server/',
1858638f
DM
106 \&parse_vm_config,
107 \&write_vm_config);
1e3baf05 108
3ea94c60
DM
109PVE::JSONSchema::register_standard_option('pve-qm-stateuri', {
110 description => "Some command save/restore state from this location.",
111 type => 'string',
112 maxLength => 128,
113 optional => 1,
114});
115
c6737ef1
DC
116PVE::JSONSchema::register_standard_option('pve-qemu-machine', {
117 description => "Specifies the Qemu machine type.",
118 type => 'string',
9471e48b 119 pattern => '(pc|pc(-i440fx)?-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|q35|pc-q35-\d+(\.\d+)+(\+pve\d+)?(\.pxe)?|virt(?:-\d+(\.\d+)+)?(\+pve\d+)?)',
c6737ef1
DC
120 maxLength => 40,
121 optional => 1,
122});
123
1a67f999 124# FIXME: remove in favor of just using the INotify one, it's cached there exactly the same way
38277afc
TL
125my $nodename_cache;
126sub nodename {
127 $nodename_cache //= PVE::INotify::nodename();
128 return $nodename_cache;
129}
1e3baf05 130
ec3582b5
WB
131my $watchdog_fmt = {
132 model => {
133 default_key => 1,
134 type => 'string',
135 enum => [qw(i6300esb ib700)],
136 description => "Watchdog type to emulate.",
137 default => 'i6300esb',
138 optional => 1,
139 },
140 action => {
141 type => 'string',
142 enum => [qw(reset shutdown poweroff pause debug none)],
143 description => "The action to perform if after activation the guest fails to poll the watchdog in time.",
144 optional => 1,
145 },
146};
147PVE::JSONSchema::register_format('pve-qm-watchdog', $watchdog_fmt);
148
9d66b397
SI
149my $agent_fmt = {
150 enabled => {
8d88a594 151 description => "Enable/disable communication with a Qemu Guest Agent (QGA) running in the VM.",
9d66b397
SI
152 type => 'boolean',
153 default => 0,
154 default_key => 1,
155 },
156 fstrim_cloned_disks => {
0a4aff09 157 description => "Run fstrim after moving a disk or migrating the VM.",
9d66b397
SI
158 type => 'boolean',
159 optional => 1,
160 default => 0
161 },
48657158
MD
162 type => {
163 description => "Select the agent type",
164 type => 'string',
165 default => 'virtio',
166 optional => 1,
167 enum => [qw(virtio isa)],
168 },
9d66b397
SI
169};
170
55655ebc
DC
171my $vga_fmt = {
172 type => {
173 description => "Select the VGA type.",
174 type => 'string',
175 default => 'std',
176 optional => 1,
177 default_key => 1,
6f070e39 178 enum => [qw(cirrus qxl qxl2 qxl3 qxl4 none serial0 serial1 serial2 serial3 std virtio virtio-gl vmware)],
55655ebc
DC
179 },
180 memory => {
181 description => "Sets the VGA memory (in MiB). Has no effect with serial display.",
182 type => 'integer',
183 optional => 1,
184 minimum => 4,
185 maximum => 512,
186 },
187};
188
6dbcb073
DC
189my $ivshmem_fmt = {
190 size => {
191 type => 'integer',
192 minimum => 1,
193 description => "The size of the file in MB.",
194 },
195 name => {
196 type => 'string',
197 pattern => '[a-zA-Z0-9\-]+',
198 optional => 1,
199 format_description => 'string',
200 description => "The name of the file. Will be prefixed with 'pve-shm-'. Default is the VMID. Will be deleted when the VM is stopped.",
201 },
202};
203
1448547f
AL
204my $audio_fmt = {
205 device => {
206 type => 'string',
207 enum => [qw(ich9-intel-hda intel-hda AC97)],
208 description => "Configure an audio device."
209 },
210 driver => {
211 type => 'string',
211785ee 212 enum => ['spice', 'none'],
1448547f
AL
213 default => 'spice',
214 optional => 1,
215 description => "Driver backend for the audio device."
216 },
217};
218
c4df18db
AL
219my $spice_enhancements_fmt = {
220 foldersharing => {
221 type => 'boolean',
222 optional => 1,
d282a24d 223 default => '0',
c4df18db
AL
224 description => "Enable folder sharing via SPICE. Needs Spice-WebDAV daemon installed in the VM."
225 },
226 videostreaming => {
227 type => 'string',
228 enum => ['off', 'all', 'filter'],
d282a24d 229 default => 'off',
c4df18db
AL
230 optional => 1,
231 description => "Enable video streaming. Uses compression for detected video streams."
232 },
233};
234
2cf61f33
SR
235my $rng_fmt = {
236 source => {
237 type => 'string',
238 enum => ['/dev/urandom', '/dev/random', '/dev/hwrng'],
239 default_key => 1,
8d88a594
TL
240 description => "The file on the host to gather entropy from. In most cases '/dev/urandom'"
241 ." should be preferred over '/dev/random' to avoid entropy-starvation issues on the"
242 ." host. Using urandom does *not* decrease security in any meaningful way, as it's"
243 ." still seeded from real entropy, and the bytes provided will most likely be mixed"
244 ." with real entropy on the guest as well. '/dev/hwrng' can be used to pass through"
245 ." a hardware RNG from the host.",
2cf61f33
SR
246 },
247 max_bytes => {
248 type => 'integer',
8d88a594
TL
249 description => "Maximum bytes of entropy allowed to get injected into the guest every"
250 ." 'period' milliseconds. Prefer a lower value when using '/dev/random' as source. Use"
251 ." `0` to disable limiting (potentially dangerous!).",
2cf61f33
SR
252 optional => 1,
253
8d88a594
TL
254 # default is 1 KiB/s, provides enough entropy to the guest to avoid boot-starvation issues
255 # (e.g. systemd etc...) while allowing no chance of overwhelming the host, provided we're
256 # reading from /dev/urandom
2cf61f33
SR
257 default => 1024,
258 },
259 period => {
260 type => 'integer',
8d88a594
TL
261 description => "Every 'period' milliseconds the entropy-injection quota is reset, allowing"
262 ." the guest to retrieve another 'max_bytes' of entropy.",
2cf61f33
SR
263 optional => 1,
264 default => 1000,
265 },
266};
267
26b443c8
TL
268my $meta_info_fmt = {
269 'ctime' => {
270 type => 'integer',
271 description => "The guest creation timestamp as UNIX epoch time",
272 minimum => 0,
273 optional => 1,
274 },
af2a1a1c
TL
275 'creation-qemu' => {
276 type => 'string',
277 description => "The QEMU (machine) version from the time this VM was created.",
278 pattern => '\d+(\.\d+)+',
279 optional => 1,
280 },
26b443c8
TL
281};
282
1e3baf05
DM
283my $confdesc = {
284 onboot => {
285 optional => 1,
286 type => 'boolean',
287 description => "Specifies whether a VM will be started during system bootup.",
288 default => 0,
289 },
290 autostart => {
291 optional => 1,
292 type => 'boolean',
293 description => "Automatic restart after crash (currently ignored).",
294 default => 0,
295 },
2ff09f52 296 hotplug => {
483ceeab
TL
297 optional => 1,
298 type => 'string', format => 'pve-hotplug-features',
299 description => "Selectively enable hotplug features. This is a comma separated list of"
94ec5e7c 300 ." hotplug features: 'network', 'disk', 'cpu', 'memory', 'usb' and 'cloudinit'. Use '0' to disable"
c60cad61
DC
301 ." hotplug completely. Using '1' as value is an alias for the default `network,disk,usb`."
302 ." USB hotplugging is possible for guests with machine version >= 7.1 and ostype l26 or"
303 ." windows > 7.",
b3c2bdd1 304 default => 'network,disk,usb',
2ff09f52 305 },
1e3baf05
DM
306 reboot => {
307 optional => 1,
308 type => 'boolean',
309 description => "Allow reboot. If set to '0' the VM exit on reboot.",
310 default => 1,
311 },
312 lock => {
313 optional => 1,
314 type => 'string',
315 description => "Lock/unlock the VM.",
159719e5 316 enum => [qw(backup clone create migrate rollback snapshot snapshot-delete suspending suspended)],
1e3baf05
DM
317 },
318 cpulimit => {
319 optional => 1,
c6f773b8 320 type => 'number',
52261945 321 description => "Limit of CPU usage.",
8d88a594
TL
322 verbose_description => "Limit of CPU usage.\n\nNOTE: If the computer has 2 CPUs, it has"
323 ." total of '2' CPU time. Value '0' indicates no CPU limit.",
1e3baf05 324 minimum => 0,
c6f773b8 325 maximum => 128,
483ceeab 326 default => 0,
1e3baf05
DM
327 },
328 cpuunits => {
329 optional => 1,
330 type => 'integer',
483ceeab 331 description => "CPU weight for a VM, will be clamped to [1, 10000] in cgroup v2.",
67498860
TL
332 verbose_description => "CPU weight for a VM. Argument is used in the kernel fair scheduler."
333 ." The larger the number is, the more CPU time this VM gets. Number is relative to"
334 ." weights of all the other running VMs.",
e65e35ca 335 minimum => 1,
237239bf 336 maximum => 262144,
67498860 337 default => 'cgroup v1: 1024, cgroup v2: 100',
1e3baf05
DM
338 },
339 memory => {
340 optional => 1,
341 type => 'integer',
8d88a594
TL
342 description => "Amount of RAM for the VM in MB. This is the maximum available memory when"
343 ." you use the balloon device.",
1e3baf05
DM
344 minimum => 16,
345 default => 512,
346 },
13a48620 347 balloon => {
483ceeab
TL
348 optional => 1,
349 type => 'integer',
350 description => "Amount of target RAM for the VM in MB. Using zero disables the ballon driver.",
8b1accf7
DM
351 minimum => 0,
352 },
353 shares => {
483ceeab
TL
354 optional => 1,
355 type => 'integer',
356 description => "Amount of memory shares for auto-ballooning. The larger the number is, the"
8d88a594
TL
357 ." more memory this VM gets. Number is relative to weights of all other running VMs."
358 ." Using zero disables auto-ballooning. Auto-ballooning is done by pvestatd.",
8b1accf7
DM
359 minimum => 0,
360 maximum => 50000,
361 default => 1000,
13a48620 362 },
1e3baf05
DM
363 keyboard => {
364 optional => 1,
365 type => 'string',
233fb336
DW
366 description => "Keyboard layout for VNC server. This option is generally not required and"
367 ." is often better handled from within the guest OS.",
e95fe75f 368 enum => PVE::Tools::kvmkeymaplist(),
aea47dd6 369 default => undef,
1e3baf05
DM
370 },
371 name => {
372 optional => 1,
7fabe17d 373 type => 'string', format => 'dns-name',
1e3baf05
DM
374 description => "Set a name for the VM. Only used on the configuration web interface.",
375 },
cdd20088
AD
376 scsihw => {
377 optional => 1,
378 type => 'string',
52261945 379 description => "SCSI controller model",
6731a4cf 380 enum => [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)],
cdd20088
AD
381 default => 'lsi',
382 },
1e3baf05
DM
383 description => {
384 optional => 1,
385 type => 'string',
a200af10
TL
386 description => "Description for the VM. Shown in the web-interface VM's summary."
387 ." This is saved as comment inside the configuration file.",
388 maxLength => 1024 * 8,
1e3baf05
DM
389 },
390 ostype => {
391 optional => 1,
392 type => 'string',
483ceeab 393 enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 win10 win11 l24 l26 solaris)],
52261945
DM
394 description => "Specify guest operating system.",
395 verbose_description => <<EODESC,
396Specify guest operating system. This is used to enable special
397optimization/features for specific operating systems:
398
399[horizontal]
400other;; unspecified OS
401wxp;; Microsoft Windows XP
402w2k;; Microsoft Windows 2000
403w2k3;; Microsoft Windows 2003
404w2k8;; Microsoft Windows 2008
405wvista;; Microsoft Windows Vista
406win7;; Microsoft Windows 7
44c2a647 407win8;; Microsoft Windows 8/2012/2012r2
1f5828f2 408win10;; Microsoft Windows 10/2016/2019
179b9f1b 409win11;; Microsoft Windows 11/2022
52261945 410l24;; Linux 2.4 Kernel
a5269260 411l26;; Linux 2.6 - 5.X Kernel
52261945 412solaris;; Solaris/OpenSolaris/OpenIndiania kernel
1e3baf05
DM
413EODESC
414 },
415 boot => {
416 optional => 1,
2141a802 417 type => 'string', format => 'pve-qm-boot',
483ceeab
TL
418 description => "Specify guest boot order. Use the 'order=' sub-property as usage with no"
419 ." key or 'legacy=' is deprecated.",
1e3baf05
DM
420 },
421 bootdisk => {
422 optional => 1,
423 type => 'string', format => 'pve-qm-bootdisk',
2141a802 424 description => "Enable booting from specified disk. Deprecated: Use 'boot: order=foo;bar' instead.",
03e480fc 425 pattern => '(ide|sata|scsi|virtio)\d+',
1e3baf05
DM
426 },
427 smp => {
428 optional => 1,
429 type => 'integer',
430 description => "The number of CPUs. Please use option -sockets instead.",
431 minimum => 1,
432 default => 1,
433 },
434 sockets => {
435 optional => 1,
436 type => 'integer',
437 description => "The number of CPU sockets.",
438 minimum => 1,
439 default => 1,
440 },
441 cores => {
442 optional => 1,
443 type => 'integer',
444 description => "The number of cores per socket.",
445 minimum => 1,
446 default => 1,
447 },
8a010eae
AD
448 numa => {
449 optional => 1,
450 type => 'boolean',
1917695c 451 description => "Enable/disable NUMA.",
8a010eae
AD
452 default => 0,
453 },
7023f3ea
AD
454 hugepages => {
455 optional => 1,
456 type => 'string',
457 description => "Enable/disable hugepages memory.",
458 enum => [qw(any 2 1024)],
459 },
f36e9894
SR
460 keephugepages => {
461 optional => 1,
462 type => 'boolean',
463 default => 0,
4df98f2f
TL
464 description => "Use together with hugepages. If enabled, hugepages will not not be deleted"
465 ." after VM shutdown and can be used for subsequent starts.",
f36e9894 466 },
de9d1e55 467 vcpus => {
3bd18e48
AD
468 optional => 1,
469 type => 'integer',
de9d1e55 470 description => "Number of hotplugged vcpus.",
3bd18e48 471 minimum => 1,
de9d1e55 472 default => 0,
3bd18e48 473 },
1e3baf05
DM
474 acpi => {
475 optional => 1,
476 type => 'boolean',
477 description => "Enable/disable ACPI.",
478 default => 1,
479 },
bc84dcca 480 agent => {
ab6a046f 481 optional => 1,
8d88a594 482 description => "Enable/disable communication with the Qemu Guest Agent and its properties.",
9d66b397
SI
483 type => 'string',
484 format => $agent_fmt,
ab6a046f 485 },
1e3baf05
DM
486 kvm => {
487 optional => 1,
488 type => 'boolean',
489 description => "Enable/disable KVM hardware virtualization.",
490 default => 1,
491 },
492 tdf => {
493 optional => 1,
494 type => 'boolean',
8c559505
DM
495 description => "Enable/disable time drift fix.",
496 default => 0,
1e3baf05 497 },
19672434 498 localtime => {
1e3baf05
DM
499 optional => 1,
500 type => 'boolean',
8d88a594
TL
501 description => "Set the real time clock (RTC) to local time. This is enabled by default if"
502 ." the `ostype` indicates a Microsoft Windows OS.",
1e3baf05
DM
503 },
504 freeze => {
505 optional => 1,
506 type => 'boolean',
507 description => "Freeze CPU at startup (use 'c' monitor command to start execution).",
508 },
509 vga => {
510 optional => 1,
55655ebc
DC
511 type => 'string', format => $vga_fmt,
512 description => "Configure the VGA hardware.",
4df98f2f
TL
513 verbose_description => "Configure the VGA Hardware. If you want to use high resolution"
514 ." modes (>= 1280x1024x16) you may need to increase the vga memory option. Since QEMU"
515 ." 2.9 the default VGA display type is 'std' for all OS types besides some Windows"
516 ." versions (XP and older) which use 'cirrus'. The 'qxl' option enables the SPICE"
517 ." display server. For win* OS you can select how many independent displays you want,"
518 ." Linux guests can add displays them self.\nYou can also run without any graphic card,"
519 ." using a serial device as terminal.",
1e3baf05 520 },
0ea9541d
DM
521 watchdog => {
522 optional => 1,
523 type => 'string', format => 'pve-qm-watchdog',
52261945 524 description => "Create a virtual hardware watchdog device.",
4df98f2f
TL
525 verbose_description => "Create a virtual hardware watchdog device. Once enabled (by a guest"
526 ." action), the watchdog must be periodically polled by an agent inside the guest or"
527 ." else the watchdog will reset the guest (or execute the respective action specified)",
0ea9541d 528 },
1e3baf05
DM
529 startdate => {
530 optional => 1,
19672434 531 type => 'string',
1e3baf05 532 typetext => "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)",
4df98f2f
TL
533 description => "Set the initial date of the real time clock. Valid format for date are:"
534 ."'now' or '2006-06-17T16:01:21' or '2006-06-17'.",
1e3baf05
DM
535 pattern => '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)',
536 default => 'now',
537 },
43574f73 538 startup => get_standard_option('pve-startup-order'),
68eda3ab
AD
539 template => {
540 optional => 1,
541 type => 'boolean',
542 description => "Enable/disable Template.",
543 default => 0,
544 },
1e3baf05
DM
545 args => {
546 optional => 1,
547 type => 'string',
52261945
DM
548 description => "Arbitrary arguments passed to kvm.",
549 verbose_description => <<EODESCR,
c7a8aad6 550Arbitrary arguments passed to kvm, for example:
1e3baf05
DM
551
552args: -no-reboot -no-hpet
c7a8aad6
FG
553
554NOTE: this option is for experts only.
1e3baf05
DM
555EODESCR
556 },
557 tablet => {
558 optional => 1,
559 type => 'boolean',
560 default => 1,
52261945 561 description => "Enable/disable the USB tablet device.",
4df98f2f
TL
562 verbose_description => "Enable/disable the USB tablet device. This device is usually needed"
563 ." to allow absolute mouse positioning with VNC. Else the mouse runs out of sync with"
564 ." normal VNC clients. If you're running lots of console-only guests on one host, you"
565 ." may consider disabling this to save some context switches. This is turned off by"
566 ." default if you use spice (`qm set <vmid> --vga qxl`).",
1e3baf05
DM
567 },
568 migrate_speed => {
569 optional => 1,
570 type => 'integer',
571 description => "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.",
572 minimum => 0,
573 default => 0,
574 },
575 migrate_downtime => {
576 optional => 1,
04432191 577 type => 'number',
1e3baf05
DM
578 description => "Set maximum tolerated downtime (in seconds) for migrations.",
579 minimum => 0,
04432191 580 default => 0.1,
1e3baf05
DM
581 },
582 cdrom => {
583 optional => 1,
b799312f 584 type => 'string', format => 'pve-qm-ide',
8485b9ba 585 typetext => '<volume>',
1e3baf05
DM
586 description => "This is an alias for option -ide2",
587 },
588 cpu => {
589 optional => 1,
590 description => "Emulated CPU type.",
591 type => 'string',
5d008ad3 592 format => 'pve-vm-cpu-conf',
1e3baf05 593 },
b7ba6b79
DM
594 parent => get_standard_option('pve-snapshot-name', {
595 optional => 1,
596 description => "Parent snapshot name. This is used internally, and should not be modified.",
597 }),
982c7f12
DM
598 snaptime => {
599 optional => 1,
600 description => "Timestamp for snapshots.",
601 type => 'integer',
602 minimum => 0,
603 },
18bfb361
DM
604 vmstate => {
605 optional => 1,
606 type => 'string', format => 'pve-volume-id',
4df98f2f
TL
607 description => "Reference to a volume which stores the VM state. This is used internally"
608 ." for snapshots.",
18bfb361 609 },
253624c7
FG
610 vmstatestorage => get_standard_option('pve-storage-id', {
611 description => "Default storage for VM state volumes/files.",
612 optional => 1,
613 }),
c6737ef1 614 runningmachine => get_standard_option('pve-qemu-machine', {
4df98f2f
TL
615 description => "Specifies the QEMU machine type of the running vm. This is used internally"
616 ." for snapshots.",
c6737ef1 617 }),
ea1c2110 618 runningcpu => {
4df98f2f
TL
619 description => "Specifies the QEMU '-cpu' parameter of the running vm. This is used"
620 ." internally for snapshots.",
ea1c2110
SR
621 optional => 1,
622 type => 'string',
623 pattern => $PVE::QemuServer::CPUConfig::qemu_cmdline_cpu_re,
624 format_description => 'QEMU -cpu parameter'
625 },
c6737ef1 626 machine => get_standard_option('pve-qemu-machine'),
d731ecbe
WB
627 arch => {
628 description => "Virtual processor architecture. Defaults to the host.",
629 optional => 1,
630 type => 'string',
631 enum => [qw(x86_64 aarch64)],
632 },
2796e7d5
DM
633 smbios1 => {
634 description => "Specify SMBIOS type 1 fields.",
635 type => 'string', format => 'pve-qm-smbios1',
5d004b00 636 maxLength => 512,
2796e7d5
DM
637 optional => 1,
638 },
cb0e4540
AG
639 protection => {
640 optional => 1,
641 type => 'boolean',
4df98f2f
TL
642 description => "Sets the protection flag of the VM. This will disable the remove VM and"
643 ." remove disk operations.",
cb0e4540
AG
644 default => 0,
645 },
3edb45e7 646 bios => {
a783c78e 647 optional => 1,
3edb45e7
DM
648 type => 'string',
649 enum => [ qw(seabios ovmf) ],
650 description => "Select BIOS implementation.",
651 default => 'seabios',
a783c78e 652 },
6ee499ff
DC
653 vmgenid => {
654 type => 'string',
655 pattern => '(?:[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}|[01])',
656 format_description => 'UUID',
4df98f2f
TL
657 description => "Set VM Generation ID. Use '1' to autogenerate on create or update, pass '0'"
658 ." to disable explicitly.",
659 verbose_description => "The VM generation ID (vmgenid) device exposes a 128-bit integer"
660 ." value identifier to the guest OS. This allows to notify the guest operating system"
661 ." when the virtual machine is executed with a different configuration (e.g. snapshot"
662 ." execution or creation from a template). The guest operating system notices the"
663 ." change, and is then able to react as appropriate by marking its copies of"
664 ." distributed databases as dirty, re-initializing its random number generator, etc.\n"
665 ."Note that auto-creation only works when done through API/CLI create or update methods"
666 .", but not when manually editing the config file.",
f7ed64e7 667 default => "1 (autogenerated)",
6ee499ff
DC
668 optional => 1,
669 },
9e784b11
DC
670 hookscript => {
671 type => 'string',
672 format => 'pve-volume-id',
673 optional => 1,
674 description => "Script that will be executed during various steps in the vms lifetime.",
675 },
6dbcb073
DC
676 ivshmem => {
677 type => 'string',
678 format => $ivshmem_fmt,
4df98f2f
TL
679 description => "Inter-VM shared memory. Useful for direct communication between VMs, or to"
680 ." the host.",
6dbcb073 681 optional => 1,
2e7b5925
AL
682 },
683 audio0 => {
684 type => 'string',
1448547f 685 format => $audio_fmt,
194b65f1 686 description => "Configure a audio device, useful in combination with QXL/Spice.",
2e7b5925
AL
687 optional => 1
688 },
c4df18db
AL
689 spice_enhancements => {
690 type => 'string',
691 format => $spice_enhancements_fmt,
692 description => "Configure additional enhancements for SPICE.",
693 optional => 1
694 },
b8e7068a
DC
695 tags => {
696 type => 'string', format => 'pve-tag-list',
697 description => 'Tags of the VM. This is only meta information.',
698 optional => 1,
699 },
2cf61f33
SR
700 rng0 => {
701 type => 'string',
702 format => $rng_fmt,
703 description => "Configure a VirtIO-based Random Number Generator.",
704 optional => 1,
705 },
26b443c8
TL
706 meta => {
707 type => 'string',
708 format => $meta_info_fmt,
709 description => "Some (read-only) meta-information about this guest.",
710 optional => 1,
711 },
83870398
DB
712 affinity => {
713 type => 'string', format => 'pve-cpuset',
326704e7 714 description => "List of host cores used to execute guest processes, for example: 0,5,8-11",
83870398
DB
715 optional => 1,
716 },
9ed7a77c
WB
717};
718
cb702ebe
DL
719my $cicustom_fmt = {
720 meta => {
721 type => 'string',
722 optional => 1,
4df98f2f
TL
723 description => 'Specify a custom file containing all meta data passed to the VM via"
724 ." cloud-init. This is provider specific meaning configdrive2 and nocloud differ.',
cb702ebe
DL
725 format => 'pve-volume-id',
726 format_description => 'volume',
727 },
728 network => {
729 type => 'string',
730 optional => 1,
4df98f2f
TL
731 description => 'Specify a custom file containing all network data passed to the VM via'
732 .' cloud-init.',
cb702ebe
DL
733 format => 'pve-volume-id',
734 format_description => 'volume',
735 },
736 user => {
737 type => 'string',
738 optional => 1,
4df98f2f
TL
739 description => 'Specify a custom file containing all user data passed to the VM via'
740 .' cloud-init.',
cb702ebe
DL
741 format => 'pve-volume-id',
742 format_description => 'volume',
743 },
101beafe
CH
744 vendor => {
745 type => 'string',
746 optional => 1,
747 description => 'Specify a custom file containing all vendor data passed to the VM via'
748 .' cloud-init.',
749 format => 'pve-volume-id',
750 format_description => 'volume',
751 },
cb702ebe
DL
752};
753PVE::JSONSchema::register_format('pve-qm-cicustom', $cicustom_fmt);
754
9ed7a77c 755my $confdesc_cloudinit = {
41cd94a0
WB
756 citype => {
757 optional => 1,
758 type => 'string',
4df98f2f
TL
759 description => 'Specifies the cloud-init configuration format. The default depends on the'
760 .' configured operating system type (`ostype`. We use the `nocloud` format for Linux,'
761 .' and `configdrive2` for windows.',
545eec65 762 enum => ['configdrive2', 'nocloud', 'opennebula'],
41cd94a0 763 },
7b42f951
WB
764 ciuser => {
765 optional => 1,
766 type => 'string',
4df98f2f
TL
767 description => "cloud-init: User name to change ssh keys and password for instead of the"
768 ." image's configured default user.",
7b42f951
WB
769 },
770 cipassword => {
771 optional => 1,
772 type => 'string',
4df98f2f
TL
773 description => 'cloud-init: Password to assign the user. Using this is generally not'
774 .' recommended. Use ssh keys instead. Also note that older cloud-init versions do not'
775 .' support hashed passwords.',
7b42f951 776 },
cb702ebe
DL
777 cicustom => {
778 optional => 1,
779 type => 'string',
4df98f2f
TL
780 description => 'cloud-init: Specify custom files to replace the automatically generated'
781 .' ones at start.',
cb702ebe
DL
782 format => 'pve-qm-cicustom',
783 },
0c9a7596
AD
784 searchdomain => {
785 optional => 1,
786 type => 'string',
bd49ecb4 787 description => 'cloud-init: Sets DNS search domains for a container. Create will'
4df98f2f 788 .' automatically use the setting from the host if neither searchdomain nor nameserver'
bd49ecb4 789 .' are set.',
0c9a7596
AD
790 },
791 nameserver => {
792 optional => 1,
793 type => 'string', format => 'address-list',
bd49ecb4 794 description => 'cloud-init: Sets DNS server IP address for a container. Create will'
4df98f2f 795 .' automatically use the setting from the host if neither searchdomain nor nameserver'
bd49ecb4 796 .' are set.',
0c9a7596
AD
797 },
798 sshkeys => {
799 optional => 1,
800 type => 'string',
801 format => 'urlencoded',
1d1c4e1c 802 description => "cloud-init: Setup public SSH keys (one key per line, OpenSSH format).",
0c9a7596 803 },
1e3baf05
DM
804};
805
806# what about other qemu settings ?
807#cpu => 'string',
808#machine => 'string',
809#fda => 'file',
810#fdb => 'file',
811#mtdblock => 'file',
812#sd => 'file',
813#pflash => 'file',
814#snapshot => 'bool',
815#bootp => 'file',
816##tftp => 'dir',
817##smb => 'dir',
818#kernel => 'file',
819#append => 'string',
820#initrd => 'file',
821##soundhw => 'string',
822
823while (my ($k, $v) = each %$confdesc) {
824 PVE::JSONSchema::register_standard_option("pve-qm-$k", $v);
825}
826
0c3d18ef 827my $MAX_USB_DEVICES = 14;
5bdcf937 828my $MAX_NETS = 32;
bae179aa 829my $MAX_SERIAL_PORTS = 4;
1989a89c 830my $MAX_PARALLEL_PORTS = 3;
2ed5d572
AD
831my $MAX_NUMA = 8;
832
ffc0d8c7
WB
833my $numa_fmt = {
834 cpus => {
835 type => "string",
836 pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
52261945 837 description => "CPUs accessing this NUMA node.",
ffc0d8c7
WB
838 format_description => "id[-id];...",
839 },
840 memory => {
841 type => "number",
52261945 842 description => "Amount of memory this NUMA node provides.",
ffc0d8c7
WB
843 optional => 1,
844 },
845 hostnodes => {
846 type => "string",
847 pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
52261945 848 description => "Host NUMA nodes to use.",
ffc0d8c7
WB
849 format_description => "id[-id];...",
850 optional => 1,
851 },
852 policy => {
853 type => 'string',
854 enum => [qw(preferred bind interleave)],
52261945 855 description => "NUMA allocation policy.",
ffc0d8c7
WB
856 optional => 1,
857 },
858};
859PVE::JSONSchema::register_format('pve-qm-numanode', $numa_fmt);
2ed5d572
AD
860my $numadesc = {
861 optional => 1,
ffc0d8c7 862 type => 'string', format => $numa_fmt,
52261945 863 description => "NUMA topology.",
2ed5d572
AD
864};
865PVE::JSONSchema::register_standard_option("pve-qm-numanode", $numadesc);
866
867for (my $i = 0; $i < $MAX_NUMA; $i++) {
868 $confdesc->{"numa$i"} = $numadesc;
869}
1e3baf05 870
f7bc17ca
TL
871my $nic_model_list = [
872 'e1000',
873 'e1000-82540em',
874 'e1000-82544gc',
875 'e1000-82545em',
e83dd50a 876 'e1000e',
f7bc17ca
TL
877 'i82551',
878 'i82557b',
879 'i82559er',
880 'ne2k_isa',
881 'ne2k_pci',
882 'pcnet',
883 'rtl8139',
884 'virtio',
885 'vmxnet3',
886];
6b64503e 887my $nic_model_list_txt = join(' ', sort @$nic_model_list);
1e3baf05 888
52261945
DM
889my $net_fmt_bridge_descr = <<__EOD__;
890Bridge to attach the network device to. The Proxmox VE standard bridge
891is called 'vmbr0'.
892
893If you do not specify a bridge, we create a kvm user (NATed) network
894device, which provides DHCP and DNS services. The following addresses
895are used:
896
897 10.0.2.2 Gateway
898 10.0.2.3 DNS Server
899 10.0.2.4 SMB Server
900
901The DHCP server assign addresses to the guest starting from 10.0.2.15.
902__EOD__
903
cd9c34d1 904my $net_fmt = {
399d96db 905 macaddr => get_standard_option('mac-addr', {
4df98f2f
TL
906 description => "MAC address. That address must be unique withing your network. This is"
907 ." automatically generated if not specified.",
399d96db 908 }),
7f694a71
DM
909 model => {
910 type => 'string',
4df98f2f
TL
911 description => "Network Card Model. The 'virtio' model provides the best performance with"
912 ." very low CPU overhead. If your guest does not support this driver, it is usually"
913 ." best to use 'e1000'.",
7f694a71
DM
914 enum => $nic_model_list,
915 default_key => 1,
916 },
917 (map { $_ => { keyAlias => 'model', alias => 'macaddr' }} @$nic_model_list),
dbdcc5cd 918 bridge => get_standard_option('pve-bridge-id', {
52261945 919 description => $net_fmt_bridge_descr,
cd9c34d1 920 optional => 1,
dbdcc5cd 921 }),
cd9c34d1
WB
922 queues => {
923 type => 'integer',
924 minimum => 0, maximum => 16,
925 description => 'Number of packet queues to be used on the device.',
cd9c34d1
WB
926 optional => 1,
927 },
928 rate => {
929 type => 'number',
930 minimum => 0,
52261945 931 description => "Rate limit in mbps (megabytes per second) as floating point number.",
cd9c34d1
WB
932 optional => 1,
933 },
934 tag => {
935 type => 'integer',
9f41a659 936 minimum => 1, maximum => 4094,
cd9c34d1 937 description => 'VLAN tag to apply to packets on this interface.',
cd9c34d1
WB
938 optional => 1,
939 },
940 trunks => {
941 type => 'string',
942 pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/,
943 description => 'VLAN trunks to pass through this interface.',
7f694a71 944 format_description => 'vlanid[;vlanid...]',
cd9c34d1
WB
945 optional => 1,
946 },
947 firewall => {
948 type => 'boolean',
949 description => 'Whether this interface should be protected by the firewall.',
cd9c34d1
WB
950 optional => 1,
951 },
952 link_down => {
953 type => 'boolean',
52261945 954 description => 'Whether this interface should be disconnected (like pulling the plug).',
cd9c34d1
WB
955 optional => 1,
956 },
61a14cde
AD
957 mtu => {
958 type => 'integer',
959 minimum => 1, maximum => 65520,
0530177b 960 description => "Force MTU, for VirtIO only. Set to '1' to use the bridge MTU",
61a14cde
AD
961 optional => 1,
962 },
cd9c34d1 963};
52261945 964
1e3baf05
DM
965my $netdesc = {
966 optional => 1,
7f694a71 967 type => 'string', format => $net_fmt,
52261945 968 description => "Specify network devices.",
1e3baf05 969};
52261945 970
1e3baf05
DM
971PVE::JSONSchema::register_standard_option("pve-qm-net", $netdesc);
972
0c9a7596
AD
973my $ipconfig_fmt = {
974 ip => {
975 type => 'string',
976 format => 'pve-ipv4-config',
977 format_description => 'IPv4Format/CIDR',
978 description => 'IPv4 address in CIDR format.',
979 optional => 1,
980 default => 'dhcp',
981 },
982 gw => {
983 type => 'string',
984 format => 'ipv4',
985 format_description => 'GatewayIPv4',
986 description => 'Default gateway for IPv4 traffic.',
987 optional => 1,
988 requires => 'ip',
989 },
990 ip6 => {
991 type => 'string',
992 format => 'pve-ipv6-config',
993 format_description => 'IPv6Format/CIDR',
994 description => 'IPv6 address in CIDR format.',
995 optional => 1,
996 default => 'dhcp',
997 },
998 gw6 => {
999 type => 'string',
1000 format => 'ipv6',
1001 format_description => 'GatewayIPv6',
1002 description => 'Default gateway for IPv6 traffic.',
1003 optional => 1,
1004 requires => 'ip6',
1005 },
1006};
1007PVE::JSONSchema::register_format('pve-qm-ipconfig', $ipconfig_fmt);
1008my $ipconfigdesc = {
1009 optional => 1,
1010 type => 'string', format => 'pve-qm-ipconfig',
1011 description => <<'EODESCR',
1012cloud-init: Specify IP addresses and gateways for the corresponding interface.
1013
1014IP addresses use CIDR notation, gateways are optional but need an IP of the same type specified.
1015
4df98f2f
TL
1016The special string 'dhcp' can be used for IP addresses to use DHCP, in which case no explicit
1017gateway should be provided.
988be8d0
ML
1018For IPv6 the special string 'auto' can be used to use stateless autoconfiguration. This requires
1019cloud-init 19.4 or newer.
0c9a7596 1020
4df98f2f
TL
1021If cloud-init is enabled and neither an IPv4 nor an IPv6 address is specified, it defaults to using
1022dhcp on IPv4.
0c9a7596
AD
1023EODESCR
1024};
1025PVE::JSONSchema::register_standard_option("pve-qm-ipconfig", $netdesc);
1026
1e3baf05
DM
1027for (my $i = 0; $i < $MAX_NETS; $i++) {
1028 $confdesc->{"net$i"} = $netdesc;
9ed7a77c
WB
1029 $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
1030}
1031
1032foreach my $key (keys %$confdesc_cloudinit) {
1033 $confdesc->{$key} = $confdesc_cloudinit->{$key};
1e3baf05
DM
1034}
1035
83870398
DB
1036PVE::JSONSchema::register_format('pve-cpuset', \&pve_verify_cpuset);
1037sub pve_verify_cpuset {
1038 my ($set_text, $noerr) = @_;
1039
1040 my ($count, $members) = eval { PVE::CpuSet::parse_cpuset($set_text) };
1041
1042 if ($@) {
1043 return if $noerr;
1044 die "unable to parse cpuset option\n";
1045 }
1046
1047 return PVE::CpuSet->new($members)->short_string();
1048}
1049
ffa42b86
DC
1050PVE::JSONSchema::register_format('pve-volume-id-or-qm-path', \&verify_volume_id_or_qm_path);
1051sub verify_volume_id_or_qm_path {
822c8a07
WB
1052 my ($volid, $noerr) = @_;
1053
6e55f579
FE
1054 return $volid if $volid eq 'none' || $volid eq 'cdrom';
1055
1056 return verify_volume_id_or_absolute_path($volid, $noerr);
1057}
1058
1059PVE::JSONSchema::register_format('pve-volume-id-or-absolute-path', \&verify_volume_id_or_absolute_path);
1060sub verify_volume_id_or_absolute_path {
1061 my ($volid, $noerr) = @_;
1062
1063 return $volid if $volid =~ m|^/|;
ffa42b86 1064
822c8a07
WB
1065 $volid = eval { PVE::JSONSchema::check_format('pve-volume-id', $volid, '') };
1066 if ($@) {
d1c1af4b 1067 return if $noerr;
822c8a07
WB
1068 die $@;
1069 }
1070 return $volid;
1071}
1072
ff6ffe20 1073my $usb_fmt = {
a6b9aee4
DC
1074 host => {
1075 default_key => 1,
1076 type => 'string', format => 'pve-qm-usb-device',
1077 format_description => 'HOSTUSBDEVICE|spice',
52261945
DM
1078 description => <<EODESCR,
1079The Host USB device or port or the value 'spice'. HOSTUSBDEVICE syntax is:
1080
1081 'bus-port(.port)*' (decimal numbers) or
1082 'vendor_id:product_id' (hexadeciaml numbers) or
1083 'spice'
1084
1085You can use the 'lsusb -t' command to list existing usb devices.
1086
4df98f2f
TL
1087NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1088machines - use with special care.
52261945
DM
1089
1090The value 'spice' can be used to add a usb redirection devices for spice.
1091EODESCR
a6b9aee4
DC
1092 },
1093 usb3 => {
1094 optional => 1,
1095 type => 'boolean',
4862922a
DC
1096 description => "Specifies whether if given host option is a USB3 device or port."
1097 ." For modern guests (machine version >= 7.1 and ostype l26 and windows > 7), this flag"
1098 ." is irrelevant (all devices are plugged into a xhci controller).",
52261945 1099 default => 0,
a6b9aee4
DC
1100 },
1101};
1102
1e3baf05
DM
1103my $usbdesc = {
1104 optional => 1,
ff6ffe20 1105 type => 'string', format => $usb_fmt,
0c3d18ef
DC
1106 description => "Configure an USB device (n is 0 to 4, for machine version >= 7.1 and ostype"
1107 ." l26 or windows > 7, n can be up to 14).",
1e3baf05
DM
1108};
1109PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
1110
bae179aa
DA
1111my $serialdesc = {
1112 optional => 1,
ca0cef26 1113 type => 'string',
1b0b51ed 1114 pattern => '(/dev/.+|socket)',
52261945
DM
1115 description => "Create a serial device inside the VM (n is 0 to 3)",
1116 verbose_description => <<EODESCR,
52261945
DM
1117Create a serial device inside the VM (n is 0 to 3), and pass through a
1118host serial device (i.e. /dev/ttyS0), or create a unix socket on the
1119host side (use 'qm terminal' to open a terminal connection).
bae179aa 1120
4df98f2f
TL
1121NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines -
1122use with special care.
bae179aa 1123
52261945 1124CAUTION: Experimental! User reported problems with this option.
bae179aa
DA
1125EODESCR
1126};
bae179aa 1127
1989a89c
DA
1128my $paralleldesc= {
1129 optional => 1,
ca0cef26 1130 type => 'string',
9ecc8431 1131 pattern => '/dev/parport\d+|/dev/usb/lp\d+',
52261945
DM
1132 description => "Map host parallel devices (n is 0 to 2).",
1133 verbose_description => <<EODESCR,
19672434 1134Map host parallel devices (n is 0 to 2).
1989a89c 1135
4df98f2f
TL
1136NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such
1137machines - use with special care.
1989a89c 1138
52261945 1139CAUTION: Experimental! User reported problems with this option.
1989a89c
DA
1140EODESCR
1141};
1989a89c
DA
1142
1143for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
1144 $confdesc->{"parallel$i"} = $paralleldesc;
1145}
1146
bae179aa
DA
1147for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
1148 $confdesc->{"serial$i"} = $serialdesc;
1149}
1150
74c17b7a
SR
1151for (my $i = 0; $i < $PVE::QemuServer::PCI::MAX_HOSTPCI_DEVICES; $i++) {
1152 $confdesc->{"hostpci$i"} = $PVE::QemuServer::PCI::hostpcidesc;
040b06b7 1153}
1e3baf05 1154
e0fd2b2f
FE
1155for my $key (keys %{$PVE::QemuServer::Drive::drivedesc_hash}) {
1156 $confdesc->{$key} = $PVE::QemuServer::Drive::drivedesc_hash->{$key};
cdb0931f
DA
1157}
1158
1e3baf05
DM
1159for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
1160 $confdesc->{"usb$i"} = $usbdesc;
1161}
1162
5cfa9f5f
SR
1163my $boot_fmt = {
1164 legacy => {
1165 optional => 1,
1166 default_key => 1,
1167 type => 'string',
1168 description => "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)."
1169 . " Deprecated, use 'order=' instead.",
1170 pattern => '[acdn]{1,4}',
1171 format_description => "[acdn]{1,4}",
1172
1173 # note: this is also the fallback if boot: is not given at all
1174 default => 'cdn',
1175 },
1176 order => {
1177 optional => 1,
1178 type => 'string',
1179 format => 'pve-qm-bootdev-list',
1180 format_description => "device[;device...]",
1181 description => <<EODESC,
1182The guest will attempt to boot from devices in the order they appear here.
1183
1184Disks, optical drives and passed-through storage USB devices will be directly
1185booted from, NICs will load PXE, and PCIe devices will either behave like disks
1186(e.g. NVMe) or load an option ROM (e.g. RAID controller, hardware NIC).
1187
1188Note that only devices in this list will be marked as bootable and thus loaded
1189by the guest firmware (BIOS/UEFI). If you require multiple disks for booting
1190(e.g. software-raid), you need to specify all of them here.
1191
1192Overrides the deprecated 'legacy=[acdn]*' value when given.
1193EODESC
1194 },
1195};
1196PVE::JSONSchema::register_format('pve-qm-boot', $boot_fmt);
1197
1198PVE::JSONSchema::register_format('pve-qm-bootdev', \&verify_bootdev);
1199sub verify_bootdev {
1200 my ($dev, $noerr) = @_;
1201
f9dde219
SR
1202 my $special = $dev =~ m/^efidisk/ || $dev =~ m/^tpmstate/;
1203 return $dev if PVE::QemuServer::Drive::is_valid_drivename($dev) && !$special;
5cfa9f5f
SR
1204
1205 my $check = sub {
1206 my ($base) = @_;
1207 return 0 if $dev !~ m/^$base\d+$/;
1208 return 0 if !$confdesc->{$dev};
1209 return 1;
1210 };
1211
1212 return $dev if $check->("net");
1213 return $dev if $check->("usb");
1214 return $dev if $check->("hostpci");
1215
d1c1af4b 1216 return if $noerr;
5cfa9f5f
SR
1217 die "invalid boot device '$dev'\n";
1218}
1219
1220sub print_bootorder {
1221 my ($devs) = @_;
4c27b18c 1222 return "" if !@$devs;
5cfa9f5f
SR
1223 my $data = { order => join(';', @$devs) };
1224 return PVE::JSONSchema::print_property_string($data, $boot_fmt);
1225}
1226
1e3baf05
DM
1227my $kvm_api_version = 0;
1228
1229sub kvm_version {
1e3baf05
DM
1230 return $kvm_api_version if $kvm_api_version;
1231
808a65b5 1232 open my $fh, '<', '/dev/kvm' or return;
1e3baf05 1233
646f2df4
WB
1234 # 0xae00 => KVM_GET_API_VERSION
1235 $kvm_api_version = ioctl($fh, 0xae00, 0);
808a65b5 1236 close($fh);
1e3baf05 1237
646f2df4 1238 return $kvm_api_version;
1e3baf05
DM
1239}
1240
1476b99f
DC
1241my $kvm_user_version = {};
1242my $kvm_mtime = {};
1e3baf05
DM
1243
1244sub kvm_user_version {
1476b99f 1245 my ($binary) = @_;
1e3baf05 1246
1476b99f
DC
1247 $binary //= get_command_for_arch(get_host_arch()); # get the native arch by default
1248 my $st = stat($binary);
1e3baf05 1249
1476b99f
DC
1250 my $cachedmtime = $kvm_mtime->{$binary} // -1;
1251 return $kvm_user_version->{$binary} if $kvm_user_version->{$binary} &&
1252 $cachedmtime == $st->mtime;
1253
1254 $kvm_user_version->{$binary} = 'unknown';
1255 $kvm_mtime->{$binary} = $st->mtime;
1e3baf05 1256
09b11429
TL
1257 my $code = sub {
1258 my $line = shift;
1259 if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) {
1476b99f 1260 $kvm_user_version->{$binary} = $2;
09b11429
TL
1261 }
1262 };
19672434 1263
1476b99f 1264 eval { run_command([$binary, '--version'], outfunc => $code); };
09b11429 1265 warn $@ if $@;
1e3baf05 1266
1476b99f 1267 return $kvm_user_version->{$binary};
1e3baf05
DM
1268
1269}
4df98f2f
TL
1270my sub extract_version {
1271 my ($machine_type, $version) = @_;
1272 $version = kvm_user_version() if !defined($version);
8eb73377 1273 return PVE::QemuServer::Machine::extract_version($machine_type, $version)
4df98f2f 1274}
1e3baf05 1275
db70021b
TL
1276sub kernel_has_vhost_net {
1277 return -c '/dev/vhost-net';
1278}
1e3baf05 1279
1e3baf05
DM
1280sub option_exists {
1281 my $key = shift;
1282 return defined($confdesc->{$key});
19672434 1283}
1e3baf05 1284
1e3baf05 1285my $cdrom_path;
1e3baf05
DM
1286sub get_cdrom_path {
1287
1288 return $cdrom_path if $cdrom_path;
1289
1290 return $cdrom_path = "/dev/cdrom" if -l "/dev/cdrom";
1291 return $cdrom_path = "/dev/cdrom1" if -l "/dev/cdrom1";
1292 return $cdrom_path = "/dev/cdrom2" if -l "/dev/cdrom2";
1293}
1294
1295sub get_iso_path {
1296 my ($storecfg, $vmid, $cdrom) = @_;
1297
1298 if ($cdrom eq 'cdrom') {
1299 return get_cdrom_path();
1300 } elsif ($cdrom eq 'none') {
1301 return '';
1302 } elsif ($cdrom =~ m|^/|) {
1303 return $cdrom;
1304 } else {
6b64503e 1305 return PVE::Storage::path($storecfg, $cdrom);
1e3baf05
DM
1306 }
1307}
1308
1309# try to convert old style file names to volume IDs
1310sub filename_to_volume_id {
1311 my ($vmid, $file, $media) = @_;
1312
0c9a7596 1313 if (!($file eq 'none' || $file eq 'cdrom' ||
1e3baf05 1314 $file =~ m|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) {
19672434 1315
d1c1af4b 1316 return if $file =~ m|/|;
19672434 1317
1e3baf05
DM
1318 if ($media && $media eq 'cdrom') {
1319 $file = "local:iso/$file";
1320 } else {
1321 $file = "local:$vmid/$file";
1322 }
1323 }
1324
1325 return $file;
1326}
1327
1328sub verify_media_type {
1329 my ($opt, $vtype, $media) = @_;
1330
1331 return if !$media;
1332
1333 my $etype;
1334 if ($media eq 'disk') {
a125592c 1335 $etype = 'images';
1e3baf05
DM
1336 } elsif ($media eq 'cdrom') {
1337 $etype = 'iso';
1338 } else {
1339 die "internal error";
1340 }
1341
1342 return if ($vtype eq $etype);
19672434 1343
1e3baf05
DM
1344 raise_param_exc({ $opt => "unexpected media type ($vtype != $etype)" });
1345}
1346
1347sub cleanup_drive_path {
1348 my ($opt, $storecfg, $drive) = @_;
1349
1350 # try to convert filesystem paths to volume IDs
1351
1352 if (($drive->{file} !~ m/^(cdrom|none)$/) &&
1353 ($drive->{file} !~ m|^/dev/.+|) &&
1354 ($drive->{file} !~ m/^([^:]+):(.+)$/) &&
19672434 1355 ($drive->{file} !~ m/^\d+$/)) {
1e3baf05 1356 my ($vtype, $volid) = PVE::Storage::path_to_volume_id($storecfg, $drive->{file});
4df98f2f
TL
1357 raise_param_exc({ $opt => "unable to associate path '$drive->{file}' to any storage"})
1358 if !$vtype;
1e3baf05
DM
1359 $drive->{media} = 'cdrom' if !$drive->{media} && $vtype eq 'iso';
1360 verify_media_type($opt, $vtype, $drive->{media});
1361 $drive->{file} = $volid;
1362 }
1363
1364 $drive->{media} = 'cdrom' if !$drive->{media} && $drive->{file} =~ m/^(cdrom|none)$/;
1365}
1366
b3c2bdd1
DM
1367sub parse_hotplug_features {
1368 my ($data) = @_;
1369
1370 my $res = {};
1371
1372 return $res if $data eq '0';
a1b7d579 1373
b3c2bdd1
DM
1374 $data = $confdesc->{hotplug}->{default} if $data eq '1';
1375
45827685 1376 foreach my $feature (PVE::Tools::split_list($data)) {
94ec5e7c 1377 if ($feature =~ m/^(network|disk|cpu|memory|usb|cloudinit)$/) {
b3c2bdd1
DM
1378 $res->{$1} = 1;
1379 } else {
596a0a20 1380 die "invalid hotplug feature '$feature'\n";
b3c2bdd1
DM
1381 }
1382 }
1383 return $res;
1384}
1385
1386PVE::JSONSchema::register_format('pve-hotplug-features', \&pve_verify_hotplug_features);
1387sub pve_verify_hotplug_features {
1388 my ($value, $noerr) = @_;
1389
1390 return $value if parse_hotplug_features($value);
1391
d1c1af4b 1392 return if $noerr;
b3c2bdd1
DM
1393
1394 die "unable to parse hotplug option\n";
1395}
1396
28ef82d3
DM
1397sub scsi_inquiry {
1398 my($fh, $noerr) = @_;
1399
1400 my $SG_IO = 0x2285;
1401 my $SG_GET_VERSION_NUM = 0x2282;
1402
1403 my $versionbuf = "\x00" x 8;
1404 my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf);
1405 if (!$ret) {
1406 die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr;
d1c1af4b 1407 return;
28ef82d3 1408 }
97d62eb7 1409 my $version = unpack("I", $versionbuf);
28ef82d3
DM
1410 if ($version < 30000) {
1411 die "scsi generic interface too old\n" if !$noerr;
d1c1af4b 1412 return;
28ef82d3 1413 }
97d62eb7 1414
28ef82d3
DM
1415 my $buf = "\x00" x 36;
1416 my $sensebuf = "\x00" x 8;
f334aa3e 1417 my $cmd = pack("C x3 C x1", 0x12, 36);
97d62eb7 1418
28ef82d3
DM
1419 # see /usr/include/scsi/sg.h
1420 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";
1421
f606d5bd
TL
1422 my $packet = pack(
1423 $sg_io_hdr_t, ord('S'), -3, length($cmd), length($sensebuf), 0, length($buf), $buf, $cmd, $sensebuf, 6000
1424 );
28ef82d3
DM
1425
1426 $ret = ioctl($fh, $SG_IO, $packet);
1427 if (!$ret) {
1428 die "scsi ioctl SG_IO failed - $!\n" if !$noerr;
d1c1af4b 1429 return;
28ef82d3 1430 }
97d62eb7 1431
28ef82d3
DM
1432 my @res = unpack($sg_io_hdr_t, $packet);
1433 if ($res[17] || $res[18]) {
1434 die "scsi ioctl SG_IO status error - $!\n" if !$noerr;
d1c1af4b 1435 return;
28ef82d3
DM
1436 }
1437
1438 my $res = {};
f606d5bd 1439 $res->@{qw(type removable vendor product revision)} = unpack("C C x6 A8 A16 A4", $buf);
28ef82d3 1440
f606d5bd
TL
1441 $res->{removable} = $res->{removable} & 128 ? 1 : 0;
1442 $res->{type} &= 0x1F;
09984754 1443
28ef82d3
DM
1444 return $res;
1445}
1446
1447sub path_is_scsi {
1448 my ($path) = @_;
1449
d1c1af4b 1450 my $fh = IO::File->new("+<$path") || return;
28ef82d3
DM
1451 my $res = scsi_inquiry($fh, 1);
1452 close($fh);
1453
1454 return $res;
1455}
1456
db656e5f 1457sub print_tabletdevice_full {
d559309f 1458 my ($conf, $arch) = @_;
b467f79a 1459
3392d6ca 1460 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
db656e5f
DM
1461
1462 # we use uhci for old VMs because tablet driver was buggy in older qemu
d559309f 1463 my $usbbus;
2b938c7d 1464 if ($q35 || $arch eq 'aarch64') {
d559309f
WB
1465 $usbbus = 'ehci';
1466 } else {
1467 $usbbus = 'uhci';
1468 }
b467f79a 1469
db656e5f
DM
1470 return "usb-tablet,id=tablet,bus=$usbbus.0,port=1";
1471}
1472
d559309f 1473sub print_keyboarddevice_full {
6971c38e 1474 my ($conf, $arch) = @_;
d559309f 1475
d1c1af4b 1476 return if $arch ne 'aarch64';
d559309f
WB
1477
1478 return "usb-kbd,id=keyboard,bus=ehci.0,port=2";
1479}
1480
a183df68
TL
1481my sub get_drive_id {
1482 my ($drive) = @_;
1483 return "$drive->{interface}$drive->{index}";
1484}
1485
ca916ecc 1486sub print_drivedevice_full {
d559309f 1487 my ($storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type) = @_;
ca916ecc
DA
1488
1489 my $device = '';
1490 my $maxdev = 0;
19672434 1491
a183df68 1492 my $drive_id = get_drive_id($drive);
ca916ecc 1493 if ($drive->{interface} eq 'virtio') {
4df98f2f
TL
1494 my $pciaddr = print_pci_addr("$drive_id", $bridges, $arch, $machine_type);
1495 $device = "virtio-blk-pci,drive=drive-$drive_id,id=${drive_id}${pciaddr}";
1496 $device .= ",iothread=iothread-$drive_id" if $drive->{iothread};
2ed36a41 1497 } elsif ($drive->{interface} eq 'scsi') {
6731a4cf 1498
ee034f5c 1499 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive);
2ed36a41
DM
1500 my $unit = $drive->{index} % $maxdev;
1501 my $devicetype = 'hd';
69bcf246
WB
1502 my $path = '';
1503 if (drive_is_cdrom($drive)) {
1504 $devicetype = 'cd';
29b19529 1505 } else {
69bcf246
WB
1506 if ($drive->{file} =~ m|^/|) {
1507 $path = $drive->{file};
1508 if (my $info = path_is_scsi($path)) {
8e3c33ab 1509 if ($info->{type} == 0 && $drive->{scsiblock}) {
69bcf246
WB
1510 $devicetype = 'block';
1511 } elsif ($info->{type} == 1) { # tape
1512 $devicetype = 'generic';
1513 }
1514 }
1515 } else {
1516 $path = PVE::Storage::path($storecfg, $drive->{file});
1517 }
1518
a034e3d6 1519 # for compatibility only, we prefer scsi-hd (#2408, #2355, #2380)
4df98f2f 1520 my $version = extract_version($machine_type, kvm_user_version());
a034e3d6 1521 if ($path =~ m/^iscsi\:\/\// &&
2ea5fb7e 1522 !min_version($version, 4, 1)) {
69bcf246
WB
1523 $devicetype = 'generic';
1524 }
1525 }
1526
ef88eaaa 1527 if (!$conf->{scsihw} || $conf->{scsihw} =~ m/^lsi/ || $conf->{scsihw} eq 'pvscsi') {
4df98f2f 1528 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit";
69bcf246 1529 } else {
4df98f2f
TL
1530 $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0"
1531 .",lun=$drive->{index}";
69bcf246 1532 }
4df98f2f 1533 $device .= ",drive=drive-$drive_id,id=$drive_id";
cdd20088 1534
6c875f9f
NC
1535 if ($drive->{ssd} && ($devicetype eq 'block' || $devicetype eq 'hd')) {
1536 $device .= ",rotation_rate=1";
1537 }
e741c516 1538 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn};
6c875f9f
NC
1539
1540 } elsif ($drive->{interface} eq 'ide' || $drive->{interface} eq 'sata') {
e0fd2b2f 1541 my $maxdev = ($drive->{interface} eq 'sata') ? $PVE::QemuServer::Drive::MAX_SATA_DISKS : 2;
2ed36a41
DM
1542 my $controller = int($drive->{index} / $maxdev);
1543 my $unit = $drive->{index} % $maxdev;
1544 my $devicetype = ($drive->{media} && $drive->{media} eq 'cdrom') ? "cd" : "hd";
1545
6c875f9f
NC
1546 $device = "ide-$devicetype";
1547 if ($drive->{interface} eq 'ide') {
1548 $device .= ",bus=ide.$controller,unit=$unit";
1549 } else {
1550 $device .= ",bus=ahci$controller.$unit";
1551 }
4df98f2f 1552 $device .= ",drive=drive-$drive_id,id=$drive_id";
6c875f9f
NC
1553
1554 if ($devicetype eq 'hd') {
1555 if (my $model = $drive->{model}) {
1556 $model = URI::Escape::uri_unescape($model);
1557 $device .= ",model=$model";
1558 }
1559 if ($drive->{ssd}) {
1560 $device .= ",rotation_rate=1";
1561 }
0f2812c2 1562 }
e741c516 1563 $device .= ",wwn=$drive->{wwn}" if $drive->{wwn};
2ed36a41
DM
1564 } elsif ($drive->{interface} eq 'usb') {
1565 die "implement me";
1566 # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0
1567 } else {
1568 die "unsupported interface type";
ca916ecc
DA
1569 }
1570
3b408e82
DM
1571 $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex};
1572
a70e7e6c
TL
1573 if (my $serial = $drive->{serial}) {
1574 $serial = URI::Escape::uri_unescape($serial);
1575 $device .= ",serial=$serial";
1576 }
1577
1578
ca916ecc
DA
1579 return $device;
1580}
1581
15b21acc 1582sub get_initiator_name {
46f58b5f 1583 my $initiator;
15b21acc 1584
d1c1af4b 1585 my $fh = IO::File->new('/etc/iscsi/initiatorname.iscsi') || return;
46f58b5f
DM
1586 while (defined(my $line = <$fh>)) {
1587 next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/;
15b21acc
MR
1588 $initiator = $1;
1589 last;
1590 }
46f58b5f
DM
1591 $fh->close();
1592
15b21acc
MR
1593 return $initiator;
1594}
1595
776c5f50 1596sub print_drive_commandline_full {
6d5673c3 1597 my ($storecfg, $vmid, $drive, $pbs_name, $io_uring) = @_;
1e3baf05 1598
d81f0f09
DM
1599 my $path;
1600 my $volid = $drive->{file};
5921764c 1601 my $format = $drive->{format};
a183df68 1602 my $drive_id = get_drive_id($drive);
370b05e7 1603
0fe779a6
WB
1604 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
1605 my $scfg = $storeid ? PVE::Storage::storage_config($storecfg, $storeid) : undef;
1606
d81f0f09
DM
1607 if (drive_is_cdrom($drive)) {
1608 $path = get_iso_path($storecfg, $vmid, $volid);
a183df68 1609 die "$drive_id: cannot back cdrom drive with PBS snapshot\n" if $pbs_name;
d81f0f09 1610 } else {
d81f0f09
DM
1611 if ($storeid) {
1612 $path = PVE::Storage::path($storecfg, $volid);
5921764c 1613 $format //= qemu_img_format($scfg, $volname);
d81f0f09
DM
1614 } else {
1615 $path = $volid;
5921764c 1616 $format //= "raw";
d81f0f09
DM
1617 }
1618 }
1619
5921764c
SR
1620 my $is_rbd = $path =~ m/^rbd:/;
1621
1e3baf05 1622 my $opts = '';
5921764c 1623 my @qemu_drive_options = qw(heads secs cyls trans media cache rerror werror aio discard);
1e3baf05 1624 foreach my $o (@qemu_drive_options) {
5fc74861 1625 $opts .= ",$o=$drive->{$o}" if defined($drive->{$o});
19672434 1626 }
8a267708
DC
1627
1628 # snapshot only accepts on|off
1629 if (defined($drive->{snapshot})) {
1630 my $v = $drive->{snapshot} ? 'on' : 'off';
1631 $opts .= ",snapshot=$v";
1632 }
1633
1f91f7b4
TL
1634 if (defined($drive->{ro})) { # ro maps to QEMUs `readonly`, which accepts `on` or `off` only
1635 $opts .= ",readonly=" . ($drive->{ro} ? 'on' : 'off');
12e1d472
DC
1636 }
1637
fb8e95a2
WB
1638 foreach my $type (['', '-total'], [_rd => '-read'], [_wr => '-write']) {
1639 my ($dir, $qmpname) = @$type;
1640 if (my $v = $drive->{"mbps$dir"}) {
1641 $opts .= ",throttling.bps$qmpname=".int($v*1024*1024);
1642 }
1643 if (my $v = $drive->{"mbps${dir}_max"}) {
1644 $opts .= ",throttling.bps$qmpname-max=".int($v*1024*1024);
1645 }
1646 if (my $v = $drive->{"bps${dir}_max_length"}) {
1647 $opts .= ",throttling.bps$qmpname-max-length=$v";
1648 }
1649 if (my $v = $drive->{"iops${dir}"}) {
1650 $opts .= ",throttling.iops$qmpname=$v";
1651 }
1652 if (my $v = $drive->{"iops${dir}_max"}) {
8aca1654 1653 $opts .= ",throttling.iops$qmpname-max=$v";
fb8e95a2
WB
1654 }
1655 if (my $v = $drive->{"iops${dir}_max_length"}) {
8aca1654 1656 $opts .= ",throttling.iops$qmpname-max-length=$v";
fb8e95a2
WB
1657 }
1658 }
1659
5921764c
SR
1660 if ($pbs_name) {
1661 $format = "rbd" if $is_rbd;
a183df68
TL
1662 die "$drive_id: Proxmox Backup Server backed drive cannot auto-detect the format\n"
1663 if !$format;
5921764c
SR
1664 $opts .= ",format=alloc-track,file.driver=$format";
1665 } elsif ($format) {
1666 $opts .= ",format=$format";
1667 }
d81f0f09 1668
b2ee900e
WB
1669 my $cache_direct = 0;
1670
1671 if (my $cache = $drive->{cache}) {
1672 $cache_direct = $cache =~ /^(?:off|none|directsync)$/;
0fe779a6 1673 } elsif (!drive_is_cdrom($drive) && !($scfg && $scfg->{type} eq 'btrfs' && !$scfg->{nocow})) {
b2ee900e
WB
1674 $opts .= ",cache=none";
1675 $cache_direct = 1;
1676 }
1677
628937f5
SR
1678 # io_uring with cache mode writeback or writethrough on krbd will hang...
1679 my $rbd_no_io_uring = $scfg && $scfg->{type} eq 'rbd' && $scfg->{krbd} && !$cache_direct;
1680
78a3ada7
TL
1681 # io_uring with cache mode writeback or writethrough on LVM will hang, without cache only
1682 # sometimes, just plain disable...
1683 my $lvm_no_io_uring = $scfg && $scfg->{type} eq 'lvm';
ec5d198e 1684
e31762f2
FE
1685 # io_uring causes problems when used with CIFS since kernel 5.15
1686 # Some discussion: https://www.spinics.net/lists/linux-cifs/msg26734.html
1687 my $cifs_no_io_uring = $scfg && $scfg->{type} eq 'cifs';
1688
b2ee900e 1689 if (!$drive->{aio}) {
e31762f2 1690 if ($io_uring && !$rbd_no_io_uring && !$lvm_no_io_uring && !$cifs_no_io_uring) {
6d5673c3
SR
1691 # io_uring supports all cache modes
1692 $opts .= ",aio=io_uring";
b2ee900e 1693 } else {
6d5673c3
SR
1694 # aio native works only with O_DIRECT
1695 if($cache_direct) {
1696 $opts .= ",aio=native";
1697 } else {
1698 $opts .= ",aio=threads";
1699 }
b2ee900e
WB
1700 }
1701 }
11490cf2 1702
6e47c3b4
WB
1703 if (!drive_is_cdrom($drive)) {
1704 my $detectzeroes;
7d4e30f3 1705 if (defined($drive->{detect_zeroes}) && !$drive->{detect_zeroes}) {
6e47c3b4
WB
1706 $detectzeroes = 'off';
1707 } elsif ($drive->{discard}) {
1708 $detectzeroes = $drive->{discard} eq 'on' ? 'unmap' : 'on';
1709 } else {
1710 # This used to be our default with discard not being specified:
1711 $detectzeroes = 'on';
1712 }
5921764c
SR
1713
1714 # note: 'detect-zeroes' works per blockdev and we want it to persist
1715 # after the alloc-track is removed, so put it on 'file' directly
1716 my $dz_param = $pbs_name ? "file.detect-zeroes" : "detect-zeroes";
1717 $opts .= ",$dz_param=$detectzeroes" if $detectzeroes;
6e47c3b4 1718 }
f1e05305 1719
5921764c
SR
1720 if ($pbs_name) {
1721 $opts .= ",backing=$pbs_name";
1722 $opts .= ",auto-remove=on";
1723 }
1724
1725 # my $file_param = $pbs_name ? "file.file.filename" : "file";
1726 my $file_param = "file";
1727 if ($pbs_name) {
1728 # non-rbd drivers require the underlying file to be a seperate block
1729 # node, so add a second .file indirection
1730 $file_param .= ".file" if !$is_rbd;
1731 $file_param .= ".filename";
1732 }
1733 my $pathinfo = $path ? "$file_param=$path," : '';
1e3baf05 1734
3ebfcc86 1735 return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts";
1e3baf05
DM
1736}
1737
5921764c
SR
1738sub print_pbs_blockdev {
1739 my ($pbs_conf, $pbs_name) = @_;
1740 my $blockdev = "driver=pbs,node-name=$pbs_name,read-only=on";
1741 $blockdev .= ",repository=$pbs_conf->{repository}";
2dda626d 1742 $blockdev .= ",namespace=$pbs_conf->{namespace}" if $pbs_conf->{namespace};
5921764c
SR
1743 $blockdev .= ",snapshot=$pbs_conf->{snapshot}";
1744 $blockdev .= ",archive=$pbs_conf->{archive}";
1745 $blockdev .= ",keyfile=$pbs_conf->{keyfile}" if $pbs_conf->{keyfile};
1746 return $blockdev;
1747}
1748
cc4d6182 1749sub print_netdevice_full {
0c03a390 1750 my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version) = @_;
cc4d6182 1751
cc4d6182
DA
1752 my $device = $net->{model};
1753 if ($net->{model} eq 'virtio') {
1754 $device = 'virtio-net-pci';
1755 };
1756
d559309f 1757 my $pciaddr = print_pci_addr("$netid", $bridges, $arch, $machine_type);
5e2068d2 1758 my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid";
a9410357 1759 if ($net->{queues} && $net->{queues} > 1 && $net->{model} eq 'virtio'){
4df98f2f
TL
1760 # Consider we have N queues, the number of vectors needed is 2 * N + 2, i.e., one per in
1761 # and out of each queue plus one config interrupt and control vector queue
a9410357
AD
1762 my $vectors = $net->{queues} * 2 + 2;
1763 $tmpstr .= ",vectors=$vectors,mq=on";
0c03a390
AD
1764 if (min_version($machine_version, 7, 1)) {
1765 $tmpstr .= ",packed=on";
1766 }
a9410357 1767 }
620d6b32
AD
1768
1769 if (min_version($machine_version, 7, 1) && $net->{model} eq 'virtio'){
1770 $tmpstr .= ",rx_queue_size=1024,tx_queue_size=1024";
1771 }
1772
cc4d6182 1773 $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex} ;
ba9e1000 1774
0530177b
TL
1775 if (my $mtu = $net->{mtu}) {
1776 if ($net->{model} eq 'virtio' && $net->{bridge}) {
1777 my $bridge_mtu = PVE::Network::read_bridge_mtu($net->{bridge});
1778 if ($mtu == 1) {
1779 $mtu = $bridge_mtu;
1780 } elsif ($mtu < 576) {
1781 die "netdev $netid: MTU '$mtu' is smaller than the IP minimum MTU '576'\n";
1782 } elsif ($mtu > $bridge_mtu) {
1783 die "netdev $netid: MTU '$mtu' is bigger than the bridge MTU '$bridge_mtu'\n";
1784 }
1785 $tmpstr .= ",host_mtu=$mtu";
1786 } else {
1787 warn "WARN: netdev $netid: ignoring MTU '$mtu', not using VirtIO or no bridge configured.\n";
61a14cde 1788 }
61a14cde
AD
1789 }
1790
ba9e1000
DM
1791 if ($use_old_bios_files) {
1792 my $romfile;
1793 if ($device eq 'virtio-net-pci') {
1794 $romfile = 'pxe-virtio.rom';
1795 } elsif ($device eq 'e1000') {
1796 $romfile = 'pxe-e1000.rom';
e83dd50a
TL
1797 } elsif ($device eq 'e1000e') {
1798 $romfile = 'pxe-e1000e.rom';
ba9e1000
DM
1799 } elsif ($device eq 'ne2k') {
1800 $romfile = 'pxe-ne2k_pci.rom';
1801 } elsif ($device eq 'pcnet') {
1802 $romfile = 'pxe-pcnet.rom';
1803 } elsif ($device eq 'rtl8139') {
1804 $romfile = 'pxe-rtl8139.rom';
1805 }
1806 $tmpstr .= ",romfile=$romfile" if $romfile;
1807 }
1808
cc4d6182
DA
1809 return $tmpstr;
1810}
1811
1812sub print_netdev_full {
d559309f 1813 my ($vmid, $conf, $arch, $net, $netid, $hotplug) = @_;
cc4d6182
DA
1814
1815 my $i = '';
1816 if ($netid =~ m/^net(\d+)$/) {
1817 $i = int($1);
1818 }
1819
1820 die "got strange net id '$i'\n" if $i >= ${MAX_NETS};
1821
1822 my $ifname = "tap${vmid}i$i";
1823
1824 # kvm uses TUNSETIFF ioctl, and that limits ifname length
1825 die "interface name '$ifname' is too long (max 15 character)\n"
1826 if length($ifname) >= 16;
1827
1828 my $vhostparam = '';
6f0cb675 1829 if (is_native($arch)) {
db70021b 1830 $vhostparam = ',vhost=on' if kernel_has_vhost_net() && $net->{model} eq 'virtio';
6f0cb675 1831 }
cc4d6182
DA
1832
1833 my $vmname = $conf->{name} || "vm$vmid";
1834
a9410357 1835 my $netdev = "";
208ba94e 1836 my $script = $hotplug ? "pve-bridge-hotplug" : "pve-bridge";
a9410357 1837
cc4d6182 1838 if ($net->{bridge}) {
4df98f2f
TL
1839 $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script"
1840 .",downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam";
cc4d6182 1841 } else {
a9410357 1842 $netdev = "type=user,id=$netid,hostname=$vmname";
cc4d6182 1843 }
a9410357
AD
1844
1845 $netdev .= ",queues=$net->{queues}" if ($net->{queues} && $net->{model} eq 'virtio');
1846
1847 return $netdev;
cc4d6182 1848}
1e3baf05 1849
55655ebc
DC
1850my $vga_map = {
1851 'cirrus' => 'cirrus-vga',
1852 'std' => 'VGA',
1853 'vmware' => 'vmware-svga',
1854 'virtio' => 'virtio-vga',
6f070e39 1855 'virtio-gl' => 'virtio-vga-gl',
55655ebc
DC
1856};
1857
1858sub print_vga_device {
2ea5fb7e 1859 my ($conf, $vga, $arch, $machine_version, $machine, $id, $qxlnum, $bridges) = @_;
55655ebc
DC
1860
1861 my $type = $vga_map->{$vga->{type}};
86c9fafe 1862 if ($arch eq 'aarch64' && defined($type) && $type eq 'virtio-vga') {
d559309f
WB
1863 $type = 'virtio-gpu';
1864 }
55655ebc 1865 my $vgamem_mb = $vga->{memory};
6021c7a5
AL
1866
1867 my $max_outputs = '';
55655ebc
DC
1868 if ($qxlnum) {
1869 $type = $id ? 'qxl' : 'qxl-vga';
6021c7a5 1870
c5a4c92c 1871 if (!$conf->{ostype} || $conf->{ostype} =~ m/^(?:l\d\d)|(?:other)$/) {
6021c7a5 1872 # set max outputs so linux can have up to 4 qxl displays with one device
2ea5fb7e 1873 if (min_version($machine_version, 4, 1)) {
9e8976ea
TL
1874 $max_outputs = ",max_outputs=4";
1875 }
6021c7a5 1876 }
55655ebc 1877 }
6021c7a5 1878
55655ebc
DC
1879 die "no devicetype for $vga->{type}\n" if !$type;
1880
1881 my $memory = "";
1882 if ($vgamem_mb) {
6f070e39 1883 if ($vga->{type} =~ /^virtio/) {
55655ebc
DC
1884 my $bytes = PVE::Tools::convert_size($vgamem_mb, "mb" => "b");
1885 $memory = ",max_hostmem=$bytes";
1886 } elsif ($qxlnum) {
1887 # from https://www.spice-space.org/multiple-monitors.html
1888 $memory = ",vgamem_mb=$vga->{memory}";
1889 my $ram = $vgamem_mb * 4;
1890 my $vram = $vgamem_mb * 2;
1891 $memory .= ",ram_size_mb=$ram,vram_size_mb=$vram";
1892 } else {
1893 $memory = ",vgamem_mb=$vga->{memory}";
1894 }
1895 } elsif ($qxlnum && $id) {
1896 $memory = ",ram_size=67108864,vram_size=33554432";
1897 }
1898
789fe8e8
AL
1899 my $edidoff = "";
1900 if ($type eq 'VGA' && windows_version($conf->{ostype})) {
b5d32c6b 1901 $edidoff=",edid=off" if (!defined($conf->{bios}) || $conf->{bios} ne 'ovmf');
789fe8e8
AL
1902 }
1903
3392d6ca 1904 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
55655ebc
DC
1905 my $vgaid = "vga" . ($id // '');
1906 my $pciaddr;
1907 if ($q35 && $vgaid eq 'vga') {
daadd5a4 1908 # the first display uses pcie.0 bus on q35 machines
d559309f 1909 $pciaddr = print_pcie_addr($vgaid, $bridges, $arch, $machine);
55655ebc 1910 } else {
d559309f 1911 $pciaddr = print_pci_addr($vgaid, $bridges, $arch, $machine);
55655ebc
DC
1912 }
1913
9f979d9f 1914 if ($vga->{type} eq 'virtio-gl') {
8884a8bf
TL
1915 my $base = '/usr/lib/x86_64-linux-gnu/lib';
1916 die "missing libraries for '$vga->{type}' detected! Please install 'libgl1' and 'libegl1'\n"
1917 if !-e "${base}EGL.so.1" || !-e "${base}GL.so.1";
9f979d9f 1918
8884a8bf
TL
1919 die "no DRM render node detected (/dev/dri/renderD*), no GPU? - needed for '$vga->{type}' display\n"
1920 if !PVE::Tools::dir_glob_regex('/dev/dri/', "renderD.*");
9f979d9f
SI
1921 }
1922
789fe8e8 1923 return "$type,id=${vgaid}${memory}${max_outputs}${pciaddr}${edidoff}";
55655ebc
DC
1924}
1925
ffc0d8c7
WB
1926sub parse_number_sets {
1927 my ($set) = @_;
1928 my $res = [];
1929 foreach my $part (split(/;/, $set)) {
1930 if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) {
1931 die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1;
1932 push @$res, [ $1, $2 ];
2ed5d572 1933 } else {
ffc0d8c7 1934 die "invalid range: $part\n";
2ed5d572
AD
1935 }
1936 }
ffc0d8c7
WB
1937 return $res;
1938}
2ed5d572 1939
ffc0d8c7
WB
1940sub parse_numa {
1941 my ($data) = @_;
1942
4df98f2f 1943 my $res = parse_property_string($numa_fmt, $data);
ffc0d8c7
WB
1944 $res->{cpus} = parse_number_sets($res->{cpus}) if defined($res->{cpus});
1945 $res->{hostnodes} = parse_number_sets($res->{hostnodes}) if defined($res->{hostnodes});
2ed5d572
AD
1946 return $res;
1947}
1948
1e3baf05
DM
1949# netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps>
1950sub parse_net {
4ddd2ca2 1951 my ($data, $disable_mac_autogen) = @_;
1e3baf05 1952
4df98f2f 1953 my $res = eval { parse_property_string($net_fmt, $data) };
cd9c34d1
WB
1954 if ($@) {
1955 warn $@;
d1c1af4b 1956 return;
1e3baf05 1957 }
4ddd2ca2 1958 if (!defined($res->{macaddr}) && !$disable_mac_autogen) {
b5b99790
WB
1959 my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
1960 $res->{macaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
1961 }
0c9a7596
AD
1962 return $res;
1963}
1964
1965# ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
1966sub parse_ipconfig {
1967 my ($data) = @_;
1968
4df98f2f 1969 my $res = eval { parse_property_string($ipconfig_fmt, $data) };
0c9a7596
AD
1970 if ($@) {
1971 warn $@;
d1c1af4b 1972 return;
0c9a7596
AD
1973 }
1974
1975 if ($res->{gw} && !$res->{ip}) {
1976 warn 'gateway specified without specifying an IP address';
d1c1af4b 1977 return;
0c9a7596
AD
1978 }
1979 if ($res->{gw6} && !$res->{ip6}) {
1980 warn 'IPv6 gateway specified without specifying an IPv6 address';
d1c1af4b 1981 return;
0c9a7596
AD
1982 }
1983 if ($res->{gw} && $res->{ip} eq 'dhcp') {
1984 warn 'gateway specified together with DHCP';
d1c1af4b 1985 return;
0c9a7596
AD
1986 }
1987 if ($res->{gw6} && $res->{ip6} !~ /^$IPV6RE/) {
1988 # gw6 + auto/dhcp
1989 warn "IPv6 gateway specified together with $res->{ip6} address";
d1c1af4b 1990 return;
0c9a7596
AD
1991 }
1992
1993 if (!$res->{ip} && !$res->{ip6}) {
1994 return { ip => 'dhcp', ip6 => 'dhcp' };
1995 }
1996
1e3baf05
DM
1997 return $res;
1998}
1999
2000sub print_net {
2001 my $net = shift;
2002
cd9c34d1 2003 return PVE::JSONSchema::print_property_string($net, $net_fmt);
1e3baf05
DM
2004}
2005
2006sub add_random_macs {
2007 my ($settings) = @_;
2008
2009 foreach my $opt (keys %$settings) {
2010 next if $opt !~ m/^net(\d+)$/;
2011 my $net = parse_net($settings->{$opt});
2012 next if !$net;
2013 $settings->{$opt} = print_net($net);
2014 }
2015}
2016
055d554d
DM
2017sub vm_is_volid_owner {
2018 my ($storecfg, $vmid, $volid) = @_;
2019
2020 if ($volid !~ m|^/|) {
2021 my ($path, $owner);
2022 eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); };
2023 if ($owner && ($owner == $vmid)) {
2024 return 1;
2025 }
2026 }
2027
d1c1af4b 2028 return;
055d554d
DM
2029}
2030
055d554d
DM
2031sub vmconfig_register_unused_drive {
2032 my ($storecfg, $vmid, $conf, $drive) = @_;
2033
2d9ddec5
WB
2034 if (drive_is_cloudinit($drive)) {
2035 eval { PVE::Storage::vdisk_free($storecfg, $drive->{file}) };
2036 warn $@ if $@;
95a5135d 2037 delete $conf->{cloudinit};
2d9ddec5 2038 } elsif (!drive_is_cdrom($drive)) {
055d554d
DM
2039 my $volid = $drive->{file};
2040 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
8793d495 2041 PVE::QemuConfig->add_unused_volume($conf, $volid, $vmid);
055d554d
DM
2042 }
2043 }
2044}
2045
1f30ac3a 2046# smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
ff6ffe20 2047my $smbios1_fmt = {
bd27e851
WB
2048 uuid => {
2049 type => 'string',
2050 pattern => '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}',
2051 format_description => 'UUID',
52261945 2052 description => "Set SMBIOS1 UUID.",
bd27e851
WB
2053 optional => 1,
2054 },
2055 version => {
2056 type => 'string',
1f30ac3a
CE
2057 pattern => '[A-Za-z0-9+\/]+={0,2}',
2058 format_description => 'Base64 encoded string',
52261945 2059 description => "Set SMBIOS1 version.",
bd27e851
WB
2060 optional => 1,
2061 },
2062 serial => {
2063 type => 'string',
1f30ac3a
CE
2064 pattern => '[A-Za-z0-9+\/]+={0,2}',
2065 format_description => 'Base64 encoded string',
52261945 2066 description => "Set SMBIOS1 serial number.",
bd27e851
WB
2067 optional => 1,
2068 },
2069 manufacturer => {
2070 type => 'string',
1f30ac3a
CE
2071 pattern => '[A-Za-z0-9+\/]+={0,2}',
2072 format_description => 'Base64 encoded string',
52261945 2073 description => "Set SMBIOS1 manufacturer.",
bd27e851
WB
2074 optional => 1,
2075 },
2076 product => {
2077 type => 'string',
1f30ac3a
CE
2078 pattern => '[A-Za-z0-9+\/]+={0,2}',
2079 format_description => 'Base64 encoded string',
52261945 2080 description => "Set SMBIOS1 product ID.",
bd27e851
WB
2081 optional => 1,
2082 },
2083 sku => {
2084 type => 'string',
1f30ac3a
CE
2085 pattern => '[A-Za-z0-9+\/]+={0,2}',
2086 format_description => 'Base64 encoded string',
52261945 2087 description => "Set SMBIOS1 SKU string.",
bd27e851
WB
2088 optional => 1,
2089 },
2090 family => {
2091 type => 'string',
1f30ac3a
CE
2092 pattern => '[A-Za-z0-9+\/]+={0,2}',
2093 format_description => 'Base64 encoded string',
52261945 2094 description => "Set SMBIOS1 family string.",
bd27e851
WB
2095 optional => 1,
2096 },
1f30ac3a
CE
2097 base64 => {
2098 type => 'boolean',
2099 description => 'Flag to indicate that the SMBIOS values are base64 encoded',
2100 optional => 1,
2101 },
2796e7d5
DM
2102};
2103
2796e7d5
DM
2104sub parse_smbios1 {
2105 my ($data) = @_;
2106
4df98f2f 2107 my $res = eval { parse_property_string($smbios1_fmt, $data) };
bd27e851 2108 warn $@ if $@;
2796e7d5
DM
2109 return $res;
2110}
2111
cd11416f
DM
2112sub print_smbios1 {
2113 my ($smbios1) = @_;
ff6ffe20 2114 return PVE::JSONSchema::print_property_string($smbios1, $smbios1_fmt);
cd11416f
DM
2115}
2116
ff6ffe20 2117PVE::JSONSchema::register_format('pve-qm-smbios1', $smbios1_fmt);
2796e7d5 2118
0ea9541d
DM
2119sub parse_watchdog {
2120 my ($value) = @_;
2121
d1c1af4b 2122 return if !$value;
0ea9541d 2123
4df98f2f 2124 my $res = eval { parse_property_string($watchdog_fmt, $value) };
ec3582b5 2125 warn $@ if $@;
0ea9541d
DM
2126 return $res;
2127}
2128
9d66b397 2129sub parse_guest_agent {
a2af1bbe 2130 my ($conf) = @_;
9d66b397 2131
a2af1bbe 2132 return {} if !defined($conf->{agent});
9d66b397 2133
a2af1bbe 2134 my $res = eval { parse_property_string($agent_fmt, $conf->{agent}) };
9d66b397
SI
2135 warn $@ if $@;
2136
2137 # if the agent is disabled ignore the other potentially set properties
2138 return {} if !$res->{enabled};
2139 return $res;
2140}
2141
a2af1bbe
TL
2142sub get_qga_key {
2143 my ($conf, $key) = @_;
2144 return undef if !defined($conf->{agent});
2145
2146 my $agent = parse_guest_agent($conf);
2147 return $agent->{$key};
2148}
2149
55655ebc
DC
2150sub parse_vga {
2151 my ($value) = @_;
2152
2153 return {} if !$value;
4df98f2f 2154 my $res = eval { parse_property_string($vga_fmt, $value) };
55655ebc
DC
2155 warn $@ if $@;
2156 return $res;
2157}
2158
2cf61f33
SR
2159sub parse_rng {
2160 my ($value) = @_;
2161
d1c1af4b 2162 return if !$value;
2cf61f33 2163
4df98f2f 2164 my $res = eval { parse_property_string($rng_fmt, $value) };
2cf61f33
SR
2165 warn $@ if $@;
2166 return $res;
2167}
2168
26b443c8
TL
2169sub parse_meta_info {
2170 my ($value) = @_;
2171
2172 return if !$value;
2173
2174 my $res = eval { parse_property_string($meta_info_fmt, $value) };
2175 warn $@ if $@;
2176 return $res;
2177}
2178
2179sub new_meta_info_string {
2180 my () = @_; # for now do not allow to override any value
2181
2182 return PVE::JSONSchema::print_property_string(
2183 {
af2a1a1c 2184 'creation-qemu' => kvm_user_version(),
26b443c8
TL
2185 ctime => "". int(time()),
2186 },
2187 $meta_info_fmt
2188 );
2189}
2190
cc181036
TL
2191sub qemu_created_version_fixups {
2192 my ($conf, $forcemachine, $kvmver) = @_;
2193
2194 my $meta = parse_meta_info($conf->{meta}) // {};
2195 my $forced_vers = PVE::QemuServer::Machine::extract_version($forcemachine);
2196
2197 # check if we need to apply some handling for VMs that always use the latest machine version but
2198 # had a machine version transition happen that affected HW such that, e.g., an OS config change
2199 # would be required (we do not want to pin machine version for non-windows OS type)
2200 if (
2201 (!defined($conf->{machine}) || $conf->{machine} =~ m/^(?:pc|q35|virt)$/) # non-versioned machine
2202 && (!defined($meta->{'creation-qemu'}) || !min_version($meta->{'creation-qemu'}, 6, 1)) # created before 6.1
2203 && (!$forced_vers || min_version($forced_vers, 6, 1)) # handle snapshot-rollback/migrations
2204 && min_version($kvmver, 6, 1) # only need to apply the change since 6.1
2205 ) {
2206 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
2207 if ($q35 && $conf->{ostype} && $conf->{ostype} eq 'l26') {
2208 # this changed to default-on in Q 6.1 for q35 machines, it will mess with PCI slot view
2209 # and thus with the predictable interface naming of systemd
2210 return ['-global', 'ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off'];
2211 }
2212 }
2213 return;
2214}
2215
1e3baf05
DM
2216PVE::JSONSchema::register_format('pve-qm-usb-device', \&verify_usb_device);
2217sub verify_usb_device {
2218 my ($value, $noerr) = @_;
2219
2220 return $value if parse_usb_device($value);
2221
d1c1af4b 2222 return if $noerr;
19672434 2223
1e3baf05
DM
2224 die "unable to parse usb device\n";
2225}
2226
1e3baf05
DM
2227# add JSON properties for create and set function
2228sub json_config_properties {
c1accf9d 2229 my ($prop, $with_disk_alloc) = @_;
1e3baf05 2230
5a08fb9c
TL
2231 my $skip_json_config_opts = {
2232 parent => 1,
2233 snaptime => 1,
2234 vmstate => 1,
2235 runningmachine => 1,
2236 runningcpu => 1,
26b443c8 2237 meta => 1,
5a08fb9c
TL
2238 };
2239
1e3baf05 2240 foreach my $opt (keys %$confdesc) {
5a08fb9c 2241 next if $skip_json_config_opts->{$opt};
c1accf9d
FE
2242
2243 if ($with_disk_alloc && is_valid_drivename($opt)) {
2244 $prop->{$opt} = $PVE::QemuServer::Drive::drivedesc_hash_with_alloc->{$opt};
2245 } else {
2246 $prop->{$opt} = $confdesc->{$opt};
2247 }
1e3baf05
DM
2248 }
2249
2250 return $prop;
2251}
2252
39051ac0
FE
2253# Properties that we can read from an OVF file
2254sub json_ovf_properties {
2255 my $prop = {};
2256
2257 for my $device (PVE::QemuServer::Drive::valid_drive_names()) {
2258 $prop->{$device} = {
2259 type => 'string',
2260 format => 'pve-volume-id-or-absolute-path',
2261 description => "Disk image that gets imported to $device",
2262 optional => 1,
2263 };
2264 }
2265
2266 $prop->{cores} = {
2267 type => 'integer',
2268 description => "The number of CPU cores.",
2269 optional => 1,
2270 };
2271 $prop->{memory} = {
2272 type => 'integer',
2273 description => "Amount of RAM for the VM in MB.",
2274 optional => 1,
2275 };
2276 $prop->{name} = {
2277 type => 'string',
2278 description => "Name of the VM.",
2279 optional => 1,
2280 };
2281
2282 return $prop;
2283}
2284
d41121fd
DM
2285# return copy of $confdesc_cloudinit to generate documentation
2286sub cloudinit_config_properties {
2287
2288 return dclone($confdesc_cloudinit);
2289}
2290
1e3baf05
DM
2291sub check_type {
2292 my ($key, $value) = @_;
2293
2294 die "unknown setting '$key'\n" if !$confdesc->{$key};
2295
2296 my $type = $confdesc->{$key}->{type};
2297
6b64503e 2298 if (!defined($value)) {
1e3baf05
DM
2299 die "got undefined value\n";
2300 }
2301
2302 if ($value =~ m/[\n\r]/) {
2303 die "property contains a line feed\n";
2304 }
2305
2306 if ($type eq 'boolean') {
19672434
DM
2307 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
2308 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
2309 die "type check ('boolean') failed - got '$value'\n";
1e3baf05
DM
2310 } elsif ($type eq 'integer') {
2311 return int($1) if $value =~ m/^(\d+)$/;
2312 die "type check ('integer') failed - got '$value'\n";
04432191
AD
2313 } elsif ($type eq 'number') {
2314 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
2315 die "type check ('number') failed - got '$value'\n";
1e3baf05
DM
2316 } elsif ($type eq 'string') {
2317 if (my $fmt = $confdesc->{$key}->{format}) {
1e3baf05 2318 PVE::JSONSchema::check_format($fmt, $value);
19672434
DM
2319 return $value;
2320 }
1e3baf05 2321 $value =~ s/^\"(.*)\"$/$1/;
19672434 2322 return $value;
1e3baf05
DM
2323 } else {
2324 die "internal error"
2325 }
2326}
2327
1e3baf05 2328sub destroy_vm {
75854662 2329 my ($storecfg, $vmid, $skiplock, $replacement_conf, $purge_unreferenced) = @_;
1e3baf05 2330
ffda963f 2331 my $conf = PVE::QemuConfig->load_config($vmid);
1e3baf05 2332
ffda963f 2333 PVE::QemuConfig->check_lock($conf) if !$skiplock;
1e3baf05 2334
5e67a2d2
DC
2335 if ($conf->{template}) {
2336 # check if any base image is still used by a linked clone
3ab0f925 2337 PVE::QemuConfig->foreach_volume_full($conf, { include_unused => 1 }, sub {
5e67a2d2 2338 my ($ds, $drive) = @_;
5e67a2d2
DC
2339 return if drive_is_cdrom($drive);
2340
2341 my $volid = $drive->{file};
5e67a2d2
DC
2342 return if !$volid || $volid =~ m|^/|;
2343
2344 die "base volume '$volid' is still in use by linked cloned\n"
2345 if PVE::Storage::volume_is_base_and_used($storecfg, $volid);
2346
2347 });
2348 }
2349
3e07c6d5 2350 my $volids = {};
ba1a1984 2351 my $remove_owned_drive = sub {
1e3baf05 2352 my ($ds, $drive) = @_;
9c52f5ed 2353 return if drive_is_cdrom($drive, 1);
1e3baf05
DM
2354
2355 my $volid = $drive->{file};
ff1a2432 2356 return if !$volid || $volid =~ m|^/|;
3e07c6d5 2357 return if $volids->{$volid};
1e3baf05 2358
6b64503e 2359 my ($path, $owner) = PVE::Storage::path($storecfg, $volid);
ff1a2432 2360 return if !$path || !$owner || ($owner != $vmid);
1e3baf05 2361
3e07c6d5 2362 $volids->{$volid} = 1;
a2f50f01 2363 eval { PVE::Storage::vdisk_free($storecfg, $volid) };
31b52247 2364 warn "Could not remove disk '$volid', check manually: $@" if $@;
ba1a1984
FE
2365 };
2366
2367 # only remove disks owned by this VM (referenced in the config)
2368 my $include_opts = {
2369 include_unused => 1,
2370 extra_keys => ['vmstate'],
2371 };
2372 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $remove_owned_drive);
2373
2374 for my $snap (values %{$conf->{snapshots}}) {
2375 next if !defined($snap->{vmstate});
2376 my $drive = PVE::QemuConfig->parse_volume('vmstate', $snap->{vmstate}, 1);
2377 next if !defined($drive);
2378 $remove_owned_drive->('vmstate', $drive);
2379 }
19672434 2380
3e07c6d5
FG
2381 PVE::QemuConfig->foreach_volume_full($conf->{pending}, $include_opts, $remove_owned_drive);
2382
75854662 2383 if ($purge_unreferenced) { # also remove unreferenced disk
d0ff75d9 2384 my $vmdisks = PVE::Storage::vdisk_list($storecfg, undef, $vmid, undef, 'images');
75854662
TL
2385 PVE::Storage::foreach_volid($vmdisks, sub {
2386 my ($volid, $sid, $volname, $d) = @_;
2387 eval { PVE::Storage::vdisk_free($storecfg, $volid) };
2388 warn $@ if $@;
2389 });
2390 }
dfda979e 2391
b04ea584 2392 if (defined $replacement_conf) {
8baf8bc7 2393 PVE::QemuConfig->write_config($vmid, $replacement_conf);
dfda979e
DJ
2394 } else {
2395 PVE::QemuConfig->destroy_config($vmid);
2396 }
1e3baf05
DM
2397}
2398
1e3baf05 2399sub parse_vm_config {
ad5812d8 2400 my ($filename, $raw, $strict) = @_;
1e3baf05 2401
d1c1af4b 2402 return if !defined($raw);
1e3baf05 2403
554ac7e7 2404 my $res = {
fc1ddcdc 2405 digest => Digest::SHA::sha1_hex($raw),
0d18dcfc 2406 snapshots => {},
0d732d16 2407 pending => {},
95a5135d 2408 cloudinit => {},
554ac7e7 2409 };
1e3baf05 2410
ad5812d8
FG
2411 my $handle_error = sub {
2412 my ($msg) = @_;
2413
2414 if ($strict) {
2415 die $msg;
2416 } else {
2417 warn $msg;
2418 }
2419 };
2420
19672434 2421 $filename =~ m|/qemu-server/(\d+)\.conf$|
1e3baf05
DM
2422 || die "got strange filename '$filename'";
2423
2424 my $vmid = $1;
2425
0d18dcfc 2426 my $conf = $res;
b0ec896e 2427 my $descr;
cbfc9d75
TL
2428 my $finish_description = sub {
2429 if (defined($descr)) {
2430 $descr =~ s/\s+$//;
2431 $conf->{description} = $descr;
2432 }
2433 $descr = undef;
2434 };
e297c490 2435 my $section = '';
0581fe4f 2436
0d18dcfc
DM
2437 my @lines = split(/\n/, $raw);
2438 foreach my $line (@lines) {
1e3baf05 2439 next if $line =~ m/^\s*$/;
be190583 2440
eab09f4e 2441 if ($line =~ m/^\[PENDING\]\s*$/i) {
e297c490 2442 $section = 'pending';
cbfc9d75 2443 $finish_description->();
e297c490 2444 $conf = $res->{$section} = {};
eab09f4e 2445 next;
95a5135d
AD
2446 } elsif ($line =~ m/^\[special:cloudinit\]\s*$/i) {
2447 $section = 'cloudinit';
eb9923f9 2448 $finish_description->();
95a5135d
AD
2449 $conf = $res->{$section} = {};
2450 next;
eab09f4e 2451
0d732d16 2452 } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) {
e297c490 2453 $section = $1;
cbfc9d75 2454 $finish_description->();
e297c490 2455 $conf = $res->{snapshots}->{$section} = {};
0d18dcfc
DM
2456 next;
2457 }
1e3baf05 2458
d1e7b922 2459 if ($line =~ m/^\#(.*)$/) {
b0ec896e 2460 $descr = '' if !defined($descr);
0581fe4f
DM
2461 $descr .= PVE::Tools::decode_text($1) . "\n";
2462 next;
2463 }
2464
1e3baf05 2465 if ($line =~ m/^(description):\s*(.*\S)\s*$/) {
b0ec896e 2466 $descr = '' if !defined($descr);
0581fe4f 2467 $descr .= PVE::Tools::decode_text($2);
0d18dcfc
DM
2468 } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) {
2469 $conf->{snapstate} = $1;
1e3baf05
DM
2470 } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) {
2471 my $key = $1;
2472 my $value = $2;
0d18dcfc 2473 $conf->{$key} = $value;
ef824322 2474 } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) {
e297c490 2475 my $value = $1;
ef824322
DM
2476 if ($section eq 'pending') {
2477 $conf->{delete} = $value; # we parse this later
2478 } else {
ad5812d8 2479 $handle_error->("vm $vmid - property 'delete' is only allowed in [PENDING]\n");
eab09f4e 2480 }
15cf7698 2481 } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(.+?)\s*$/) {
1e3baf05
DM
2482 my $key = $1;
2483 my $value = $2;
2484 eval { $value = check_type($key, $value); };
2485 if ($@) {
ad5812d8 2486 $handle_error->("vm $vmid - unable to parse value of '$key' - $@");
1e3baf05 2487 } else {
b799312f 2488 $key = 'ide2' if $key eq 'cdrom';
1e3baf05 2489 my $fmt = $confdesc->{$key}->{format};
b799312f 2490 if ($fmt && $fmt =~ /^pve-qm-(?:ide|scsi|virtio|sata)$/) {
1e3baf05
DM
2491 my $v = parse_drive($key, $value);
2492 if (my $volid = filename_to_volume_id($vmid, $v->{file}, $v->{media})) {
2493 $v->{file} = $volid;
71c58bb7 2494 $value = print_drive($v);
1e3baf05 2495 } else {
ad5812d8 2496 $handle_error->("vm $vmid - unable to parse value of '$key'\n");
1e3baf05
DM
2497 next;
2498 }
2499 }
2500
b799312f 2501 $conf->{$key} = $value;
1e3baf05 2502 }
f8d2a1ce 2503 } else {
ad5812d8 2504 $handle_error->("vm $vmid - unable to parse config: $line\n");
1e3baf05
DM
2505 }
2506 }
2507
cbfc9d75 2508 $finish_description->();
0d18dcfc 2509 delete $res->{snapstate}; # just to be sure
1e3baf05
DM
2510
2511 return $res;
2512}
2513
1858638f
DM
2514sub write_vm_config {
2515 my ($filename, $conf) = @_;
1e3baf05 2516
0d18dcfc
DM
2517 delete $conf->{snapstate}; # just to be sure
2518
1858638f
DM
2519 if ($conf->{cdrom}) {
2520 die "option ide2 conflicts with cdrom\n" if $conf->{ide2};
2521 $conf->{ide2} = $conf->{cdrom};
2522 delete $conf->{cdrom};
2523 }
1e3baf05
DM
2524
2525 # we do not use 'smp' any longer
1858638f
DM
2526 if ($conf->{sockets}) {
2527 delete $conf->{smp};
2528 } elsif ($conf->{smp}) {
2529 $conf->{sockets} = $conf->{smp};
2530 delete $conf->{cores};
2531 delete $conf->{smp};
1e3baf05
DM
2532 }
2533
ee2f90b1 2534 my $used_volids = {};
0d18dcfc 2535
ee2f90b1 2536 my $cleanup_config = sub {
ef824322 2537 my ($cref, $pending, $snapname) = @_;
1858638f 2538
ee2f90b1
DM
2539 foreach my $key (keys %$cref) {
2540 next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' ||
95a5135d 2541 $key eq 'snapstate' || $key eq 'pending' || $key eq 'cloudinit';
ee2f90b1 2542 my $value = $cref->{$key};
ef824322
DM
2543 if ($key eq 'delete') {
2544 die "propertry 'delete' is only allowed in [PENDING]\n"
2545 if !$pending;
2546 # fixme: check syntax?
2547 next;
2548 }
ee2f90b1
DM
2549 eval { $value = check_type($key, $value); };
2550 die "unable to parse value of '$key' - $@" if $@;
1858638f 2551
ee2f90b1
DM
2552 $cref->{$key} = $value;
2553
74479ee9 2554 if (!$snapname && is_valid_drivename($key)) {
ed221350 2555 my $drive = parse_drive($key, $value);
ee2f90b1
DM
2556 $used_volids->{$drive->{file}} = 1 if $drive && $drive->{file};
2557 }
1e3baf05 2558 }
ee2f90b1
DM
2559 };
2560
2561 &$cleanup_config($conf);
ef824322
DM
2562
2563 &$cleanup_config($conf->{pending}, 1);
2564
95a5135d
AD
2565 &$cleanup_config($conf->{cloudinit});
2566
ee2f90b1 2567 foreach my $snapname (keys %{$conf->{snapshots}}) {
15c6e277 2568 die "internal error: snapshot name '$snapname' is forbidden" if lc($snapname) eq 'pending';
ef824322 2569 &$cleanup_config($conf->{snapshots}->{$snapname}, undef, $snapname);
1e3baf05
DM
2570 }
2571
1858638f
DM
2572 # remove 'unusedX' settings if we re-add a volume
2573 foreach my $key (keys %$conf) {
2574 my $value = $conf->{$key};
ee2f90b1 2575 if ($key =~ m/^unused/ && $used_volids->{$value}) {
1858638f 2576 delete $conf->{$key};
1e3baf05 2577 }
1858638f 2578 }
be190583 2579
0d18dcfc 2580 my $generate_raw_config = sub {
b0ec896e 2581 my ($conf, $pending) = @_;
0581fe4f 2582
0d18dcfc
DM
2583 my $raw = '';
2584
2585 # add description as comment to top of file
b0ec896e
DM
2586 if (defined(my $descr = $conf->{description})) {
2587 if ($descr) {
2588 foreach my $cl (split(/\n/, $descr)) {
2589 $raw .= '#' . PVE::Tools::encode_text($cl) . "\n";
2590 }
2591 } else {
2592 $raw .= "#\n" if $pending;
2593 }
0d18dcfc
DM
2594 }
2595
2596 foreach my $key (sort keys %$conf) {
95a5135d 2597 next if $key =~ /^(digest|description|pending|cloudinit|snapshots)$/;
0d18dcfc
DM
2598 $raw .= "$key: $conf->{$key}\n";
2599 }
2600 return $raw;
2601 };
0581fe4f 2602
0d18dcfc 2603 my $raw = &$generate_raw_config($conf);
ef824322
DM
2604
2605 if (scalar(keys %{$conf->{pending}})){
2606 $raw .= "\n[PENDING]\n";
b0ec896e 2607 $raw .= &$generate_raw_config($conf->{pending}, 1);
ef824322
DM
2608 }
2609
95a5135d
AD
2610 if (scalar(keys %{$conf->{cloudinit}})){
2611 $raw .= "\n[special:cloudinit]\n";
2612 $raw .= &$generate_raw_config($conf->{cloudinit});
2613 }
2614
0d18dcfc
DM
2615 foreach my $snapname (sort keys %{$conf->{snapshots}}) {
2616 $raw .= "\n[$snapname]\n";
2617 $raw .= &$generate_raw_config($conf->{snapshots}->{$snapname});
1858638f 2618 }
1e3baf05 2619
1858638f
DM
2620 return $raw;
2621}
1e3baf05 2622
19672434 2623sub load_defaults {
1e3baf05
DM
2624
2625 my $res = {};
2626
2627 # we use static defaults from our JSON schema configuration
2628 foreach my $key (keys %$confdesc) {
2629 if (defined(my $default = $confdesc->{$key}->{default})) {
2630 $res->{$key} = $default;
2631 }
2632 }
19672434 2633
1e3baf05
DM
2634 return $res;
2635}
2636
2637sub config_list {
2638 my $vmlist = PVE::Cluster::get_vmlist();
2639 my $res = {};
2640 return $res if !$vmlist || !$vmlist->{ids};
2641 my $ids = $vmlist->{ids};
38277afc 2642 my $nodename = nodename();
1e3baf05 2643
1e3baf05
DM
2644 foreach my $vmid (keys %$ids) {
2645 my $d = $ids->{$vmid};
2646 next if !$d->{node} || $d->{node} ne $nodename;
5ee957cc 2647 next if !$d->{type} || $d->{type} ne 'qemu';
1e3baf05
DM
2648 $res->{$vmid}->{exists} = 1;
2649 }
2650 return $res;
2651}
2652
64e13401
DM
2653# test if VM uses local resources (to prevent migration)
2654sub check_local_resources {
2655 my ($conf, $noerr) = @_;
2656
ca6abacf 2657 my @loc_res = ();
19672434 2658
ca6abacf
TM
2659 push @loc_res, "hostusb" if $conf->{hostusb}; # old syntax
2660 push @loc_res, "hostpci" if $conf->{hostpci}; # old syntax
64e13401 2661
ca6abacf 2662 push @loc_res, "ivshmem" if $conf->{ivshmem};
6dbcb073 2663
0d29ab3b 2664 foreach my $k (keys %$conf) {
a9ce7583 2665 next if $k =~ m/^usb/ && ($conf->{$k} =~ m/^spice(?![^,])/);
d44712fc
EK
2666 # sockets are safe: they will recreated be on the target side post-migrate
2667 next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket');
ca6abacf 2668 push @loc_res, $k if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/;
64e13401
DM
2669 }
2670
ca6abacf 2671 die "VM uses local resources\n" if scalar @loc_res && !$noerr;
64e13401 2672
ca6abacf 2673 return \@loc_res;
64e13401
DM
2674}
2675
719893a9 2676# check if used storages are available on all nodes (use by migrate)
47152e2e
DM
2677sub check_storage_availability {
2678 my ($storecfg, $conf, $node) = @_;
2679
912792e2 2680 PVE::QemuConfig->foreach_volume($conf, sub {
47152e2e
DM
2681 my ($ds, $drive) = @_;
2682
2683 my $volid = $drive->{file};
2684 return if !$volid;
2685
2686 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2687 return if !$sid;
2688
2689 # check if storage is available on both nodes
0d2db084
FE
2690 my $scfg = PVE::Storage::storage_check_enabled($storecfg, $sid);
2691 PVE::Storage::storage_check_enabled($storecfg, $sid, $node);
24b84b47 2692
3148f0b0
TL
2693 my ($vtype) = PVE::Storage::parse_volname($storecfg, $volid);
2694
2695 die "$volid: content type '$vtype' is not available on storage '$sid'\n"
2696 if !$scfg->{content}->{$vtype};
47152e2e
DM
2697 });
2698}
2699
719893a9
DM
2700# list nodes where all VM images are available (used by has_feature API)
2701sub shared_nodes {
2702 my ($conf, $storecfg) = @_;
2703
2704 my $nodelist = PVE::Cluster::get_nodelist();
2705 my $nodehash = { map { $_ => 1 } @$nodelist };
38277afc 2706 my $nodename = nodename();
be190583 2707
912792e2 2708 PVE::QemuConfig->foreach_volume($conf, sub {
719893a9
DM
2709 my ($ds, $drive) = @_;
2710
2711 my $volid = $drive->{file};
2712 return if !$volid;
2713
2714 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2715 if ($storeid) {
2716 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
2717 if ($scfg->{disable}) {
2718 $nodehash = {};
2719 } elsif (my $avail = $scfg->{nodes}) {
2720 foreach my $node (keys %$nodehash) {
2721 delete $nodehash->{$node} if !$avail->{$node};
2722 }
2723 } elsif (!$scfg->{shared}) {
2724 foreach my $node (keys %$nodehash) {
2725 delete $nodehash->{$node} if $node ne $nodename
2726 }
2727 }
2728 }
2729 });
2730
2731 return $nodehash
2732}
2733
f25852c2
TM
2734sub check_local_storage_availability {
2735 my ($conf, $storecfg) = @_;
2736
2737 my $nodelist = PVE::Cluster::get_nodelist();
2738 my $nodehash = { map { $_ => {} } @$nodelist };
2739
912792e2 2740 PVE::QemuConfig->foreach_volume($conf, sub {
f25852c2
TM
2741 my ($ds, $drive) = @_;
2742
2743 my $volid = $drive->{file};
2744 return if !$volid;
2745
2746 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
2747 if ($storeid) {
2748 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
2749
2750 if ($scfg->{disable}) {
2751 foreach my $node (keys %$nodehash) {
32075a2c 2752 $nodehash->{$node}->{unavailable_storages}->{$storeid} = 1;
f25852c2
TM
2753 }
2754 } elsif (my $avail = $scfg->{nodes}) {
2755 foreach my $node (keys %$nodehash) {
2756 if (!$avail->{$node}) {
32075a2c 2757 $nodehash->{$node}->{unavailable_storages}->{$storeid} = 1;
f25852c2
TM
2758 }
2759 }
2760 }
2761 }
2762 });
2763
32075a2c
TL
2764 foreach my $node (values %$nodehash) {
2765 if (my $unavail = $node->{unavailable_storages}) {
2766 $node->{unavailable_storages} = [ sort keys %$unavail ];
2767 }
2768 }
2769
f25852c2
TM
2770 return $nodehash
2771}
2772
babf613a 2773# Compat only, use assert_config_exists_on_node and vm_running_locally where possible
1e3baf05 2774sub check_running {
7e8dcf2c 2775 my ($vmid, $nocheck, $node) = @_;
1e3baf05 2776
babf613a
SR
2777 PVE::QemuConfig::assert_config_exists_on_node($vmid, $node) if !$nocheck;
2778 return PVE::QemuServer::Helpers::vm_running_locally($vmid);
1e3baf05
DM
2779}
2780
2781sub vzlist {
19672434 2782
1e3baf05
DM
2783 my $vzlist = config_list();
2784
d036e418 2785 my $fd = IO::Dir->new($PVE::QemuServer::Helpers::var_run_tmpdir) || return $vzlist;
1e3baf05 2786
19672434 2787 while (defined(my $de = $fd->read)) {
1e3baf05
DM
2788 next if $de !~ m/^(\d+)\.pid$/;
2789 my $vmid = $1;
6b64503e
DM
2790 next if !defined($vzlist->{$vmid});
2791 if (my $pid = check_running($vmid)) {
1e3baf05
DM
2792 $vzlist->{$vmid}->{pid} = $pid;
2793 }
2794 }
2795
2796 return $vzlist;
2797}
2798
b1a70cab
DM
2799our $vmstatus_return_properties = {
2800 vmid => get_standard_option('pve-vmid'),
2801 status => {
2802 description => "Qemu process status.",
2803 type => 'string',
2804 enum => ['stopped', 'running'],
2805 },
2806 maxmem => {
2807 description => "Maximum memory in bytes.",
2808 type => 'integer',
2809 optional => 1,
2810 renderer => 'bytes',
2811 },
2812 maxdisk => {
2813 description => "Root disk size in bytes.",
2814 type => 'integer',
2815 optional => 1,
2816 renderer => 'bytes',
2817 },
2818 name => {
2819 description => "VM name.",
2820 type => 'string',
2821 optional => 1,
2822 },
2823 qmpstatus => {
2824 description => "Qemu QMP agent status.",
2825 type => 'string',
2826 optional => 1,
2827 },
2828 pid => {
2829 description => "PID of running qemu process.",
2830 type => 'integer',
2831 optional => 1,
2832 },
2833 uptime => {
2834 description => "Uptime.",
2835 type => 'integer',
2836 optional => 1,
2837 renderer => 'duration',
2838 },
2839 cpus => {
2840 description => "Maximum usable CPUs.",
2841 type => 'number',
2842 optional => 1,
2843 },
e6ed61b4 2844 lock => {
11efdfa5 2845 description => "The current config lock, if any.",
e6ed61b4
DC
2846 type => 'string',
2847 optional => 1,
b8e7068a
DC
2848 },
2849 tags => {
2850 description => "The current configured tags, if any",
2851 type => 'string',
2852 optional => 1,
2853 },
949112c3
FE
2854 'running-machine' => {
2855 description => "The currently running machine type (if running).",
2856 type => 'string',
2857 optional => 1,
2858 },
2859 'running-qemu' => {
2860 description => "The currently running QEMU version (if running).",
2861 type => 'string',
2862 optional => 1,
2863 },
b1a70cab
DM
2864};
2865
1e3baf05
DM
2866my $last_proc_pid_stat;
2867
03a33f30
DM
2868# get VM status information
2869# This must be fast and should not block ($full == false)
2870# We only query KVM using QMP if $full == true (this can be slow)
1e3baf05 2871sub vmstatus {
03a33f30 2872 my ($opt_vmid, $full) = @_;
1e3baf05
DM
2873
2874 my $res = {};
2875
19672434 2876 my $storecfg = PVE::Storage::config();
1e3baf05
DM
2877
2878 my $list = vzlist();
3618ee99
EK
2879 my $defaults = load_defaults();
2880
694fcad4 2881 my ($uptime) = PVE::ProcFSTools::read_proc_uptime(1);
1e3baf05 2882
ae4915a2
DM
2883 my $cpucount = $cpuinfo->{cpus} || 1;
2884
1e3baf05
DM
2885 foreach my $vmid (keys %$list) {
2886 next if $opt_vmid && ($vmid ne $opt_vmid);
2887
9f78b695 2888 my $conf = PVE::QemuConfig->load_config($vmid);
1e3baf05 2889
ad2cad72 2890 my $d = { vmid => int($vmid) };
8a0addab 2891 $d->{pid} = int($list->{$vmid}->{pid}) if $list->{$vmid}->{pid};
1e3baf05
DM
2892
2893 # fixme: better status?
2894 $d->{status} = $list->{$vmid}->{pid} ? 'running' : 'stopped';
2895
776c5f50 2896 my $size = PVE::QemuServer::Drive::bootdisk_size($storecfg, $conf);
af990afe
DM
2897 if (defined($size)) {
2898 $d->{disk} = 0; # no info available
1e3baf05
DM
2899 $d->{maxdisk} = $size;
2900 } else {
2901 $d->{disk} = 0;
2902 $d->{maxdisk} = 0;
2903 }
2904
3618ee99
EK
2905 $d->{cpus} = ($conf->{sockets} || $defaults->{sockets})
2906 * ($conf->{cores} || $defaults->{cores});
ae4915a2 2907 $d->{cpus} = $cpucount if $d->{cpus} > $cpucount;
d7c8364b 2908 $d->{cpus} = $conf->{vcpus} if $conf->{vcpus};
ae4915a2 2909
1e3baf05 2910 $d->{name} = $conf->{name} || "VM $vmid";
3618ee99
EK
2911 $d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024)
2912 : $defaults->{memory}*(1024*1024);
1e3baf05 2913
8b1accf7 2914 if ($conf->{balloon}) {
4bdb0514 2915 $d->{balloon_min} = $conf->{balloon}*(1024*1024);
3618ee99
EK
2916 $d->{shares} = defined($conf->{shares}) ? $conf->{shares}
2917 : $defaults->{shares};
8b1accf7
DM
2918 }
2919
1e3baf05
DM
2920 $d->{uptime} = 0;
2921 $d->{cpu} = 0;
1e3baf05
DM
2922 $d->{mem} = 0;
2923
2924 $d->{netout} = 0;
2925 $d->{netin} = 0;
2926
2927 $d->{diskread} = 0;
2928 $d->{diskwrite} = 0;
2929
75a2a423 2930 $d->{template} = 1 if PVE::QemuConfig->is_template($conf);
4d8c851b 2931
8107b378 2932 $d->{serial} = 1 if conf_has_serial($conf);
e6ed61b4 2933 $d->{lock} = $conf->{lock} if $conf->{lock};
b8e7068a 2934 $d->{tags} = $conf->{tags} if defined($conf->{tags});
8107b378 2935
1e3baf05
DM
2936 $res->{$vmid} = $d;
2937 }
2938
2939 my $netdev = PVE::ProcFSTools::read_proc_net_dev();
2940 foreach my $dev (keys %$netdev) {
2941 next if $dev !~ m/^tap([1-9]\d*)i/;
2942 my $vmid = $1;
2943 my $d = $res->{$vmid};
2944 next if !$d;
19672434 2945
1e3baf05
DM
2946 $d->{netout} += $netdev->{$dev}->{receive};
2947 $d->{netin} += $netdev->{$dev}->{transmit};
604ea644
AD
2948
2949 if ($full) {
ad2cad72
FE
2950 $d->{nics}->{$dev}->{netout} = int($netdev->{$dev}->{receive});
2951 $d->{nics}->{$dev}->{netin} = int($netdev->{$dev}->{transmit});
604ea644
AD
2952 }
2953
1e3baf05
DM
2954 }
2955
1e3baf05
DM
2956 my $ctime = gettimeofday;
2957
2958 foreach my $vmid (keys %$list) {
2959
2960 my $d = $res->{$vmid};
2961 my $pid = $d->{pid};
2962 next if !$pid;
2963
694fcad4
DM
2964 my $pstat = PVE::ProcFSTools::read_proc_pid_stat($pid);
2965 next if !$pstat; # not running
19672434 2966
694fcad4 2967 my $used = $pstat->{utime} + $pstat->{stime};
1e3baf05 2968
694fcad4 2969 $d->{uptime} = int(($uptime - $pstat->{starttime})/$cpuinfo->{user_hz});
1e3baf05 2970
694fcad4 2971 if ($pstat->{vsize}) {
6b64503e 2972 $d->{mem} = int(($pstat->{rss}/$pstat->{vsize})*$d->{maxmem});
1e3baf05
DM
2973 }
2974
2975 my $old = $last_proc_pid_stat->{$pid};
2976 if (!$old) {
19672434
DM
2977 $last_proc_pid_stat->{$pid} = {
2978 time => $ctime,
1e3baf05
DM
2979 used => $used,
2980 cpu => 0,
1e3baf05
DM
2981 };
2982 next;
2983 }
2984
7f0b5beb 2985 my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz};
1e3baf05
DM
2986
2987 if ($dtime > 1000) {
2988 my $dutime = $used - $old->{used};
2989
ae4915a2 2990 $d->{cpu} = (($dutime/$dtime)* $cpucount) / $d->{cpus};
1e3baf05 2991 $last_proc_pid_stat->{$pid} = {
19672434 2992 time => $ctime,
1e3baf05
DM
2993 used => $used,
2994 cpu => $d->{cpu},
1e3baf05
DM
2995 };
2996 } else {
2997 $d->{cpu} = $old->{cpu};
1e3baf05
DM
2998 }
2999 }
3000
f5eb281a 3001 return $res if !$full;
03a33f30
DM
3002
3003 my $qmpclient = PVE::QMPClient->new();
3004
64e7fcf2
DM
3005 my $ballooncb = sub {
3006 my ($vmid, $resp) = @_;
3007
3008 my $info = $resp->{'return'};
38babf81
DM
3009 return if !$info->{max_mem};
3010
64e7fcf2
DM
3011 my $d = $res->{$vmid};
3012
38babf81
DM
3013 # use memory assigned to VM
3014 $d->{maxmem} = $info->{max_mem};
3015 $d->{balloon} = $info->{actual};
3016
3017 if (defined($info->{total_mem}) && defined($info->{free_mem})) {
3018 $d->{mem} = $info->{total_mem} - $info->{free_mem};
3019 $d->{freemem} = $info->{free_mem};
64e7fcf2
DM
3020 }
3021
604ea644 3022 $d->{ballooninfo} = $info;
64e7fcf2
DM
3023 };
3024
03a33f30
DM
3025 my $blockstatscb = sub {
3026 my ($vmid, $resp) = @_;
3027 my $data = $resp->{'return'} || [];
3028 my $totalrdbytes = 0;
3029 my $totalwrbytes = 0;
604ea644 3030
03a33f30
DM
3031 for my $blockstat (@$data) {
3032 $totalrdbytes = $totalrdbytes + $blockstat->{stats}->{rd_bytes};
3033 $totalwrbytes = $totalwrbytes + $blockstat->{stats}->{wr_bytes};
604ea644
AD
3034
3035 $blockstat->{device} =~ s/drive-//;
3036 $res->{$vmid}->{blockstat}->{$blockstat->{device}} = $blockstat->{stats};
03a33f30
DM
3037 }
3038 $res->{$vmid}->{diskread} = $totalrdbytes;
3039 $res->{$vmid}->{diskwrite} = $totalwrbytes;
3040 };
3041
949112c3
FE
3042 my $machinecb = sub {
3043 my ($vmid, $resp) = @_;
3044 my $data = $resp->{'return'} || [];
3045
3046 $res->{$vmid}->{'running-machine'} =
3047 PVE::QemuServer::Machine::current_from_query_machines($data);
3048 };
3049
3050 my $versioncb = sub {
3051 my ($vmid, $resp) = @_;
3052 my $data = $resp->{'return'} // {};
3053 my $version = 'unknown';
3054
3055 if (my $v = $data->{qemu}) {
3056 $version = $v->{major} . "." . $v->{minor} . "." . $v->{micro};
3057 }
3058
3059 $res->{$vmid}->{'running-qemu'} = $version;
3060 };
3061
03a33f30
DM
3062 my $statuscb = sub {
3063 my ($vmid, $resp) = @_;
64e7fcf2 3064
03a33f30 3065 $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats');
949112c3
FE
3066 $qmpclient->queue_cmd($vmid, $machinecb, 'query-machines');
3067 $qmpclient->queue_cmd($vmid, $versioncb, 'query-version');
64e7fcf2
DM
3068 # this fails if ballon driver is not loaded, so this must be
3069 # the last commnand (following command are aborted if this fails).
38babf81 3070 $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon');
03a33f30
DM
3071
3072 my $status = 'unknown';
3073 if (!defined($status = $resp->{'return'}->{status})) {
3074 warn "unable to get VM status\n";
3075 return;
3076 }
3077
3078 $res->{$vmid}->{qmpstatus} = $resp->{'return'}->{status};
3079 };
3080
3081 foreach my $vmid (keys %$list) {
3082 next if $opt_vmid && ($vmid ne $opt_vmid);
3083 next if !$res->{$vmid}->{pid}; # not running
3084 $qmpclient->queue_cmd($vmid, $statuscb, 'query-status');
3085 }
3086
b017fbda 3087 $qmpclient->queue_execute(undef, 2);
03a33f30 3088
6891fd70
SR
3089 foreach my $vmid (keys %$list) {
3090 next if $opt_vmid && ($vmid ne $opt_vmid);
e5b18771
FG
3091 next if !$res->{$vmid}->{pid}; #not running
3092
6891fd70
SR
3093 # we can't use the $qmpclient since it might have already aborted on
3094 # 'query-balloon', but this might also fail for older versions...
3095 my $qemu_support = eval { mon_cmd($vmid, "query-proxmox-support") };
3096 $res->{$vmid}->{'proxmox-support'} = $qemu_support // {};
3097 }
3098
03a33f30
DM
3099 foreach my $vmid (keys %$list) {
3100 next if $opt_vmid && ($vmid ne $opt_vmid);
3101 $res->{$vmid}->{qmpstatus} = $res->{$vmid}->{status} if !$res->{$vmid}->{qmpstatus};
3102 }
3103
1e3baf05
DM
3104 return $res;
3105}
3106
8107b378
DC
3107sub conf_has_serial {
3108 my ($conf) = @_;
3109
3110 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
3111 if ($conf->{"serial$i"}) {
3112 return 1;
3113 }
3114 }
3115
3116 return 0;
3117}
3118
d5535a00
TL
3119sub conf_has_audio {
3120 my ($conf, $id) = @_;
3121
3122 $id //= 0;
3123 my $audio = $conf->{"audio$id"};
d1c1af4b 3124 return if !defined($audio);
d5535a00 3125
4df98f2f 3126 my $audioproperties = parse_property_string($audio_fmt, $audio);
d5535a00
TL
3127 my $audiodriver = $audioproperties->{driver} // 'spice';
3128
3129 return {
3130 dev => $audioproperties->{device},
b0f96836 3131 dev_id => "audiodev$id",
d5535a00
TL
3132 backend => $audiodriver,
3133 backend_id => "$audiodriver-backend${id}",
3134 };
3135}
3136
b01de199 3137sub audio_devs {
1cc5ed1b 3138 my ($audio, $audiopciaddr, $machine_version) = @_;
b01de199
TL
3139
3140 my $devs = [];
3141
3142 my $id = $audio->{dev_id};
1cc5ed1b
AL
3143 my $audiodev = "";
3144 if (min_version($machine_version, 4, 2)) {
3145 $audiodev = ",audiodev=$audio->{backend_id}";
3146 }
b01de199
TL
3147
3148 if ($audio->{dev} eq 'AC97') {
1cc5ed1b 3149 push @$devs, '-device', "AC97,id=${id}${audiopciaddr}$audiodev";
b01de199
TL
3150 } elsif ($audio->{dev} =~ /intel\-hda$/) {
3151 push @$devs, '-device', "$audio->{dev},id=${id}${audiopciaddr}";
1cc5ed1b
AL
3152 push @$devs, '-device', "hda-micro,id=${id}-codec0,bus=${id}.0,cad=0$audiodev";
3153 push @$devs, '-device', "hda-duplex,id=${id}-codec1,bus=${id}.0,cad=1$audiodev";
b01de199
TL
3154 } else {
3155 die "unkown audio device '$audio->{dev}', implement me!";
3156 }
3157
3158 push @$devs, '-audiodev', "$audio->{backend},id=$audio->{backend_id}";
3159
3160 return $devs;
3161}
3162
f9dde219
SR
3163sub get_tpm_paths {
3164 my ($vmid) = @_;
3165 return {
3166 socket => "/var/run/qemu-server/$vmid.swtpm",
3167 pid => "/var/run/qemu-server/$vmid.swtpm.pid",
3168 };
3169}
3170
3171sub add_tpm_device {
3172 my ($vmid, $devices, $conf) = @_;
3173
3174 return if !$conf->{tpmstate0};
3175
3176 my $paths = get_tpm_paths($vmid);
3177
3178 push @$devices, "-chardev", "socket,id=tpmchar,path=$paths->{socket}";
3179 push @$devices, "-tpmdev", "emulator,id=tpmdev,chardev=tpmchar";
3180 push @$devices, "-device", "tpm-tis,tpmdev=tpmdev";
3181}
3182
3183sub start_swtpm {
3184 my ($storecfg, $vmid, $tpmdrive, $migration) = @_;
3185
3186 return if !$tpmdrive;
3187
3188 my $state;
3189 my $tpm = parse_drive("tpmstate0", $tpmdrive);
3190 my ($storeid, $volname) = PVE::Storage::parse_volume_id($tpm->{file}, 1);
3191 if ($storeid) {
3192 $state = PVE::Storage::map_volume($storecfg, $tpm->{file});
3193 } else {
3194 $state = $tpm->{file};
3195 }
3196
3197 my $paths = get_tpm_paths($vmid);
3198
3199 # during migration, we will get state from remote
3200 #
3201 if (!$migration) {
3202 # run swtpm_setup to create a new TPM state if it doesn't exist yet
3203 my $setup_cmd = [
3204 "swtpm_setup",
3205 "--tpmstate",
3206 "file://$state",
3207 "--createek",
3208 "--create-ek-cert",
3209 "--create-platform-cert",
3210 "--lock-nvram",
3211 "--config",
3212 "/etc/swtpm_setup.conf", # do not use XDG configs
3213 "--runas",
3214 "0", # force creation as root, error if not possible
3215 "--not-overwrite", # ignore existing state, do not modify
3216 ];
3217
3218 push @$setup_cmd, "--tpm2" if $tpm->{version} eq 'v2.0';
3219 # TPM 2.0 supports ECC crypto, use if possible
3220 push @$setup_cmd, "--ecc" if $tpm->{version} eq 'v2.0';
3221
3222 run_command($setup_cmd, outfunc => sub {
3223 print "swtpm_setup: $1\n";
3224 });
3225 }
3226
3227 my $emulator_cmd = [
3228 "swtpm",
3229 "socket",
3230 "--tpmstate",
3231 "backend-uri=file://$state,mode=0600",
3232 "--ctrl",
3233 "type=unixio,path=$paths->{socket},mode=0600",
3234 "--pid",
3235 "file=$paths->{pid}",
3236 "--terminate", # terminate on QEMU disconnect
3237 "--daemon",
3238 ];
3239 push @$emulator_cmd, "--tpm2" if $tpm->{version} eq 'v2.0';
3240 run_command($emulator_cmd, outfunc => sub { print $1; });
3241
6bbcd71f 3242 my $tries = 100; # swtpm may take a bit to start before daemonizing, wait up to 5s for pid
f85951dc 3243 while (! -e $paths->{pid}) {
90c41bac 3244 die "failed to start swtpm: pid file '$paths->{pid}' wasn't created.\n" if --$tries == 0;
6bbcd71f 3245 usleep(50_000);
f85951dc
SR
3246 }
3247
f9dde219
SR
3248 # return untainted PID of swtpm daemon so it can be killed on error
3249 file_read_firstline($paths->{pid}) =~ m/(\d+)/;
3250 return $1;
3251}
3252
86b8228b
DM
3253sub vga_conf_has_spice {
3254 my ($vga) = @_;
3255
55655ebc
DC
3256 my $vgaconf = parse_vga($vga);
3257 my $vgatype = $vgaconf->{type};
3258 return 0 if !$vgatype || $vgatype !~ m/^qxl([234])?$/;
590e698c
DM
3259
3260 return $1 || 1;
86b8228b
DM
3261}
3262
d731ecbe
WB
3263sub is_native($) {
3264 my ($arch) = @_;
3265 return get_host_arch() eq $arch;
3266}
3267
045749f2
TL
3268sub get_vm_arch {
3269 my ($conf) = @_;
3270 return $conf->{arch} // get_host_arch();
3271}
3272
d731ecbe
WB
3273my $default_machines = {
3274 x86_64 => 'pc',
3275 aarch64 => 'virt',
3276};
3277
0761e619
TL
3278sub get_installed_machine_version {
3279 my ($kvmversion) = @_;
3280 $kvmversion = kvm_user_version() if !defined($kvmversion);
3281 $kvmversion =~ m/^(\d+\.\d+)/;
3282 return $1;
3283}
3284
3285sub windows_get_pinned_machine_version {
3286 my ($machine, $base_version, $kvmversion) = @_;
3287
3288 my $pin_version = $base_version;
3289 if (!defined($base_version) ||
3290 !PVE::QemuServer::Machine::can_run_pve_machine_version($base_version, $kvmversion)
3291 ) {
3292 $pin_version = get_installed_machine_version($kvmversion);
3293 }
3294 if (!$machine || $machine eq 'pc') {
3295 $machine = "pc-i440fx-$pin_version";
3296 } elsif ($machine eq 'q35') {
3297 $machine = "pc-q35-$pin_version";
3298 } elsif ($machine eq 'virt') {
3299 $machine = "virt-$pin_version";
3300 } else {
3301 warn "unknown machine type '$machine', not touching that!\n";
3302 }
3303
3304 return $machine;
3305}
3306
045749f2 3307sub get_vm_machine {
ac0077cc 3308 my ($conf, $forcemachine, $arch, $add_pve_version, $kvmversion) = @_;
045749f2
TL
3309
3310 my $machine = $forcemachine || $conf->{machine};
d731ecbe 3311
9471e48b 3312 if (!$machine || $machine =~ m/^(?:pc|q35|virt)$/) {
4dd1e83c
TL
3313 $kvmversion //= kvm_user_version();
3314 # we must pin Windows VMs without a specific version to 5.1, as 5.2 fixed a bug in ACPI
3315 # layout which confuses windows quite a bit and may result in various regressions..
3316 # see: https://lists.gnu.org/archive/html/qemu-devel/2021-02/msg08484.html
3317 if (windows_version($conf->{ostype})) {
0761e619 3318 $machine = windows_get_pinned_machine_version($machine, '5.1', $kvmversion);
4dd1e83c 3319 }
045749f2
TL
3320 $arch //= 'x86_64';
3321 $machine ||= $default_machines->{$arch};
ac0077cc 3322 if ($add_pve_version) {
ac0077cc
SR
3323 my $pvever = PVE::QemuServer::Machine::get_pve_version($kvmversion);
3324 $machine .= "+pve$pvever";
3325 }
3326 }
3327
d4be7f31
SR
3328 if ($add_pve_version && $machine !~ m/\+pve\d+?(?:\.pxe)?$/) {
3329 my $is_pxe = $machine =~ m/^(.*?)\.pxe$/;
3330 $machine = $1 if $is_pxe;
3331
ac0077cc
SR
3332 # for version-pinned machines that do not include a pve-version (e.g.
3333 # pc-q35-4.1), we assume 0 to keep them stable in case we bump
3334 $machine .= '+pve0';
d4be7f31
SR
3335
3336 $machine .= '.pxe' if $is_pxe;
045749f2
TL
3337 }
3338
3339 return $machine;
d731ecbe
WB
3340}
3341
90b20b15
DC
3342sub get_ovmf_files($$$) {
3343 my ($arch, $efidisk, $smm) = @_;
96ed3574 3344
b5099b4f 3345 my $types = $OVMF->{$arch}
96ed3574
WB
3346 or die "no OVMF images known for architecture '$arch'\n";
3347
b5099b4f
SR
3348 my $type = 'default';
3349 if (defined($efidisk->{efitype}) && $efidisk->{efitype} eq '4m') {
90b20b15
DC
3350 $type = $smm ? "4m" : "4m-no-smm";
3351 $type .= '-ms' if $efidisk->{'pre-enrolled-keys'};
b5099b4f
SR
3352 }
3353
3354 return $types->{$type}->@*;
96ed3574
WB
3355}
3356
6908fd9b
WB
3357my $Arch2Qemu = {
3358 aarch64 => '/usr/bin/qemu-system-aarch64',
3359 x86_64 => '/usr/bin/qemu-system-x86_64',
3360};
3361sub get_command_for_arch($) {
3362 my ($arch) = @_;
3363 return '/usr/bin/kvm' if is_native($arch);
3364
3365 my $cmd = $Arch2Qemu->{$arch}
3366 or die "don't know how to emulate architecture '$arch'\n";
3367 return $cmd;
3368}
3369
05a4c550
SR
3370# To use query_supported_cpu_flags and query_understood_cpu_flags to get flags
3371# to use in a QEMU command line (-cpu element), first array_intersect the result
3372# of query_supported_ with query_understood_. This is necessary because:
3373#
3374# a) query_understood_ returns flags the host cannot use and
3375# b) query_supported_ (rather the QMP call) doesn't actually return CPU
3376# flags, but CPU settings - with most of them being flags. Those settings
3377# (and some flags, curiously) cannot be specified as a "-cpu" argument.
3378#
3379# query_supported_ needs to start up to 2 temporary VMs and is therefore rather
3380# expensive. If you need the value returned from this, you can get it much
3381# cheaper from pmxcfs using PVE::Cluster::get_node_kv('cpuflags-$accel') with
3382# $accel being 'kvm' or 'tcg'.
3383#
3384# pvestatd calls this function on startup and whenever the QEMU/KVM version
3385# changes, automatically populating pmxcfs.
3386#
3387# Returns: { kvm => [ flagX, flagY, ... ], tcg => [ flag1, flag2, ... ] }
3388# since kvm and tcg machines support different flags
3389#
3390sub query_supported_cpu_flags {
52cffab6 3391 my ($arch) = @_;
05a4c550 3392
52cffab6
SR
3393 $arch //= get_host_arch();
3394 my $default_machine = $default_machines->{$arch};
3395
3396 my $flags = {};
05a4c550
SR
3397
3398 # FIXME: Once this is merged, the code below should work for ARM as well:
3399 # https://lists.nongnu.org/archive/html/qemu-devel/2019-06/msg04947.html
3400 die "QEMU/KVM cannot detect CPU flags on ARM (aarch64)\n" if
3401 $arch eq "aarch64";
3402
3403 my $kvm_supported = defined(kvm_version());
3404 my $qemu_cmd = get_command_for_arch($arch);
3405 my $fakevmid = -1;
3406 my $pidfile = PVE::QemuServer::Helpers::pidfile_name($fakevmid);
3407
3408 # Start a temporary (frozen) VM with vmid -1 to allow sending a QMP command
3409 my $query_supported_run_qemu = sub {
3410 my ($kvm) = @_;
3411
3412 my $flags = {};
3413 my $cmd = [
3414 $qemu_cmd,
3415 '-machine', $default_machine,
3416 '-display', 'none',
378ad769 3417 '-chardev', "socket,id=qmp,path=/var/run/qemu-server/$fakevmid.qmp,server=on,wait=off",
05a4c550
SR
3418 '-mon', 'chardev=qmp,mode=control',
3419 '-pidfile', $pidfile,
3420 '-S', '-daemonize'
3421 ];
3422
3423 if (!$kvm) {
3424 push @$cmd, '-accel', 'tcg';
3425 }
3426
3427 my $rc = run_command($cmd, noerr => 1, quiet => 0);
3428 die "QEMU flag querying VM exited with code " . $rc if $rc;
3429
3430 eval {
3431 my $cmd_result = mon_cmd(
3432 $fakevmid,
3433 'query-cpu-model-expansion',
3434 type => 'full',
3435 model => { name => 'host' }
3436 );
3437
3438 my $props = $cmd_result->{model}->{props};
3439 foreach my $prop (keys %$props) {
3440 next if $props->{$prop} ne '1';
3441 # QEMU returns some flags multiple times, with '_', '.' or '-'
3442 # (e.g. lahf_lm and lahf-lm; sse4.2, sse4-2 and sse4_2; ...).
3443 # We only keep those with underscores, to match /proc/cpuinfo
3444 $prop =~ s/\.|-/_/g;
3445 $flags->{$prop} = 1;
3446 }
3447 };
3448 my $err = $@;
3449
6bbcd71f 3450 # force stop with 10 sec timeout and 'nocheck', always stop, even if QMP failed
05a4c550
SR
3451 vm_stop(undef, $fakevmid, 1, 1, 10, 0, 1);
3452
3453 die $err if $err;
3454
3455 return [ sort keys %$flags ];
3456 };
3457
3458 # We need to query QEMU twice, since KVM and TCG have different supported flags
3459 PVE::QemuConfig->lock_config($fakevmid, sub {
3460 $flags->{tcg} = eval { $query_supported_run_qemu->(0) };
3461 warn "warning: failed querying supported tcg flags: $@\n" if $@;
3462
3463 if ($kvm_supported) {
3464 $flags->{kvm} = eval { $query_supported_run_qemu->(1) };
3465 warn "warning: failed querying supported kvm flags: $@\n" if $@;
3466 }
3467 });
3468
3469 return $flags;
3470}
3471
3472# Understood CPU flags are written to a file at 'pve-qemu' compile time
3473my $understood_cpu_flag_dir = "/usr/share/kvm";
3474sub query_understood_cpu_flags {
3475 my $arch = get_host_arch();
3476 my $filepath = "$understood_cpu_flag_dir/recognized-CPUID-flags-$arch";
3477
3478 die "Cannot query understood QEMU CPU flags for architecture: $arch (file not found)\n"
3479 if ! -e $filepath;
3480
3481 my $raw = file_get_contents($filepath);
3482 $raw =~ s/^\s+|\s+$//g;
3483 my @flags = split(/\s+/, $raw);
3484
3485 return \@flags;
3486}
3487
e5a6919c
FE
3488# Since commit 277d33454f77ec1d1e0bc04e37621e4dd2424b67 in pve-qemu, smm is not off by default
3489# anymore. But smm=off seems to be required when using SeaBIOS and serial display.
3490my sub should_disable_smm {
3491 my ($conf, $vga) = @_;
3492
3493 return (!defined($conf->{bios}) || $conf->{bios} eq 'seabios') &&
bec87424 3494 $vga->{type} && $vga->{type} =~ m/^(serial\d+|none)$/;
e5a6919c
FE
3495}
3496
1e3baf05 3497sub config_to_command {
5921764c
SR
3498 my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
3499 $pbs_backing) = @_;
1e3baf05 3500
3326ae19 3501 my ($globalFlags, $machineFlags, $rtcFlags) = ([], [], []);
5bdcf937 3502 my $devices = [];
5bdcf937 3503 my $bridges = {};
b42d3cf9 3504 my $ostype = $conf->{ostype};
4317f69f 3505 my $winversion = windows_version($ostype);
d731ecbe 3506 my $kvm = $conf->{kvm};
38277afc 3507 my $nodename = nodename();
d731ecbe 3508
045749f2 3509 my $arch = get_vm_arch($conf);
1476b99f
DC
3510 my $kvm_binary = get_command_for_arch($arch);
3511 my $kvmver = kvm_user_version($kvm_binary);
045749f2 3512
a04dd5c4
SR
3513 if (!$kvmver || $kvmver !~ m/^(\d+)\.(\d+)/ || $1 < 3) {
3514 $kvmver //= "undefined";
3515 die "Detected old QEMU binary ('$kvmver', at least 3.0 is required)\n";
3516 }
3517
9471e48b
TL
3518 my $add_pve_version = min_version($kvmver, 4, 1);
3519
3520 my $machine_type = get_vm_machine($conf, $forcemachine, $arch, $add_pve_version);
4df98f2f 3521 my $machine_version = extract_version($machine_type, $kvmver);
d731ecbe 3522 $kvm //= 1 if is_native($arch);
4317f69f 3523
a77a53ae 3524 $machine_version =~ m/(\d+)\.(\d+)/;
ac0077cc 3525 my ($machine_major, $machine_minor) = ($1, $2);
ac0077cc 3526
b516c848
SR
3527 if ($kvmver =~ m/^\d+\.\d+\.(\d+)/ && $1 >= 90) {
3528 warn "warning: Installed QEMU version ($kvmver) is a release candidate, ignoring version checks\n";
3529 } elsif (!min_version($kvmver, $machine_major, $machine_minor)) {
4df98f2f
TL
3530 die "Installed QEMU version '$kvmver' is too old to run machine type '$machine_type',"
3531 ." please upgrade node '$nodename'\n"
b516c848 3532 } elsif (!PVE::QemuServer::Machine::can_run_pve_machine_version($machine_version, $kvmver)) {
ac0077cc 3533 my $max_pve_version = PVE::QemuServer::Machine::get_pve_version($machine_version);
4df98f2f
TL
3534 die "Installed qemu-server (max feature level for $machine_major.$machine_minor is"
3535 ." pve$max_pve_version) is too old to run machine type '$machine_type', please upgrade"
3536 ." node '$nodename'\n";
ac0077cc
SR
3537 }
3538
3539 # if a specific +pve version is required for a feature, use $version_guard
3540 # instead of min_version to allow machines to be run with the minimum
3541 # required version
3542 my $required_pve_version = 0;
3543 my $version_guard = sub {
3544 my ($major, $minor, $pve) = @_;
3545 return 0 if !min_version($machine_version, $major, $minor, $pve);
47f35977
SR
3546 my $max_pve = PVE::QemuServer::Machine::get_pve_version("$major.$minor");
3547 return 1 if min_version($machine_version, $major, $minor, $max_pve+1);
ac0077cc
SR
3548 $required_pve_version = $pve if $pve && $pve > $required_pve_version;
3549 return 1;
3550 };
a77a53ae 3551
4df98f2f
TL
3552 if ($kvm && !defined kvm_version()) {
3553 die "KVM virtualisation configured, but not available. Either disable in VM configuration"
3554 ." or enable in BIOS.\n";
d731ecbe 3555 }
bfcd9b7e 3556
3392d6ca 3557 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
4d3f29ed 3558 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
249c4a6c
AD
3559 my $use_old_bios_files = undef;
3560 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
db656e5f 3561
74fe3d9a 3562 my $cmd = [];
83870398 3563 if ($conf->{affinity}) {
74fe3d9a 3564 push @$cmd, '/usr/bin/taskset', '--cpu-list', '--all-tasks', $conf->{affinity};
83870398
DB
3565 }
3566
1476b99f 3567 push @$cmd, $kvm_binary;
1e3baf05
DM
3568
3569 push @$cmd, '-id', $vmid;
3570
e4d4cda1
HR
3571 my $vmname = $conf->{name} || "vm$vmid";
3572
6884a7d7 3573 push @$cmd, '-name', "$vmname,debug-threads=on";
e4d4cda1 3574
27b25d03
SR
3575 push @$cmd, '-no-shutdown';
3576
1e3baf05
DM
3577 my $use_virtio = 0;
3578
d036e418 3579 my $qmpsocket = PVE::QemuServer::Helpers::qmp_socket($vmid);
378ad769 3580 push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server=on,wait=off";
c971c4f2
AD
3581 push @$cmd, '-mon', "chardev=qmp,mode=control";
3582
2ea5fb7e 3583 if (min_version($machine_version, 2, 12)) {
b4496b9e 3584 push @$cmd, '-chardev', "socket,id=qmp-event,path=/var/run/qmeventd.sock,reconnect=5";
71bd73b5
DC
3585 push @$cmd, '-mon', "chardev=qmp-event,mode=control";
3586 }
1e3baf05 3587
d036e418 3588 push @$cmd, '-pidfile' , PVE::QemuServer::Helpers::pidfile_name($vmid);
19672434 3589
1e3baf05
DM
3590 push @$cmd, '-daemonize';
3591
2796e7d5 3592 if ($conf->{smbios1}) {
1f30ac3a
CE
3593 my $smbios_conf = parse_smbios1($conf->{smbios1});
3594 if ($smbios_conf->{base64}) {
3595 # Do not pass base64 flag to qemu
3596 delete $smbios_conf->{base64};
3597 my $smbios_string = "";
3598 foreach my $key (keys %$smbios_conf) {
3599 my $value;
3600 if ($key eq "uuid") {
3601 $value = $smbios_conf->{uuid}
3602 } else {
3603 $value = decode_base64($smbios_conf->{$key});
3604 }
3605 # qemu accepts any binary data, only commas need escaping by double comma
3606 $value =~ s/,/,,/g;
3607 $smbios_string .= "," . $key . "=" . $value if $value;
3608 }
3609 push @$cmd, '-smbios', "type=1" . $smbios_string;
3610 } else {
3611 push @$cmd, '-smbios', "type=1,$conf->{smbios1}";
3612 }
2796e7d5
DM
3613 }
3614
3edb45e7 3615 if ($conf->{bios} && $conf->{bios} eq 'ovmf') {
b5099b4f
SR
3616 my $d;
3617 if (my $efidisk = $conf->{efidisk0}) {
3618 $d = parse_drive('efidisk0', $efidisk);
3619 }
3620
90b20b15 3621 my ($ovmf_code, $ovmf_vars) = get_ovmf_files($arch, $d, $q35);
818c3b8d 3622 die "uefi base image '$ovmf_code' not found\n" if ! -f $ovmf_code;
2ddc0a5c 3623
818c3b8d 3624 my ($path, $format);
b4dc6475 3625 my $read_only_str = '';
b5099b4f 3626 if ($d) {
2ddc0a5c 3627 my ($storeid, $volname) = PVE::Storage::parse_volume_id($d->{file}, 1);
13bca7b4 3628 $format = $d->{format};
2ddc0a5c
DC
3629 if ($storeid) {
3630 $path = PVE::Storage::path($storecfg, $d->{file});
13bca7b4
WB
3631 if (!defined($format)) {
3632 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
3633 $format = qemu_img_format($scfg, $volname);
3634 }
2ddc0a5c
DC
3635 } else {
3636 $path = $d->{file};
13bca7b4
WB
3637 die "efidisk format must be specified\n"
3638 if !defined($format);
2ddc0a5c 3639 }
b4dc6475
FG
3640
3641 $read_only_str = ',readonly=on' if drive_is_read_only($conf, $d);
2ddc0a5c 3642 } else {
34e82fa2 3643 log_warn("no efidisk configured! Using temporary efivars disk.");
4dcce9ee 3644 $path = "/tmp/$vmid-ovmf.fd";
96ed3574 3645 PVE::Tools::file_copy($ovmf_vars, $path, -s $ovmf_vars);
13bca7b4 3646 $format = 'raw';
2ddc0a5c 3647 }
4dcce9ee 3648
818ce80e
DC
3649 my $size_str = "";
3650
3651 if ($format eq 'raw' && $version_guard->(4, 1, 2)) {
3652 $size_str = ",size=" . (-s $ovmf_vars);
3653 }
3654
738dc81c
TL
3655 # SPI flash does lots of read-modify-write OPs, without writeback this gets really slow #3329
3656 my $cache = "";
3657 if ($path =~ m/^rbd:/) {
3658 $cache = ',cache=writeback';
3659 $path .= ':rbd_cache_policy=writeback'; # avoid write-around, we *need* to cache writes too
3660 }
6aaad230 3661
378ad769 3662 push @$cmd, '-drive', "if=pflash,unit=0,format=raw,readonly=on,file=$ovmf_code";
b4dc6475 3663 push @$cmd, '-drive', "if=pflash,unit=1$cache,format=$format,id=drive-efidisk0$size_str,file=${path}${read_only_str}";
a783c78e
AD
3664 }
3665
483ceeab 3666 if ($q35) { # tell QEMU to load q35 config early
7583d156 3667 # we use different pcie-port hardware for qemu >= 4.0 for passthrough
2ea5fb7e 3668 if (min_version($machine_version, 4, 0)) {
7583d156
DC
3669 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35-4.0.cfg';
3670 } else {
3671 push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg';
3672 }
3673 }
da8b4189 3674
cc181036
TL
3675 if (defined(my $fixups = qemu_created_version_fixups($conf, $forcemachine, $kvmver))) {
3676 push @$cmd, $fixups->@*;
3677 }
3678
844d8fa6
DC
3679 if ($conf->{vmgenid}) {
3680 push @$devices, '-device', 'vmgenid,guid='.$conf->{vmgenid};
3681 }
3682
d40e5e18 3683 # add usb controllers
4df98f2f 3684 my @usbcontrollers = PVE::QemuServer::USB::get_usb_controllers(
4862922a 3685 $conf, $bridges, $arch, $machine_type, $usbdesc->{format}, $MAX_USB_DEVICES, $machine_version);
d40e5e18 3686 push @$devices, @usbcontrollers if @usbcontrollers;
55655ebc 3687 my $vga = parse_vga($conf->{vga});
2fa3151e 3688
55655ebc
DC
3689 my $qxlnum = vga_conf_has_spice($conf->{vga});
3690 $vga->{type} = 'qxl' if $qxlnum;
2fa3151e 3691
55655ebc 3692 if (!$vga->{type}) {
869ad4a7
WB
3693 if ($arch eq 'aarch64') {
3694 $vga->{type} = 'virtio';
2ea5fb7e 3695 } elsif (min_version($machine_version, 2, 9)) {
55655ebc 3696 $vga->{type} = (!$winversion || $winversion >= 6) ? 'std' : 'cirrus';
a2a5cd64 3697 } else {
55655ebc 3698 $vga->{type} = ($winversion >= 6) ? 'std' : 'cirrus';
a2a5cd64 3699 }
5acbfe9e
DM
3700 }
3701
1e3baf05 3702 # enable absolute mouse coordinates (needed by vnc)
fa3b3ce0
TL
3703 my $tablet = $conf->{tablet};
3704 if (!defined($tablet)) {
5acbfe9e 3705 $tablet = $defaults->{tablet};
590e698c 3706 $tablet = 0 if $qxlnum; # disable for spice because it is not needed
55655ebc 3707 $tablet = 0 if $vga->{type} =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card)
5acbfe9e
DM
3708 }
3709
d559309f
WB
3710 if ($tablet) {
3711 push @$devices, '-device', print_tabletdevice_full($conf, $arch) if $tablet;
3712 my $kbd = print_keyboarddevice_full($conf, $arch);
3713 push @$devices, '-device', $kbd if defined($kbd);
3714 }
b467f79a 3715
e5d611c3 3716 my $bootorder = device_bootorder($conf);
2141a802 3717
74c17b7a 3718 # host pci device passthrough
13d68979 3719 my ($kvm_off, $gpu_passthrough, $legacy_igd) = PVE::QemuServer::PCI::print_hostpci_devices(
41af2dfc 3720 $vmid, $conf, $devices, $vga, $winversion, $q35, $bridges, $arch, $machine_type, $bootorder);
1e3baf05
DM
3721
3722 # usb devices
ae36393d 3723 my $usb_dev_features = {};
2ea5fb7e 3724 $usb_dev_features->{spice_usb3} = 1 if min_version($machine_version, 4, 0);
ae36393d 3725
4df98f2f 3726 my @usbdevices = PVE::QemuServer::USB::get_usb_devices(
4862922a 3727 $conf, $usbdesc->{format}, $MAX_USB_DEVICES, $usb_dev_features, $bootorder, $machine_version);
d40e5e18 3728 push @$devices, @usbdevices if @usbdevices;
2141a802 3729
1e3baf05 3730 # serial devices
bae179aa 3731 for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) {
fa3b3ce0
TL
3732 my $path = $conf->{"serial$i"} or next;
3733 if ($path eq 'socket') {
3734 my $socket = "/var/run/qemu-server/${vmid}.serial$i";
3735 push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server=on,wait=off";
3736 # On aarch64, serial0 is the UART device. Qemu only allows
3737 # connecting UART devices via the '-serial' command line, as
3738 # the device has a fixed slot on the hardware...
3739 if ($arch eq 'aarch64' && $i == 0) {
3740 push @$devices, '-serial', "chardev:serial$i";
9f9d2fb2 3741 } else {
9f9d2fb2
DM
3742 push @$devices, '-device', "isa-serial,chardev=serial$i";
3743 }
fa3b3ce0
TL
3744 } else {
3745 die "no such serial device\n" if ! -c $path;
3746 push @$devices, '-chardev', "tty,id=serial$i,path=$path";
3747 push @$devices, '-device', "isa-serial,chardev=serial$i";
34978be3 3748 }
1e3baf05
DM
3749 }
3750
3751 # parallel devices
1989a89c 3752 for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) {
34978be3 3753 if (my $path = $conf->{"parallel$i"}) {
19672434 3754 die "no such parallel device\n" if ! -c $path;
32e69805 3755 my $devtype = $path =~ m!^/dev/usb/lp! ? 'tty' : 'parport';
4c5dbaf6 3756 push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path";
5bdcf937 3757 push @$devices, '-device', "isa-parallel,chardev=parallel$i";
34978be3 3758 }
1e3baf05
DM
3759 }
3760
b01de199 3761 if (min_version($machine_version, 4, 0) && (my $audio = conf_has_audio($conf))) {
2e7b5925 3762 my $audiopciaddr = print_pci_addr("audio0", $bridges, $arch, $machine_type);
1cc5ed1b 3763 my $audio_devs = audio_devs($audio, $audiopciaddr, $machine_version);
b01de199 3764 push @$devices, @$audio_devs;
2e7b5925 3765 }
19672434 3766
f9dde219
SR
3767 add_tpm_device($vmid, $devices, $conf);
3768
1e3baf05
DM
3769 my $sockets = 1;
3770 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
3771 $sockets = $conf->{sockets} if $conf->{sockets};
3772
3773 my $cores = $conf->{cores} || 1;
3bd18e48 3774
de9d1e55 3775 my $maxcpus = $sockets * $cores;
76267728 3776
de9d1e55 3777 my $vcpus = $conf->{vcpus} ? $conf->{vcpus} : $maxcpus;
76267728 3778
de9d1e55
AD
3779 my $allowed_vcpus = $cpuinfo->{cpus};
3780
483ceeab 3781 die "MAX $allowed_vcpus vcpus allowed per VM on this node\n" if ($allowed_vcpus < $maxcpus);
1e3baf05 3782
483ceeab 3783 if ($hotplug_features->{cpu} && min_version($machine_version, 2, 7)) {
69c81430
AD
3784 push @$cmd, '-smp', "1,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3785 for (my $i = 2; $i <= $vcpus; $i++) {
3786 my $cpustr = print_cpu_device($conf,$i);
3787 push @$cmd, '-device', $cpustr;
3788 }
3789
3790 } else {
3791
3792 push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus";
3793 }
1e3baf05
DM
3794 push @$cmd, '-nodefaults';
3795
dbea4415 3796 push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000,splash=/usr/share/qemu-server/bootsplash.jpg";
1e3baf05 3797
6b64503e 3798 push @$cmd, '-no-acpi' if defined($conf->{acpi}) && $conf->{acpi} == 0;
1e3baf05 3799
6b64503e 3800 push @$cmd, '-no-reboot' if defined($conf->{reboot}) && $conf->{reboot} == 0;
1e3baf05 3801
84902837 3802 if ($vga->{type} && $vga->{type} !~ m/^serial\d+$/ && $vga->{type} ne 'none'){
4df98f2f
TL
3803 push @$devices, '-device', print_vga_device(
3804 $conf, $vga, $arch, $machine_version, $machine_type, undef, $qxlnum, $bridges);
6f070e39
TL
3805
3806 push @$cmd, '-display', 'egl-headless,gl=core' if $vga->{type} eq 'virtio-gl'; # VIRGL
3807
d036e418 3808 my $socket = PVE::QemuServer::Helpers::vnc_socket($vmid);
378ad769 3809 push @$cmd, '-vnc', "unix:$socket,password=on";
b7be4ba9 3810 } else {
55655ebc 3811 push @$cmd, '-vga', 'none' if $vga->{type} eq 'none';
b7be4ba9
AD
3812 push @$cmd, '-nographic';
3813 }
3814
1e3baf05 3815 # time drift fix
6b64503e 3816 my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf};
8c559505 3817 my $useLocaltime = $conf->{localtime};
1e3baf05 3818
4317f69f
AD
3819 if ($winversion >= 5) { # windows
3820 $useLocaltime = 1 if !defined($conf->{localtime});
7a131888 3821
4317f69f
AD
3822 # use time drift fix when acpi is enabled
3823 if (!(defined($conf->{acpi}) && $conf->{acpi} == 0)) {
3824 $tdf = 1 if !defined($conf->{tdf});
462e8d19 3825 }
4317f69f 3826 }
462e8d19 3827
4317f69f
AD
3828 if ($winversion >= 6) {
3829 push @$globalFlags, 'kvm-pit.lost_tick_policy=discard';
3830 push @$cmd, '-no-hpet';
1e3baf05
DM
3831 }
3832
8c559505
DM
3833 push @$rtcFlags, 'driftfix=slew' if $tdf;
3834
2f6f002c 3835 if ($conf->{startdate} && $conf->{startdate} ne 'now') {
8c559505
DM
3836 push @$rtcFlags, "base=$conf->{startdate}";
3837 } elsif ($useLocaltime) {
3838 push @$rtcFlags, 'base=localtime';
3839 }
1e3baf05 3840
58c64ad5
SR
3841 if ($forcecpu) {
3842 push @$cmd, '-cpu', $forcecpu;
3843 } else {
2f6f002c 3844 push @$cmd, get_cpu_options($conf, $arch, $kvm, $kvm_off, $machine_version, $winversion, $gpu_passthrough);
58c64ad5 3845 }
519ed28c 3846
0567a4d5 3847 PVE::QemuServer::Memory::config($conf, $vmid, $sockets, $cores, $defaults, $hotplug_features, $cmd);
370b05e7 3848
1e3baf05
DM
3849 push @$cmd, '-S' if $conf->{freeze};
3850
b20df606 3851 push @$cmd, '-k', $conf->{keyboard} if defined($conf->{keyboard});
1e3baf05 3852
48657158
MD
3853 my $guest_agent = parse_guest_agent($conf);
3854
3855 if ($guest_agent->{enabled}) {
d036e418 3856 my $qgasocket = PVE::QemuServer::Helpers::qmp_socket($vmid, 1);
378ad769 3857 push @$devices, '-chardev', "socket,path=$qgasocket,server=on,wait=off,id=qga0";
48657158 3858
60f03a11 3859 if (!$guest_agent->{type} || $guest_agent->{type} eq 'virtio') {
48657158
MD
3860 my $pciaddr = print_pci_addr("qga0", $bridges, $arch, $machine_type);
3861 push @$devices, '-device', "virtio-serial,id=qga0$pciaddr";
3862 push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0';
3863 } elsif ($guest_agent->{type} eq 'isa') {
3864 push @$devices, '-device', "isa-serial,chardev=qga0";
3865 }
ab6a046f
AD
3866 }
3867
e5d611c3
TL
3868 my $rng = $conf->{rng0} ? parse_rng($conf->{rng0}) : undef;
3869 if ($rng && $version_guard->(4, 1, 2)) {
05853188
SR
3870 check_rng_source($rng->{source});
3871
2cf61f33
SR
3872 my $max_bytes = $rng->{max_bytes} // $rng_fmt->{max_bytes}->{default};
3873 my $period = $rng->{period} // $rng_fmt->{period}->{default};
2cf61f33
SR
3874 my $limiter_str = "";
3875 if ($max_bytes) {
3876 $limiter_str = ",max-bytes=$max_bytes,period=$period";
3877 }
3878
2cf61f33 3879 my $rng_addr = print_pci_addr("rng0", $bridges, $arch, $machine_type);
2cf61f33
SR
3880 push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
3881 push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
3882 }
3883
1d794448 3884 my $spice_port;
2fa3151e 3885
f8ea1b30 3886 if ($qxlnum || $vga->{type} =~ /^virtio/) {
590e698c 3887 if ($qxlnum > 1) {
ac087616 3888 if ($winversion){
2f6f002c 3889 for (my $i = 1; $i < $qxlnum; $i++){
4df98f2f
TL
3890 push @$devices, '-device', print_vga_device(
3891 $conf, $vga, $arch, $machine_version, $machine_type, $i, $qxlnum, $bridges);
590e698c
DM
3892 }
3893 } else {
3894 # assume other OS works like Linux
55655ebc
DC
3895 my ($ram, $vram) = ("134217728", "67108864");
3896 if ($vga->{memory}) {
3897 $ram = PVE::Tools::convert_size($qxlnum*4*$vga->{memory}, 'mb' => 'b');
3898 $vram = PVE::Tools::convert_size($qxlnum*2*$vga->{memory}, 'mb' => 'b');
3899 }
3900 push @$cmd, '-global', "qxl-vga.ram_size=$ram";
3901 push @$cmd, '-global', "qxl-vga.vram_size=$vram";
2fa3151e
AD
3902 }
3903 }
3904
d559309f 3905 my $pciaddr = print_pci_addr("spice", $bridges, $arch, $machine_type);
95a4b4a9 3906
af0eba7e 3907 my $pfamily = PVE::Tools::get_host_address_family($nodename);
91152441
WB
3908 my @nodeaddrs = PVE::Tools::getaddrinfo_all('localhost', family => $pfamily);
3909 die "failed to get an ip address of type $pfamily for 'localhost'\n" if !@nodeaddrs;
4d316a63
AL
3910
3911 push @$devices, '-device', "virtio-serial,id=spice$pciaddr";
3912 push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent";
3913 push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0";
3914
91152441
WB
3915 my $localhost = PVE::Network::addr_to_ip($nodeaddrs[0]->{addr});
3916 $spice_port = PVE::Tools::next_spice_port($pfamily, $localhost);
943340a6 3917
4df98f2f
TL
3918 my $spice_enhancement_str = $conf->{spice_enhancements} // '';
3919 my $spice_enhancement = parse_property_string($spice_enhancements_fmt, $spice_enhancement_str);
caab114a
TL
3920 if ($spice_enhancement->{foldersharing}) {
3921 push @$devices, '-chardev', "spiceport,id=foldershare,name=org.spice-space.webdav.0";
3922 push @$devices, '-device', "virtserialport,chardev=foldershare,name=org.spice-space.webdav.0";
3923 }
c4df18db 3924
caab114a 3925 my $spice_opts = "tls-port=${spice_port},addr=$localhost,tls-ciphers=HIGH,seamless-migration=on";
4df98f2f
TL
3926 $spice_opts .= ",streaming-video=$spice_enhancement->{videostreaming}"
3927 if $spice_enhancement->{videostreaming};
3928
caab114a 3929 push @$devices, '-spice', "$spice_opts";
1011b570
DM
3930 }
3931
8d9ae0d2
DM
3932 # enable balloon by default, unless explicitly disabled
3933 if (!defined($conf->{balloon}) || $conf->{balloon}) {
3326ae19 3934 my $pciaddr = print_pci_addr("balloon0", $bridges, $arch, $machine_type);
c70e4ec3
AD
3935 my $ballooncmd = "virtio-balloon-pci,id=balloon0$pciaddr";
3936 $ballooncmd .= ",free-page-reporting=on" if min_version($machine_version, 6, 2);
3937 push @$devices, '-device', $ballooncmd;
8d9ae0d2 3938 }
1e3baf05 3939
0ea9541d
DM
3940 if ($conf->{watchdog}) {
3941 my $wdopts = parse_watchdog($conf->{watchdog});
3326ae19 3942 my $pciaddr = print_pci_addr("watchdog", $bridges, $arch, $machine_type);
0a40e8ea 3943 my $watchdog = $wdopts->{model} || 'i6300esb';
5bdcf937
AD
3944 push @$devices, '-device', "$watchdog$pciaddr";
3945 push @$devices, '-watchdog-action', $wdopts->{action} if $wdopts->{action};
0ea9541d
DM
3946 }
3947
1e3baf05 3948 my $vollist = [];
941e0c42 3949 my $scsicontroller = {};
26ee04b6 3950 my $ahcicontroller = {};
cdd20088 3951 my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : $defaults->{scsihw};
1e3baf05 3952
5881b913
DM
3953 # Add iscsi initiator name if available
3954 if (my $initiator = get_initiator_name()) {
3955 push @$devices, '-iscsi', "initiator-name=$initiator";
3956 }
3957
912792e2 3958 PVE::QemuConfig->foreach_volume($conf, sub {
1e3baf05
DM
3959 my ($ds, $drive) = @_;
3960
ff1a2432 3961 if (PVE::Storage::parse_volume_id($drive->{file}, 1)) {
3f11f0d7 3962 check_volume_storage_type($storecfg, $drive->{file});
1e3baf05 3963 push @$vollist, $drive->{file};
ff1a2432 3964 }
afdb31d5 3965
4dcce9ee
TL
3966 # ignore efidisk here, already added in bios/fw handling code above
3967 return if $drive->{interface} eq 'efidisk';
f9dde219
SR
3968 # similar for TPM
3969 return if $drive->{interface} eq 'tpmstate';
4dcce9ee 3970
1e3baf05 3971 $use_virtio = 1 if $ds =~ m/^virtio/;
3b408e82 3972
2141a802 3973 $drive->{bootindex} = $bootorder->{$ds} if $bootorder->{$ds};
3b408e82 3974
2f6f002c 3975 if ($drive->{interface} eq 'virtio'){
51f492cd
AD
3976 push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread};
3977 }
3978
2f6f002c 3979 if ($drive->{interface} eq 'scsi') {
cdd20088 3980
ee034f5c 3981 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive);
6731a4cf 3982
b8fb1c03
SR
3983 die "scsi$drive->{index}: machine version 4.1~pve2 or higher is required to use more than 14 SCSI disks\n"
3984 if $drive->{index} > 13 && !&$version_guard(4, 1, 2);
3985
3326ae19 3986 my $pciaddr = print_pci_addr("$controller_prefix$controller", $bridges, $arch, $machine_type);
a1b7d579 3987 my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ? "virtio-scsi-pci" : $scsihw;
fc8b40fd
AD
3988
3989 my $iothread = '';
3990 if($conf->{scsihw} && $conf->{scsihw} eq "virtio-scsi-single" && $drive->{iothread}){
3991 $iothread .= ",iothread=iothread-$controller_prefix$controller";
3992 push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller";
e7a5104d 3993 } elsif ($drive->{iothread}) {
d80ad18c
MH
3994 log_warn(
3995 "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n"
3996 );
fc8b40fd
AD
3997 }
3998
6e11f143
AD
3999 my $queues = '';
4000 if($conf->{scsihw} && $conf->{scsihw} eq "virtio-scsi-single" && $drive->{queues}){
4001 $queues = ",num_queues=$drive->{queues}";
370b05e7 4002 }
6e11f143 4003
4df98f2f
TL
4004 push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues"
4005 if !$scsicontroller->{$controller};
cdd20088 4006 $scsicontroller->{$controller}=1;
2f6f002c 4007 }
3b408e82 4008
26ee04b6 4009 if ($drive->{interface} eq 'sata') {
2f6f002c 4010 my $controller = int($drive->{index} / $PVE::QemuServer::Drive::MAX_SATA_DISKS);
3326ae19 4011 my $pciaddr = print_pci_addr("ahci$controller", $bridges, $arch, $machine_type);
4df98f2f
TL
4012 push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr"
4013 if !$ahcicontroller->{$controller};
2f6f002c 4014 $ahcicontroller->{$controller}=1;
26ee04b6 4015 }
46f58b5f 4016
5921764c
SR
4017 my $pbs_conf = $pbs_backing->{$ds};
4018 my $pbs_name = undef;
4019 if ($pbs_conf) {
4020 $pbs_name = "drive-$ds-pbs";
4021 push @$devices, '-blockdev', print_pbs_blockdev($pbs_conf, $pbs_name);
4022 }
4023
6d5673c3
SR
4024 my $drive_cmd = print_drive_commandline_full(
4025 $storecfg, $vmid, $drive, $pbs_name, min_version($kvmver, 6, 0));
3dc33a72
FG
4026
4027 # extra protection for templates, but SATA and IDE don't support it..
75748d44 4028 $drive_cmd .= ',readonly=on' if drive_is_read_only($conf, $drive);
4ef13a7f 4029
15b21acc 4030 push @$devices, '-drive',$drive_cmd;
4df98f2f
TL
4031 push @$devices, '-device', print_drivedevice_full(
4032 $storecfg, $conf, $vmid, $drive, $bridges, $arch, $machine_type);
1e3baf05
DM
4033 });
4034
cc4d6182 4035 for (my $i = 0; $i < $MAX_NETS; $i++) {
2141a802
SR
4036 my $netname = "net$i";
4037
4038 next if !$conf->{$netname};
4039 my $d = parse_net($conf->{$netname});
d0a86b24 4040 next if !$d;
4ddd2ca2 4041 # save the MAC addr here (could be auto-gen. in some odd setups) for FDB registering later?
1e3baf05 4042
d0a86b24 4043 $use_virtio = 1 if $d->{model} eq 'virtio';
1e3baf05 4044
2141a802 4045 $d->{bootindex} = $bootorder->{$netname} if $bootorder->{$netname};
1e3baf05 4046
2141a802 4047 my $netdevfull = print_netdev_full($vmid, $conf, $arch, $d, $netname);
d0a86b24 4048 push @$devices, '-netdev', $netdevfull;
5bdcf937 4049
d0a86b24 4050 my $netdevicefull = print_netdevice_full(
0c03a390 4051 $vmid, $conf, $d, $netname, $bridges, $use_old_bios_files, $arch, $machine_type, $machine_version);
4df98f2f 4052
d0a86b24 4053 push @$devices, '-device', $netdevicefull;
5bdcf937 4054 }
1e3baf05 4055
6dbcb073 4056 if ($conf->{ivshmem}) {
4df98f2f 4057 my $ivshmem = parse_property_string($ivshmem_fmt, $conf->{ivshmem});
e3c27a6a 4058
6dbcb073
DC
4059 my $bus;
4060 if ($q35) {
4061 $bus = print_pcie_addr("ivshmem");
4062 } else {
4063 $bus = print_pci_addr("ivshmem", $bridges, $arch, $machine_type);
4064 }
e3c27a6a
TL
4065
4066 my $ivshmem_name = $ivshmem->{name} // $vmid;
4067 my $path = '/dev/shm/pve-shm-' . $ivshmem_name;
4068
6dbcb073 4069 push @$devices, '-device', "ivshmem-plain,memdev=ivshmem$bus,";
4df98f2f
TL
4070 push @$devices, '-object', "memory-backend-file,id=ivshmem,share=on,mem-path=$path"
4071 .",size=$ivshmem->{size}M";
6dbcb073
DC
4072 }
4073
2513b862
DC
4074 # pci.4 is nested in pci.1
4075 $bridges->{1} = 1 if $bridges->{4};
4076
3326ae19
TL
4077 if (!$q35) { # add pci bridges
4078 if (min_version($machine_version, 2, 3)) {
fc79e813
AD
4079 $bridges->{1} = 1;
4080 $bridges->{2} = 1;
4081 }
6731a4cf 4082 $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/;
2513b862
DC
4083 }
4084
4085 for my $k (sort {$b cmp $a} keys %$bridges) {
4086 next if $q35 && $k < 4; # q35.cfg already includes bridges up to 3
13d68979
SR
4087
4088 my $k_name = $k;
4089 if ($k == 2 && $legacy_igd) {
4090 $k_name = "$k-igd";
4091 }
3326ae19 4092 my $pciaddr = print_pci_addr("pci.$k_name", undef, $arch, $machine_type);
2513b862 4093 my $devstr = "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr";
3326ae19
TL
4094
4095 if ($q35) { # add after -readconfig pve-q35.cfg
2513b862
DC
4096 splice @$devices, 2, 0, '-device', $devstr;
4097 } else {
4098 unshift @$devices, '-device', $devstr if $k > 0;
f8e83f05 4099 }
19672434
DM
4100 }
4101
ac0077cc
SR
4102 if (!$kvm) {
4103 push @$machineFlags, 'accel=tcg';
4104 }
4105
e5a6919c
FE
4106 push @$machineFlags, 'smm=off' if should_disable_smm($conf, $vga);
4107
ac0077cc
SR
4108 my $machine_type_min = $machine_type;
4109 if ($add_pve_version) {
4110 $machine_type_min =~ s/\+pve\d+$//;
4111 $machine_type_min .= "+pve$required_pve_version";
4112 }
4113 push @$machineFlags, "type=${machine_type_min}";
4114
5bdcf937 4115 push @$cmd, @$devices;
2f6f002c
TL
4116 push @$cmd, '-rtc', join(',', @$rtcFlags) if scalar(@$rtcFlags);
4117 push @$cmd, '-machine', join(',', @$machineFlags) if scalar(@$machineFlags);
4118 push @$cmd, '-global', join(',', @$globalFlags) if scalar(@$globalFlags);
8c559505 4119
7ceade4c
DC
4120 if (my $vmstate = $conf->{vmstate}) {
4121 my $statepath = PVE::Storage::path($storecfg, $vmstate);
24d1f93a 4122 push @$vollist, $vmstate;
7ceade4c 4123 push @$cmd, '-loadstate', $statepath;
b85666cf 4124 print "activating and using '$vmstate' as vmstate\n";
7ceade4c
DC
4125 }
4126
85fcf79e
FG
4127 if (PVE::QemuConfig->is_template($conf)) {
4128 # needed to workaround base volumes being read-only
4129 push @$cmd, '-snapshot';
4130 }
4131
76350670
DC
4132 # add custom args
4133 if ($conf->{args}) {
4134 my $aa = PVE::Tools::split_args($conf->{args});
4135 push @$cmd, @$aa;
4136 }
4137
1d794448 4138 return wantarray ? ($cmd, $vollist, $spice_port) : $cmd;
1e3baf05 4139}
19672434 4140
05853188
SR
4141sub check_rng_source {
4142 my ($source) = @_;
4143
4144 # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
4145 die "cannot create VirtIO RNG device: source file '$source' doesn't exist\n"
4146 if ! -e $source;
4147
4148 my $rng_current = '/sys/devices/virtual/misc/hw_random/rng_current';
4149 if ($source eq '/dev/hwrng' && file_read_firstline($rng_current) eq 'none') {
4df98f2f
TL
4150 # Needs to abort, otherwise QEMU crashes on first rng access. Note that rng_current cannot
4151 # be changed to 'none' manually, so once the VM is past this point, it's no longer an issue.
4152 die "Cannot start VM with passed-through RNG device: '/dev/hwrng' exists, but"
4153 ." '$rng_current' is set to 'none'. Ensure that a compatible hardware-RNG is attached"
4154 ." to the host.\n";
05853188
SR
4155 }
4156}
4157
943340a6 4158sub spice_port {
1011b570 4159 my ($vmid) = @_;
943340a6 4160
0a13e08e 4161 my $res = mon_cmd($vmid, 'query-spice');
943340a6
DM
4162
4163 return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n";
1011b570
DM
4164}
4165
86fdcfb2
DA
4166sub vm_devices_list {
4167 my ($vmid) = @_;
4168
0a13e08e 4169 my $res = mon_cmd($vmid, 'query-pci');
f721624b 4170 my $devices_to_check = [];
ceea9078
DM
4171 my $devices = {};
4172 foreach my $pcibus (@$res) {
f721624b
DC
4173 push @$devices_to_check, @{$pcibus->{devices}},
4174 }
4175
4176 while (@$devices_to_check) {
4177 my $to_check = [];
4178 for my $d (@$devices_to_check) {
4179 $devices->{$d->{'qdev_id'}} = 1 if $d->{'qdev_id'};
4180 next if !$d->{'pci_bridge'};
4181
4182 $devices->{$d->{'qdev_id'}} += scalar(@{$d->{'pci_bridge'}->{devices}});
4183 push @$to_check, @{$d->{'pci_bridge'}->{devices}};
f78cc802 4184 }
f721624b 4185 $devices_to_check = $to_check;
f78cc802
AD
4186 }
4187
0a13e08e 4188 my $resblock = mon_cmd($vmid, 'query-block');
f78cc802
AD
4189 foreach my $block (@$resblock) {
4190 if($block->{device} =~ m/^drive-(\S+)/){
4191 $devices->{$1} = 1;
1dc4f496
DM
4192 }
4193 }
86fdcfb2 4194
0a13e08e 4195 my $resmice = mon_cmd($vmid, 'query-mice');
3d7389fe
DM
4196 foreach my $mice (@$resmice) {
4197 if ($mice->{name} eq 'QEMU HID Tablet') {
4198 $devices->{tablet} = 1;
4199 last;
4200 }
4201 }
4202
deb091c5
DC
4203 # for usb devices there is no query-usb
4204 # but we can iterate over the entries in
4205 # qom-list path=/machine/peripheral
0a13e08e 4206 my $resperipheral = mon_cmd($vmid, 'qom-list', path => '/machine/peripheral');
deb091c5 4207 foreach my $per (@$resperipheral) {
c60cad61 4208 if ($per->{name} =~ m/^usb(?:redirdev)?\d+$/) {
deb091c5
DC
4209 $devices->{$per->{name}} = 1;
4210 }
4211 }
4212
1dc4f496 4213 return $devices;
86fdcfb2
DA
4214}
4215
ec21aa11 4216sub vm_deviceplug {
d559309f 4217 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
ae57f6b3 4218
3392d6ca 4219 my $q35 = PVE::QemuServer::Machine::machine_type_is_q35($conf);
db656e5f 4220
95d6343b
DA
4221 my $devices_list = vm_devices_list($vmid);
4222 return 1 if defined($devices_list->{$deviceid});
4223
4df98f2f
TL
4224 # add PCI bridge if we need it for the device
4225 qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid, $arch, $machine_type);
fee46675 4226
3d7389fe 4227 if ($deviceid eq 'tablet') {
d559309f 4228 qemu_deviceadd($vmid, print_tabletdevice_full($conf, $arch));
d559309f 4229 } elsif ($deviceid eq 'keyboard') {
d559309f 4230 qemu_deviceadd($vmid, print_keyboarddevice_full($conf, $arch));
c60cad61
DC
4231 } elsif ($deviceid =~ m/^usbredirdev(\d+)$/) {
4232 my $id = $1;
4233 qemu_spice_usbredir_chardev_add($vmid, "usbredirchardev$id");
4234 qemu_deviceadd($vmid, PVE::QemuServer::USB::print_spice_usbdevice($id, "xhci", $id + 1));
4eb68604 4235 } elsif ($deviceid =~ m/^usb(\d+)$/) {
c60cad61 4236 qemu_deviceadd($vmid, PVE::QemuServer::USB::print_usbdevice_full($conf, $deviceid, $device, {}, $1 + 1));
fee46675 4237 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
22de899a
AD
4238 qemu_iothread_add($vmid, $deviceid, $device);
4239
3326ae19
TL
4240 qemu_driveadd($storecfg, $vmid, $device);
4241 my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
fee46675 4242
3326ae19 4243 qemu_deviceadd($vmid, $devicefull);
fee46675
DM
4244 eval { qemu_deviceaddverify($vmid, $deviceid); };
4245 if (my $err = $@) {
63c2da2f
DM
4246 eval { qemu_drivedel($vmid, $deviceid); };
4247 warn $@ if $@;
fee46675 4248 die $err;
5e5dcb73 4249 }
2733141c 4250 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
3326ae19
TL
4251 my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : "lsi";
4252 my $pciaddr = print_pci_addr($deviceid, undef, $arch, $machine_type);
a1b7d579 4253 my $scsihw_type = $scsihw eq 'virtio-scsi-single' ? "virtio-scsi-pci" : $scsihw;
2733141c 4254
3326ae19 4255 my $devicefull = "$scsihw_type,id=$deviceid$pciaddr";
fee46675 4256
fc8b40fd
AD
4257 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread}) {
4258 qemu_iothread_add($vmid, $deviceid, $device);
4259 $devicefull .= ",iothread=iothread-$deviceid";
4260 }
4261
6e11f143
AD
4262 if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues}) {
4263 $devicefull .= ",num_queues=$device->{queues}";
4264 }
4265
3326ae19
TL
4266 qemu_deviceadd($vmid, $devicefull);
4267 qemu_deviceaddverify($vmid, $deviceid);
fee46675 4268 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
d559309f 4269 qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device, $arch, $machine_type);
fee46675 4270 qemu_driveadd($storecfg, $vmid, $device);
a1b7d579 4271
acfc6ef8 4272 my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device, undef, $arch, $machine_type);
fee46675
DM
4273 eval { qemu_deviceadd($vmid, $devicefull); };
4274 if (my $err = $@) {
63c2da2f
DM
4275 eval { qemu_drivedel($vmid, $deviceid); };
4276 warn $@ if $@;
fee46675 4277 die $err;
a4f091a0 4278 }
fee46675 4279 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
d1c1af4b 4280 return if !qemu_netdevadd($vmid, $conf, $arch, $device, $deviceid);
8718099c 4281
3392d6ca 4282 my $machine_type = PVE::QemuServer::Machine::qemu_machine_pxe($vmid, $conf);
0c03a390 4283 my $machine_version = PVE::QemuServer::Machine::extract_version($machine_type);
95d3be58
DC
4284 my $use_old_bios_files = undef;
4285 ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type);
8718099c 4286
4df98f2f 4287 my $netdevicefull = print_netdevice_full(
0c03a390 4288 $vmid, $conf, $device, $deviceid, undef, $use_old_bios_files, $arch, $machine_type, $machine_version);
95d3be58 4289 qemu_deviceadd($vmid, $netdevicefull);
79046fd1
DC
4290 eval {
4291 qemu_deviceaddverify($vmid, $deviceid);
4292 qemu_set_link_status($vmid, $deviceid, !$device->{link_down});
4293 };
fee46675
DM
4294 if (my $err = $@) {
4295 eval { qemu_netdevdel($vmid, $deviceid); };
4296 warn $@ if $@;
4297 die $err;
95d3be58 4298 }
fee46675 4299 } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) {
40f28a9f 4300 my $bridgeid = $2;
d559309f 4301 my $pciaddr = print_pci_addr($deviceid, undef, $arch, $machine_type);
40f28a9f 4302 my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr";
a1b7d579 4303
40f28a9f 4304 qemu_deviceadd($vmid, $devicefull);
fee46675 4305 qemu_deviceaddverify($vmid, $deviceid);
fee46675 4306 } else {
a1b7d579 4307 die "can't hotplug device '$deviceid'\n";
40f28a9f
AD
4308 }
4309
5e5dcb73 4310 return 1;
a4dea331
DA
4311}
4312
3eec5767 4313# fixme: this should raise exceptions on error!
ec21aa11 4314sub vm_deviceunplug {
f19d1c47 4315 my ($vmid, $conf, $deviceid) = @_;
873c2d69 4316
95d6343b
DA
4317 my $devices_list = vm_devices_list($vmid);
4318 return 1 if !defined($devices_list->{$deviceid});
4319
2141a802
SR
4320 my $bootdisks = PVE::QemuServer::Drive::get_bootdisks($conf);
4321 die "can't unplug bootdisk '$deviceid'\n" if grep {$_ eq $deviceid} @$bootdisks;
63c2da2f 4322
c60cad61 4323 if ($deviceid eq 'tablet' || $deviceid eq 'keyboard' || $deviceid eq 'xhci') {
3d7389fe 4324 qemu_devicedel($vmid, $deviceid);
c60cad61
DC
4325 } elsif ($deviceid =~ m/^usbredirdev\d+$/) {
4326 qemu_devicedel($vmid, $deviceid);
4327 qemu_devicedelverify($vmid, $deviceid);
4eb68604 4328 } elsif ($deviceid =~ m/^usb\d+$/) {
c60cad61
DC
4329 qemu_devicedel($vmid, $deviceid);
4330 qemu_devicedelverify($vmid, $deviceid);
63c2da2f 4331 } elsif ($deviceid =~ m/^(virtio)(\d+)$/) {
9a66c311 4332 my $device = parse_drive($deviceid, $conf->{$deviceid});
f19d1c47 4333
a8d0fec3
TL
4334 qemu_devicedel($vmid, $deviceid);
4335 qemu_devicedelverify($vmid, $deviceid);
4336 qemu_drivedel($vmid, $deviceid);
9a66c311 4337 qemu_iothread_del($vmid, $deviceid, $device);
2733141c 4338 } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) {
63c2da2f 4339 qemu_devicedel($vmid, $deviceid);
8ce30dde 4340 qemu_devicedelverify($vmid, $deviceid);
63c2da2f 4341 } elsif ($deviceid =~ m/^(scsi)(\d+)$/) {
9a66c311 4342 my $device = parse_drive($deviceid, $conf->{$deviceid});
cfc817c7 4343
a8d0fec3 4344 qemu_devicedel($vmid, $deviceid);
52b361af 4345 qemu_devicedelverify($vmid, $deviceid);
a8d0fec3 4346 qemu_drivedel($vmid, $deviceid);
a1b7d579 4347 qemu_deletescsihw($conf, $vmid, $deviceid);
8ce30dde 4348
9a66c311
FG
4349 qemu_iothread_del($vmid, "virtioscsi$device->{index}", $device)
4350 if $conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single');
63c2da2f 4351 } elsif ($deviceid =~ m/^(net)(\d+)$/) {
a8d0fec3
TL
4352 qemu_devicedel($vmid, $deviceid);
4353 qemu_devicedelverify($vmid, $deviceid);
4354 qemu_netdevdel($vmid, $deviceid);
63c2da2f
DM
4355 } else {
4356 die "can't unplug device '$deviceid'\n";
2630d2a9
DA
4357 }
4358
5e5dcb73
DA
4359 return 1;
4360}
4361
c60cad61
DC
4362sub qemu_spice_usbredir_chardev_add {
4363 my ($vmid, $id) = @_;
4364
4365 mon_cmd($vmid, "chardev-add" , (
4366 id => $id,
4367 backend => {
4368 type => 'spicevmc',
4369 data => {
4370 type => "usbredir",
4371 },
4372 },
4373 ));
4374}
4375
5e5dcb73
DA
4376sub qemu_deviceadd {
4377 my ($vmid, $devicefull) = @_;
873c2d69 4378
d695b5b7
AD
4379 $devicefull = "driver=".$devicefull;
4380 my %options = split(/[=,]/, $devicefull);
f19d1c47 4381
0a13e08e 4382 mon_cmd($vmid, "device_add" , %options);
5e5dcb73 4383}
afdb31d5 4384
5e5dcb73 4385sub qemu_devicedel {
fee46675 4386 my ($vmid, $deviceid) = @_;
63c2da2f 4387
0a13e08e 4388 my $ret = mon_cmd($vmid, "device_del", id => $deviceid);
5e5dcb73
DA
4389}
4390
22de899a 4391sub qemu_iothread_add {
a8d0fec3 4392 my ($vmid, $deviceid, $device) = @_;
22de899a
AD
4393
4394 if ($device->{iothread}) {
4395 my $iothreads = vm_iothreads_list($vmid);
4396 qemu_objectadd($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"};
4397 }
4398}
4399
4400sub qemu_iothread_del {
a8d0fec3 4401 my ($vmid, $deviceid, $device) = @_;
22de899a 4402
22de899a
AD
4403 if ($device->{iothread}) {
4404 my $iothreads = vm_iothreads_list($vmid);
4405 qemu_objectdel($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"};
4406 }
4407}
4408
4d3f29ed 4409sub qemu_objectadd {
a8d0fec3 4410 my ($vmid, $objectid, $qomtype) = @_;
4d3f29ed 4411
0a13e08e 4412 mon_cmd($vmid, "object-add", id => $objectid, "qom-type" => $qomtype);
4d3f29ed
AD
4413
4414 return 1;
4415}
4416
4417sub qemu_objectdel {
a8d0fec3 4418 my ($vmid, $objectid) = @_;
4d3f29ed 4419
0a13e08e 4420 mon_cmd($vmid, "object-del", id => $objectid);
4d3f29ed
AD
4421
4422 return 1;
4423}
4424
5e5dcb73 4425sub qemu_driveadd {
fee46675 4426 my ($storecfg, $vmid, $device) = @_;
5e5dcb73 4427
6d5673c3
SR
4428 my $kvmver = get_running_qemu_version($vmid);
4429 my $io_uring = min_version($kvmver, 6, 0);
4430 my $drive = print_drive_commandline_full($storecfg, $vmid, $device, undef, $io_uring);
7a69fc3c 4431 $drive =~ s/\\/\\\\/g;
0a13e08e 4432 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_add auto \"$drive\"");
fee46675 4433
5e5dcb73 4434 # If the command succeeds qemu prints: "OK"
fee46675
DM
4435 return 1 if $ret =~ m/OK/s;
4436
4437 die "adding drive failed: $ret\n";
5e5dcb73 4438}
afdb31d5 4439
5e5dcb73 4440sub qemu_drivedel {
a8d0fec3 4441 my ($vmid, $deviceid) = @_;
873c2d69 4442
0a13e08e 4443 my $ret = PVE::QemuServer::Monitor::hmp_cmd($vmid, "drive_del drive-$deviceid");
5e5dcb73 4444 $ret =~ s/^\s+//;
a1b7d579 4445
63c2da2f 4446 return 1 if $ret eq "";
a1b7d579 4447
63c2da2f 4448 # NB: device not found errors mean the drive was auto-deleted and we ignore the error
a1b7d579
DM
4449 return 1 if $ret =~ m/Device \'.*?\' not found/s;
4450
63c2da2f 4451 die "deleting drive $deviceid failed : $ret\n";
5e5dcb73 4452}
f19d1c47 4453
5e5dcb73 4454sub qemu_deviceaddverify {
fee46675 4455 my ($vmid, $deviceid) = @_;
873c2d69 4456
5e5dcb73
DA
4457 for (my $i = 0; $i <= 5; $i++) {
4458 my $devices_list = vm_devices_list($vmid);
4459 return 1 if defined($devices_list->{$deviceid});
4460 sleep 1;
afdb31d5 4461 }
fee46675
DM
4462
4463 die "error on hotplug device '$deviceid'\n";
5e5dcb73 4464}
afdb31d5 4465
5e5dcb73
DA
4466
4467sub qemu_devicedelverify {
63c2da2f
DM
4468 my ($vmid, $deviceid) = @_;
4469
a1b7d579 4470 # need to verify that the device is correctly removed as device_del
63c2da2f 4471 # is async and empty return is not reliable
5e5dcb73 4472
5e5dcb73
DA
4473 for (my $i = 0; $i <= 5; $i++) {
4474 my $devices_list = vm_devices_list($vmid);
4475 return 1 if !defined($devices_list->{$deviceid});
4476 sleep 1;
afdb31d5 4477 }
63c2da2f
DM
4478
4479 die "error on hot-unplugging device '$deviceid'\n";
873c2d69
DA
4480}
4481
cdd20088 4482sub qemu_findorcreatescsihw {
d559309f 4483 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
cfc817c7 4484
ee034f5c 4485 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
2733141c
AD
4486
4487 my $scsihwid="$controller_prefix$controller";
cfc817c7
DA
4488 my $devices_list = vm_devices_list($vmid);
4489
a8d0fec3 4490 if (!defined($devices_list->{$scsihwid})) {
d559309f 4491 vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device, $arch, $machine_type);
cfc817c7 4492 }
fee46675 4493
cfc817c7
DA
4494 return 1;
4495}
4496
8ce30dde
AD
4497sub qemu_deletescsihw {
4498 my ($conf, $vmid, $opt) = @_;
4499
4500 my $device = parse_drive($opt, $conf->{$opt});
4501
a1511b3c 4502 if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
2733141c
AD
4503 vm_deviceunplug($vmid, $conf, "virtioscsi$device->{index}");
4504 return 1;
4505 }
4506
ee034f5c 4507 my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device);
8ce30dde
AD
4508
4509 my $devices_list = vm_devices_list($vmid);
4510 foreach my $opt (keys %{$devices_list}) {
e0fd2b2f
FE
4511 if (is_valid_drivename($opt)) {
4512 my $drive = parse_drive($opt, $conf->{$opt});
a8d0fec3 4513 if ($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) {
8ce30dde
AD
4514 return 1;
4515 }
4516 }
4517 }
4518
4519 my $scsihwid="scsihw$controller";
4520
4521 vm_deviceunplug($vmid, $conf, $scsihwid);
4522
4523 return 1;
4524}
4525
281fedb3 4526sub qemu_add_pci_bridge {
d559309f 4527 my ($storecfg, $conf, $vmid, $device, $arch, $machine_type) = @_;
40f28a9f
AD
4528
4529 my $bridges = {};
281fedb3
DM
4530
4531 my $bridgeid;
4532
d559309f 4533 print_pci_addr($device, $bridges, $arch, $machine_type);
40f28a9f
AD
4534
4535 while (my ($k, $v) = each %$bridges) {
4536 $bridgeid = $k;
4537 }
fee46675 4538 return 1 if !defined($bridgeid) || $bridgeid < 1;
281fedb3 4539
40f28a9f
AD
4540 my $bridge = "pci.$bridgeid";
4541 my $devices_list = vm_devices_list($vmid);
4542
281fedb3 4543 if (!defined($devices_list->{$bridge})) {
d559309f 4544 vm_deviceplug($storecfg, $conf, $vmid, $bridge, $arch, $machine_type);
40f28a9f 4545 }
281fedb3 4546
40f28a9f
AD
4547 return 1;
4548}
4549
25088687
DM
4550sub qemu_set_link_status {
4551 my ($vmid, $device, $up) = @_;
4552
0a13e08e 4553 mon_cmd($vmid, "set_link", name => $device,
25088687
DM
4554 up => $up ? JSON::true : JSON::false);
4555}
4556
2630d2a9 4557sub qemu_netdevadd {
d559309f 4558 my ($vmid, $conf, $arch, $device, $deviceid) = @_;
2630d2a9 4559
d559309f 4560 my $netdev = print_netdev_full($vmid, $conf, $arch, $device, $deviceid, 1);
73aa03b8 4561 my %options = split(/[=,]/, $netdev);
2630d2a9 4562
bf5aef9b
DC
4563 if (defined(my $vhost = $options{vhost})) {
4564 $options{vhost} = JSON::boolean(PVE::JSONSchema::parse_boolean($vhost));
4565 }
4566
4567 if (defined(my $queues = $options{queues})) {
4568 $options{queues} = $queues + 0;
4569 }
4570
0a13e08e 4571 mon_cmd($vmid, "netdev_add", %options);
73aa03b8 4572 return 1;
2630d2a9
DA
4573}
4574
4575sub qemu_netdevdel {
4576 my ($vmid, $deviceid) = @_;
4577
0a13e08e 4578 mon_cmd($vmid, "netdev_del", id => $deviceid);
2630d2a9
DA
4579}
4580
16521d63 4581sub qemu_usb_hotplug {
d559309f 4582 my ($storecfg, $conf, $vmid, $deviceid, $device, $arch, $machine_type) = @_;
16521d63
DC
4583
4584 return if !$device;
4585
4586 # remove the old one first
4587 vm_deviceunplug($vmid, $conf, $deviceid);
4588
4589 # check if xhci controller is necessary and available
c60cad61 4590 my $devicelist = vm_devices_list($vmid);
16521d63 4591
c60cad61
DC
4592 if (!$devicelist->{xhci}) {
4593 my $pciaddr = print_pci_addr("xhci", undef, $arch, $machine_type);
4594 qemu_deviceadd($vmid, PVE::QemuServer::USB::print_qemu_xhci_controller($pciaddr));
16521d63 4595 }
c60cad61
DC
4596
4597 # print_usbdevice_full expects the parsed device
16521d63
DC
4598 my $d = parse_usb_device($device->{host});
4599 $d->{usb3} = $device->{usb3};
4600
4601 # add the new one
d559309f 4602 vm_deviceplug($storecfg, $conf, $vmid, $deviceid, $d, $arch, $machine_type);
16521d63
DC
4603}
4604
838776ab 4605sub qemu_cpu_hotplug {
8edc9c08 4606 my ($vmid, $conf, $vcpus) = @_;
838776ab 4607
3392d6ca 4608 my $machine_type = PVE::QemuServer::Machine::get_current_qemu_machine($vmid);
1e881b75 4609
8edc9c08
AD
4610 my $sockets = 1;
4611 $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused
4612 $sockets = $conf->{sockets} if $conf->{sockets};
4613 my $cores = $conf->{cores} || 1;
4614 my $maxcpus = $sockets * $cores;
838776ab 4615
8edc9c08 4616 $vcpus = $maxcpus if !$vcpus;
3a11fadb 4617
8edc9c08
AD
4618 die "you can't add more vcpus than maxcpus\n"
4619 if $vcpus > $maxcpus;
3a11fadb 4620
8edc9c08 4621 my $currentvcpus = $conf->{vcpus} || $maxcpus;
1e881b75 4622
eba3e64d 4623 if ($vcpus < $currentvcpus) {
1e881b75 4624
2ea5fb7e 4625 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
1e881b75
AD
4626
4627 for (my $i = $currentvcpus; $i > $vcpus; $i--) {
4628 qemu_devicedel($vmid, "cpu$i");
4629 my $retry = 0;
4630 my $currentrunningvcpus = undef;
4631 while (1) {
65af8c31 4632 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
1e881b75 4633 last if scalar(@{$currentrunningvcpus}) == $i-1;
961af8a3 4634 raise_param_exc({ vcpus => "error unplugging cpu$i" }) if $retry > 5;
1e881b75
AD
4635 $retry++;
4636 sleep 1;
4637 }
4638 #update conf after each succesfull cpu unplug
4639 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4640 PVE::QemuConfig->write_config($vmid, $conf);
4641 }
4642 } else {
961af8a3 4643 die "cpu hot-unplugging requires qemu version 2.7 or higher\n";
1e881b75
AD
4644 }
4645
4646 return;
4647 }
838776ab 4648
65af8c31 4649 my $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
961af8a3 4650 die "vcpus in running vm does not match its configuration\n"
8edc9c08 4651 if scalar(@{$currentrunningvcpus}) != $currentvcpus;
838776ab 4652
2ea5fb7e 4653 if (PVE::QemuServer::Machine::machine_version($machine_type, 2, 7)) {
eba3e64d
AD
4654
4655 for (my $i = $currentvcpus+1; $i <= $vcpus; $i++) {
4656 my $cpustr = print_cpu_device($conf, $i);
4657 qemu_deviceadd($vmid, $cpustr);
4658
4659 my $retry = 0;
4660 my $currentrunningvcpus = undef;
4661 while (1) {
65af8c31 4662 $currentrunningvcpus = mon_cmd($vmid, "query-cpus-fast");
eba3e64d 4663 last if scalar(@{$currentrunningvcpus}) == $i;
961af8a3 4664 raise_param_exc({ vcpus => "error hotplugging cpu$i" }) if $retry > 10;
eba3e64d
AD
4665 sleep 1;
4666 $retry++;
4667 }
4668 #update conf after each succesfull cpu hotplug
4669 $conf->{vcpus} = scalar(@{$currentrunningvcpus});
4670 PVE::QemuConfig->write_config($vmid, $conf);
4671 }
4672 } else {
4673
4674 for (my $i = $currentvcpus; $i < $vcpus; $i++) {
0a13e08e 4675 mon_cmd($vmid, "cpu-add", id => int($i));
eba3e64d 4676 }
838776ab
AD
4677 }
4678}
4679
affd2f88 4680sub qemu_block_set_io_throttle {
277ca170
WB
4681 my ($vmid, $deviceid,
4682 $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr,
9196a8ec
WB
4683 $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max,
4684 $bps_max_length, $bps_rd_max_length, $bps_wr_max_length,
4685 $iops_max_length, $iops_rd_max_length, $iops_wr_max_length) = @_;
affd2f88 4686
f3f323a3
AD
4687 return if !check_running($vmid) ;
4688
0a13e08e 4689 mon_cmd($vmid, "block_set_io_throttle", device => $deviceid,
277ca170
WB
4690 bps => int($bps),
4691 bps_rd => int($bps_rd),
4692 bps_wr => int($bps_wr),
4693 iops => int($iops),
4694 iops_rd => int($iops_rd),
4695 iops_wr => int($iops_wr),
4696 bps_max => int($bps_max),
4697 bps_rd_max => int($bps_rd_max),
4698 bps_wr_max => int($bps_wr_max),
4699 iops_max => int($iops_max),
4700 iops_rd_max => int($iops_rd_max),
9196a8ec
WB
4701 iops_wr_max => int($iops_wr_max),
4702 bps_max_length => int($bps_max_length),
4703 bps_rd_max_length => int($bps_rd_max_length),
4704 bps_wr_max_length => int($bps_wr_max_length),
4705 iops_max_length => int($iops_max_length),
4706 iops_rd_max_length => int($iops_rd_max_length),
4707 iops_wr_max_length => int($iops_wr_max_length),
277ca170 4708 );
f3f323a3 4709
affd2f88
AD
4710}
4711
c1175c92
AD
4712sub qemu_block_resize {
4713 my ($vmid, $deviceid, $storecfg, $volid, $size) = @_;
4714
ed221350 4715 my $running = check_running($vmid);
c1175c92 4716
7246e8f9 4717 $size = 0 if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running);
c1175c92
AD
4718
4719 return if !$running;
4720
375db731
FE
4721 my $padding = (1024 - $size % 1024) % 1024;
4722 $size = $size + $padding;
4723
190c8461
SR
4724 mon_cmd(
4725 $vmid,
4726 "block_resize",
4727 device => $deviceid,
4728 size => int($size),
4729 timeout => 60,
4730 );
c1175c92
AD
4731}
4732
1ab0057c
AD
4733sub qemu_volume_snapshot {
4734 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4735
ed221350 4736 my $running = check_running($vmid);
1ab0057c 4737
9d83932d 4738 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
0a13e08e 4739 mon_cmd($vmid, 'blockdev-snapshot-internal-sync', device => $deviceid, name => $snap);
e5eaa028
WL
4740 } else {
4741 PVE::Storage::volume_snapshot($storecfg, $volid, $snap);
4742 }
1ab0057c
AD
4743}
4744
fc46aff9
AD
4745sub qemu_volume_snapshot_delete {
4746 my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_;
4747
ed221350 4748 my $running = check_running($vmid);
fc46aff9 4749
a2f1554b
AD
4750 if($running) {
4751
4752 $running = undef;
4753 my $conf = PVE::QemuConfig->load_config($vmid);
912792e2 4754 PVE::QemuConfig->foreach_volume($conf, sub {
a2f1554b
AD
4755 my ($ds, $drive) = @_;
4756 $running = 1 if $drive->{file} eq $volid;
4757 });
4758 }
4759
9d83932d 4760 if ($running && do_snapshots_with_qemu($storecfg, $volid, $deviceid)) {
0a13e08e 4761 mon_cmd($vmid, 'blockdev-snapshot-delete-internal-sync', device => $deviceid, name => $snap);
1ef7592f
AD
4762 } else {
4763 PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running);
4764 }
fc46aff9
AD
4765}
4766
264e519f 4767sub set_migration_caps {
27a5be53 4768 my ($vmid, $savevm) = @_;
a89fded1 4769
acc10e51
SR
4770 my $qemu_support = eval { mon_cmd($vmid, "query-proxmox-support") };
4771
27a5be53
SR
4772 my $bitmap_prop = $savevm ? 'pbs-dirty-bitmap-savevm' : 'pbs-dirty-bitmap-migration';
4773 my $dirty_bitmaps = $qemu_support->{$bitmap_prop} ? 1 : 0;
4774
8b8345f3 4775 my $cap_ref = [];
a89fded1
AD
4776
4777 my $enabled_cap = {
8b8345f3 4778 "auto-converge" => 1,
0b0a47e8 4779 "xbzrle" => 1,
8b8345f3
DM
4780 "x-rdma-pin-all" => 0,
4781 "zero-blocks" => 0,
acc10e51 4782 "compress" => 0,
27a5be53 4783 "dirty-bitmaps" => $dirty_bitmaps,
a89fded1
AD
4784 };
4785
0a13e08e 4786 my $supported_capabilities = mon_cmd($vmid, "query-migrate-capabilities");
a89fded1 4787
8b8345f3 4788 for my $supported_capability (@$supported_capabilities) {
b463a3ce
SP
4789 push @$cap_ref, {
4790 capability => $supported_capability->{capability},
22430fa2
DM
4791 state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false,
4792 };
a89fded1
AD
4793 }
4794
0a13e08e 4795 mon_cmd($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
8b8345f3 4796}
a89fded1 4797
912792e2
FE
4798sub foreach_volid {
4799 my ($conf, $func, @param) = @_;
4800
4801 my $volhash = {};
4802
4803 my $test_volid = sub {
ae180b8f 4804 my ($key, $drive, $snapname) = @_;
912792e2 4805
ae180b8f 4806 my $volid = $drive->{file};
912792e2
FE
4807 return if !$volid;
4808
4809 $volhash->{$volid}->{cdrom} //= 1;
ae180b8f 4810 $volhash->{$volid}->{cdrom} = 0 if !drive_is_cdrom($drive);
912792e2 4811
ae180b8f 4812 my $replicate = $drive->{replicate} // 1;
912792e2
FE
4813 $volhash->{$volid}->{replicate} //= 0;
4814 $volhash->{$volid}->{replicate} = 1 if $replicate;
4815
4816 $volhash->{$volid}->{shared} //= 0;
ae180b8f 4817 $volhash->{$volid}->{shared} = 1 if $drive->{shared};
912792e2
FE
4818
4819 $volhash->{$volid}->{referenced_in_config} //= 0;
4820 $volhash->{$volid}->{referenced_in_config} = 1 if !defined($snapname);
4821
4822 $volhash->{$volid}->{referenced_in_snapshot}->{$snapname} = 1
4823 if defined($snapname);
ae180b8f
FE
4824
4825 my $size = $drive->{size};
4826 $volhash->{$volid}->{size} //= $size if $size;
4827
4828 $volhash->{$volid}->{is_vmstate} //= 0;
4829 $volhash->{$volid}->{is_vmstate} = 1 if $key eq 'vmstate';
4830
f9dde219
SR
4831 $volhash->{$volid}->{is_tpmstate} //= 0;
4832 $volhash->{$volid}->{is_tpmstate} = 1 if $key eq 'tpmstate0';
4833
ae180b8f
FE
4834 $volhash->{$volid}->{is_unused} //= 0;
4835 $volhash->{$volid}->{is_unused} = 1 if $key =~ /^unused\d+$/;
a6be63ac
FE
4836
4837 $volhash->{$volid}->{drivename} = $key if is_valid_drivename($key);
912792e2
FE
4838 };
4839
ae180b8f
FE
4840 my $include_opts = {
4841 extra_keys => ['vmstate'],
4842 include_unused => 1,
4843 };
4844
0b953b8e 4845 PVE::QemuConfig->foreach_volume_full($conf, $include_opts, $test_volid);
912792e2
FE
4846 foreach my $snapname (keys %{$conf->{snapshots}}) {
4847 my $snap = $conf->{snapshots}->{$snapname};
0b953b8e 4848 PVE::QemuConfig->foreach_volume_full($snap, $include_opts, $test_volid, $snapname);
912792e2
FE
4849 }
4850
4851 foreach my $volid (keys %$volhash) {
4852 &$func($volid, $volhash->{$volid}, @param);
4853 }
4854}
4855
81d95ae1 4856my $fast_plug_option = {
7498eb64 4857 'lock' => 1,
81d95ae1 4858 'name' => 1,
a1b7d579 4859 'onboot' => 1,
81d95ae1
DM
4860 'shares' => 1,
4861 'startup' => 1,
b0ec896e 4862 'description' => 1,
ec647db4 4863 'protection' => 1,
8cad5e9b 4864 'vmstatestorage' => 1,
9e784b11 4865 'hookscript' => 1,
b8e7068a 4866 'tags' => 1,
81d95ae1
DM
4867};
4868
71d9006b
AD
4869for my $opt (keys %$confdesc_cloudinit) {
4870 $fast_plug_option->{$opt} = 1;
4871};
4872
3a11fadb
DM
4873# hotplug changes in [PENDING]
4874# $selection hash can be used to only apply specified options, for
4875# example: { cores => 1 } (only apply changed 'cores')
4876# $errors ref is used to return error messages
c427973b 4877sub vmconfig_hotplug_pending {
3a11fadb 4878 my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
c427973b 4879
8e90138a 4880 my $defaults = load_defaults();
045749f2
TL
4881 my $arch = get_vm_arch($conf);
4882 my $machine_type = get_vm_machine($conf, undef, $arch);
c427973b
DM
4883
4884 # commit values which do not have any impact on running VM first
3a11fadb
DM
4885 # Note: those option cannot raise errors, we we do not care about
4886 # $selection and always apply them.
4887
4888 my $add_error = sub {
4889 my ($opt, $msg) = @_;
4890 $errors->{$opt} = "hotplug problem - $msg";
4891 };
c427973b
DM
4892
4893 my $changes = 0;
4894 foreach my $opt (keys %{$conf->{pending}}) { # add/change
81d95ae1 4895 if ($fast_plug_option->{$opt}) {
c427973b
DM
4896 $conf->{$opt} = $conf->{pending}->{$opt};
4897 delete $conf->{pending}->{$opt};
4898 $changes = 1;
4899 }
4900 }
4901
4902 if ($changes) {
ffda963f 4903 PVE::QemuConfig->write_config($vmid, $conf);
c427973b
DM
4904 }
4905
c60cad61
DC
4906 my $ostype = $conf->{ostype};
4907 my $version = extract_version($machine_type, get_running_qemu_version($vmid));
b3c2bdd1 4908 my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
c60cad61
DC
4909 my $usb_hotplug = $hotplug_features->{usb}
4910 && min_version($version, 7, 1)
4911 && defined($ostype) && ($ostype eq 'l26' || windows_version($ostype) > 7);
c427973b 4912
5b65b00d 4913 my $cgroup = PVE::QemuServer::CGroup->new($vmid);
98bc3aeb 4914 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
d321c4a9 4915 foreach my $opt (sort keys %$pending_delete_hash) {
3a11fadb 4916 next if $selection && !$selection->{$opt};
d321c4a9 4917 my $force = $pending_delete_hash->{$opt}->{force};
3a11fadb 4918 eval {
51a6f637
AD
4919 if ($opt eq 'hotplug') {
4920 die "skip\n" if ($conf->{hotplug} =~ /memory/);
4921 } elsif ($opt eq 'tablet') {
b3c2bdd1 4922 die "skip\n" if !$hotplug_features->{usb};
3a11fadb 4923 if ($defaults->{tablet}) {
d559309f
WB
4924 vm_deviceplug($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
4925 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
4926 if $arch eq 'aarch64';
3a11fadb 4927 } else {
d559309f
WB
4928 vm_deviceunplug($vmid, $conf, 'tablet');
4929 vm_deviceunplug($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
3a11fadb 4930 }
c60cad61
DC
4931 } elsif ($opt =~ m/^usb(\d+)$/) {
4932 my $index = $1;
4933 die "skip\n" if !$usb_hotplug;
4934 vm_deviceunplug($vmid, $conf, "usbredirdev$index"); # if it's a spice port
4935 vm_deviceunplug($vmid, $conf, $opt);
8edc9c08 4936 } elsif ($opt eq 'vcpus') {
b3c2bdd1 4937 die "skip\n" if !$hotplug_features->{cpu};
8edc9c08 4938 qemu_cpu_hotplug($vmid, $conf, undef);
9c2f7069 4939 } elsif ($opt eq 'balloon') {
81d95ae1 4940 # enable balloon device is not hotpluggable
75b51053
DC
4941 die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
4942 # here we reset the ballooning value to memory
4943 my $balloon = $conf->{memory} || $defaults->{memory};
0a13e08e 4944 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
81d95ae1
DM
4945 } elsif ($fast_plug_option->{$opt}) {
4946 # do nothing
3eec5767 4947 } elsif ($opt =~ m/^net(\d+)$/) {
b3c2bdd1 4948 die "skip\n" if !$hotplug_features->{network};
3eec5767 4949 vm_deviceunplug($vmid, $conf, $opt);
74479ee9 4950 } elsif (is_valid_drivename($opt)) {
b3c2bdd1 4951 die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
19120f99 4952 vm_deviceunplug($vmid, $conf, $opt);
3dc38fbb 4953 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
4d3f29ed
AD
4954 } elsif ($opt =~ m/^memory$/) {
4955 die "skip\n" if !$hotplug_features->{memory};
6779f1ac 4956 PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
c8effec3 4957 } elsif ($opt eq 'cpuunits') {
0d318453 4958 $cgroup->change_cpu_shares(undef);
58be00f1 4959 } elsif ($opt eq 'cpulimit') {
25de70ae 4960 $cgroup->change_cpu_quota(undef, undef); # reset, cgroup module can better decide values
3d7389fe 4961 } else {
e56beeda 4962 die "skip\n";
3d7389fe 4963 }
3a11fadb
DM
4964 };
4965 if (my $err = $@) {
e56beeda
DM
4966 &$add_error($opt, $err) if $err ne "skip\n";
4967 } else {
3a11fadb 4968 delete $conf->{$opt};
98bc3aeb 4969 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
3d7389fe 4970 }
3d7389fe
DM
4971 }
4972
4973 foreach my $opt (keys %{$conf->{pending}}) {
3a11fadb 4974 next if $selection && !$selection->{$opt};
3d7389fe 4975 my $value = $conf->{pending}->{$opt};
3a11fadb 4976 eval {
51a6f637
AD
4977 if ($opt eq 'hotplug') {
4978 die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/);
4979 } elsif ($opt eq 'tablet') {
b3c2bdd1 4980 die "skip\n" if !$hotplug_features->{usb};
3a11fadb 4981 if ($value == 1) {
d559309f
WB
4982 vm_deviceplug($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
4983 vm_deviceplug($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
4984 if $arch eq 'aarch64';
3a11fadb 4985 } elsif ($value == 0) {
d559309f
WB
4986 vm_deviceunplug($vmid, $conf, 'tablet');
4987 vm_deviceunplug($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
3a11fadb 4988 }
c60cad61
DC
4989 } elsif ($opt =~ m/^usb(\d+)$/) {
4990 my $index = $1;
4991 die "skip\n" if !$usb_hotplug;
4992 my $d = eval { parse_property_string($usbdesc->{format}, $value) };
4993 my $id = $opt;
4994 if ($d->{host} eq 'spice') {
4995 $id = "usbredirdev$index";
4996 }
4997 qemu_usb_hotplug($storecfg, $conf, $vmid, $id, $d, $arch, $machine_type);
8edc9c08 4998 } elsif ($opt eq 'vcpus') {
b3c2bdd1 4999 die "skip\n" if !$hotplug_features->{cpu};
3a11fadb
DM
5000 qemu_cpu_hotplug($vmid, $conf, $value);
5001 } elsif ($opt eq 'balloon') {
81d95ae1 5002 # enable/disable balloning device is not hotpluggable
8fe689e7 5003 my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon});
a1b7d579 5004 my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon});
81d95ae1
DM
5005 die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
5006
3a11fadb 5007 # allow manual ballooning if shares is set to zero
4cc1efa6 5008 if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
9c2f7069 5009 my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
0a13e08e 5010 mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
9c2f7069 5011 }
a1b7d579 5012 } elsif ($opt =~ m/^net(\d+)$/) {
3eec5767 5013 # some changes can be done without hotplug
a1b7d579 5014 vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
d559309f 5015 $vmid, $opt, $value, $arch, $machine_type);
74479ee9 5016 } elsif (is_valid_drivename($opt)) {
f9dde219 5017 die "skip\n" if $opt eq 'efidisk0' || $opt eq 'tpmstate0';
a05cff86 5018 # some changes can be done without hotplug
9ed7a77c
WB
5019 my $drive = parse_drive($opt, $value);
5020 if (drive_is_cloudinit($drive)) {
b137c30c 5021 $conf->{cloudinit} = PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
9ed7a77c 5022 }
b3c2bdd1 5023 vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
9e7bce2c 5024 $vmid, $opt, $value, $arch, $machine_type);
4d3f29ed
AD
5025 } elsif ($opt =~ m/^memory$/) { #dimms
5026 die "skip\n" if !$hotplug_features->{memory};
6779f1ac 5027 $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
c8effec3 5028 } elsif ($opt eq 'cpuunits') {
6b7ef5e5 5029 my $new_cpuunits = PVE::CGroup::clamp_cpu_shares($conf->{pending}->{$opt}); #clamp
0d318453 5030 $cgroup->change_cpu_shares($new_cpuunits);
58be00f1 5031 } elsif ($opt eq 'cpulimit') {
c6f773b8 5032 my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
5b65b00d 5033 $cgroup->change_cpu_quota($cpulimit, 100000);
74ea2c65
AD
5034 } elsif ($opt eq 'agent') {
5035 vmconfig_update_agent($conf, $opt, $value);
3a11fadb 5036 } else {
e56beeda 5037 die "skip\n"; # skip non-hot-pluggable options
3d7389fe 5038 }
3a11fadb
DM
5039 };
5040 if (my $err = $@) {
e56beeda
DM
5041 &$add_error($opt, $err) if $err ne "skip\n";
5042 } else {
3a11fadb
DM
5043 $conf->{$opt} = $value;
5044 delete $conf->{pending}->{$opt};
3d7389fe 5045 }
3d7389fe 5046 }
c60cad61
DC
5047
5048 # unplug xhci controller if no usb device is left
5049 if ($usb_hotplug) {
5050 my $has_usb = 0;
5051 for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) {
5052 next if !defined($conf->{"usb$i"});
5053 $has_usb = 1;
5054 last;
5055 }
5056 if (!$has_usb) {
5057 vm_deviceunplug($vmid, $conf, 'xhci');
5058 }
5059 }
5060
4df15a03 5061 PVE::QemuConfig->write_config($vmid, $conf);
94ec5e7c 5062
b137c30c 5063 if ($hotplug_features->{cloudinit}) {
94ec5e7c
AD
5064 my $pending = PVE::QemuServer::Cloudinit::get_pending_config($conf, $vmid);
5065 my $regenerate = undef;
5066 for my $item (@$pending) {
5067 $regenerate = 1 if defined($item->{delete}) or defined($item->{pending});
5068 }
5069 PVE::QemuServer::vmconfig_update_cloudinit_drive($storecfg, $conf, $vmid) if $regenerate;
5070 }
c427973b 5071}
055d554d 5072
3dc38fbb
WB
5073sub try_deallocate_drive {
5074 my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
5075
5076 if (($force || $key =~ /^unused/) && !drive_is_cdrom($drive, 1)) {
5077 my $volid = $drive->{file};
5078 if (vm_is_volid_owner($storecfg, $vmid, $volid)) {
5079 my $sid = PVE::Storage::parse_volume_id($volid);
5080 $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
cee01bcb
WB
5081
5082 # check if the disk is really unused
cee01bcb 5083 die "unable to delete '$volid' - volume is still in use (snapshot?)\n"
e0fd2b2f 5084 if PVE::QemuServer::Drive::is_volume_in_use($storecfg, $conf, $key, $volid);
cee01bcb 5085 PVE::Storage::vdisk_free($storecfg, $volid);
3dc38fbb 5086 return 1;
40b977f3
WL
5087 } else {
5088 # If vm is not owner of this disk remove from config
5089 return 1;
3dc38fbb
WB
5090 }
5091 }
5092
d1c1af4b 5093 return;
3dc38fbb
WB
5094}
5095
5096sub vmconfig_delete_or_detach_drive {
5097 my ($vmid, $storecfg, $conf, $opt, $force) = @_;
5098
5099 my $drive = parse_drive($opt, $conf->{$opt});
5100
5101 my $rpcenv = PVE::RPCEnvironment::get();
5102 my $authuser = $rpcenv->get_user();
5103
5104 if ($force) {
5105 $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
5106 try_deallocate_drive($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force);
5107 } else {
5108 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $drive);
5109 }
5110}
5111
98bc3aeb
OB
5112
5113
055d554d 5114sub vmconfig_apply_pending {
eb5e482d
OB
5115 my ($vmid, $conf, $storecfg, $errors) = @_;
5116
a644de29
OB
5117 return if !scalar(keys %{$conf->{pending}});
5118
eb5e482d
OB
5119 my $add_apply_error = sub {
5120 my ($opt, $msg) = @_;
5121 my $err_msg = "unable to apply pending change $opt : $msg";
5122 $errors->{$opt} = $err_msg;
5123 warn $err_msg;
5124 };
c427973b
DM
5125
5126 # cold plug
055d554d 5127
98bc3aeb 5128 my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
d321c4a9 5129 foreach my $opt (sort keys %$pending_delete_hash) {
fb4d1ba2 5130 my $force = $pending_delete_hash->{$opt}->{force};
eb5e482d 5131 eval {
3d48b95a
OB
5132 if ($opt =~ m/^unused/) {
5133 die "internal error";
5134 } elsif (defined($conf->{$opt}) && is_valid_drivename($opt)) {
eb5e482d 5135 vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
eb5e482d
OB
5136 }
5137 };
5138 if (my $err = $@) {
5139 $add_apply_error->($opt, $err);
055d554d 5140 } else {
98bc3aeb 5141 PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
055d554d 5142 delete $conf->{$opt};
055d554d
DM
5143 }
5144 }
5145
3d48b95a 5146 PVE::QemuConfig->cleanup_pending($conf);
055d554d 5147
c930f99e
AD
5148 my $generate_cloudnit = undef;
5149
055d554d 5150 foreach my $opt (keys %{$conf->{pending}}) { # add/change
3d48b95a 5151 next if $opt eq 'delete'; # just to be sure
eb5e482d 5152 eval {
3d48b95a 5153 if (defined($conf->{$opt}) && is_valid_drivename($opt)) {
eb5e482d 5154 vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
eb5e482d
OB
5155 }
5156 };
5157 if (my $err = $@) {
5158 $add_apply_error->($opt, $err);
055d554d 5159 } else {
c930f99e
AD
5160
5161 if (is_valid_drivename($opt)) {
5162 my $drive = parse_drive($opt, $conf->{pending}->{$opt});
5163 $generate_cloudnit = 1 if drive_is_cloudinit($drive);
5164 }
5165
eb5e482d 5166 $conf->{$opt} = delete $conf->{pending}->{$opt};
055d554d 5167 }
055d554d 5168 }
3d48b95a 5169
b137c30c
TL
5170 $conf->{cloudinit} = PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid)
5171 if $generate_cloudnit;
5172
3d48b95a
OB
5173 # write all changes at once to avoid unnecessary i/o
5174 PVE::QemuConfig->write_config($vmid, $conf);
055d554d
DM
5175}
5176
3eec5767 5177sub vmconfig_update_net {
d559309f 5178 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
3eec5767
DM
5179
5180 my $newnet = parse_net($value);
5181
5182 if ($conf->{$opt}) {
5183 my $oldnet = parse_net($conf->{$opt});
5184
0f1af9e7
OB
5185 if (safe_string_ne($oldnet->{model}, $newnet->{model}) ||
5186 safe_string_ne($oldnet->{macaddr}, $newnet->{macaddr}) ||
5187 safe_num_ne($oldnet->{queues}, $newnet->{queues}) ||
3eec5767
DM
5188 !($newnet->{bridge} && $oldnet->{bridge})) { # bridge/nat mode change
5189
5190 # for non online change, we try to hot-unplug
7196b757 5191 die "skip\n" if !$hotplug;
3eec5767
DM
5192 vm_deviceunplug($vmid, $conf, $opt);
5193 } else {
5194
5195 die "internal error" if $opt !~ m/net(\d+)/;
5196 my $iface = "tap${vmid}i$1";
a1b7d579 5197
0f1af9e7
OB
5198 if (safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
5199 safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
5200 safe_string_ne($oldnet->{trunks}, $newnet->{trunks}) ||
5201 safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
3eec5767 5202 PVE::Network::tap_unplug($iface);
28e129cc
AD
5203
5204 if ($have_sdn) {
5205 PVE::Network::SDN::Zones::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
5206 } else {
5207 PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
5208 }
0f1af9e7 5209 } elsif (safe_num_ne($oldnet->{rate}, $newnet->{rate})) {
4f4fbeb0
WB
5210 # Rate can be applied on its own but any change above needs to
5211 # include the rate in tap_plug since OVS resets everything.
5212 PVE::Network::tap_rate_limit($iface, $newnet->{rate});
3eec5767 5213 }
38c590d9 5214
0f1af9e7 5215 if (safe_string_ne($oldnet->{link_down}, $newnet->{link_down})) {
25088687
DM
5216 qemu_set_link_status($vmid, $opt, !$newnet->{link_down});
5217 }
5218
38c590d9 5219 return 1;
3eec5767
DM
5220 }
5221 }
a1b7d579 5222
7196b757 5223 if ($hotplug) {
d559309f 5224 vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
38c590d9
DM
5225 } else {
5226 die "skip\n";
5227 }
3eec5767
DM
5228}
5229
74ea2c65
AD
5230sub vmconfig_update_agent {
5231 my ($conf, $opt, $value) = @_;
5232
5233 die "skip\n" if !$conf->{$opt};
5234
5235 my $hotplug_options = { fstrim_cloned_disks => 1 };
5236
5237 my $old_agent = parse_guest_agent($conf);
5238 my $agent = parse_guest_agent({$opt => $value});
5239
33f8b887 5240 for my $option (keys %$agent) { # added/changed options
74ea2c65
AD
5241 next if defined($hotplug_options->{$option});
5242 die "skip\n" if safe_string_ne($agent->{$option}, $old_agent->{$option});
5243 }
5244
33f8b887 5245 for my $option (keys %$old_agent) { # removed options
74ea2c65
AD
5246 next if defined($hotplug_options->{$option});
5247 die "skip\n" if safe_string_ne($old_agent->{$option}, $agent->{$option});
5248 }
33f8b887
TL
5249
5250 return; # either no actual change (e.g., format string reordered) or just hotpluggable changes
74ea2c65
AD
5251}
5252
a05cff86 5253sub vmconfig_update_disk {
9e7bce2c 5254 my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $arch, $machine_type) = @_;
a05cff86
DM
5255
5256 my $drive = parse_drive($opt, $value);
5257
4df98f2f
TL
5258 if ($conf->{$opt} && (my $old_drive = parse_drive($opt, $conf->{$opt}))) {
5259 my $media = $drive->{media} || 'disk';
5260 my $oldmedia = $old_drive->{media} || 'disk';
5261 die "unable to change media type\n" if $media ne $oldmedia;
a05cff86 5262
4df98f2f 5263 if (!drive_is_cdrom($old_drive)) {
a05cff86 5264
4df98f2f 5265 if ($drive->{file} ne $old_drive->{file}) {
a05cff86 5266
4df98f2f 5267 die "skip\n" if !$hotplug;
a05cff86 5268
4df98f2f
TL
5269 # unplug and register as unused
5270 vm_deviceunplug($vmid, $conf, $opt);
5271 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive)
a1b7d579 5272
4df98f2f
TL
5273 } else {
5274 # update existing disk
5275
5276 # skip non hotpluggable value
5277 if (safe_string_ne($drive->{discard}, $old_drive->{discard}) ||
5278 safe_string_ne($drive->{iothread}, $old_drive->{iothread}) ||
5279 safe_string_ne($drive->{queues}, $old_drive->{queues}) ||
5280 safe_string_ne($drive->{cache}, $old_drive->{cache}) ||
5281 safe_string_ne($drive->{ssd}, $old_drive->{ssd})) {
5282 die "skip\n";
5283 }
a05cff86 5284
4df98f2f
TL
5285 # apply throttle
5286 if (safe_num_ne($drive->{mbps}, $old_drive->{mbps}) ||
5287 safe_num_ne($drive->{mbps_rd}, $old_drive->{mbps_rd}) ||
5288 safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) ||
5289 safe_num_ne($drive->{iops}, $old_drive->{iops}) ||
5290 safe_num_ne($drive->{iops_rd}, $old_drive->{iops_rd}) ||
5291 safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr}) ||
5292 safe_num_ne($drive->{mbps_max}, $old_drive->{mbps_max}) ||
5293 safe_num_ne($drive->{mbps_rd_max}, $old_drive->{mbps_rd_max}) ||
5294 safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) ||
5295 safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) ||
5296 safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) ||
5297 safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max}) ||
5298 safe_num_ne($drive->{bps_max_length}, $old_drive->{bps_max_length}) ||
5299 safe_num_ne($drive->{bps_rd_max_length}, $old_drive->{bps_rd_max_length}) ||
5300 safe_num_ne($drive->{bps_wr_max_length}, $old_drive->{bps_wr_max_length}) ||
5301 safe_num_ne($drive->{iops_max_length}, $old_drive->{iops_max_length}) ||
5302 safe_num_ne($drive->{iops_rd_max_length}, $old_drive->{iops_rd_max_length}) ||
5303 safe_num_ne($drive->{iops_wr_max_length}, $old_drive->{iops_wr_max_length})) {
5304
5305 qemu_block_set_io_throttle(
5306 $vmid,"drive-$opt",
5307 ($drive->{mbps} || 0)*1024*1024,
5308 ($drive->{mbps_rd} || 0)*1024*1024,
5309 ($drive->{mbps_wr} || 0)*1024*1024,
5310 $drive->{iops} || 0,
5311 $drive->{iops_rd} || 0,
5312 $drive->{iops_wr} || 0,
5313 ($drive->{mbps_max} || 0)*1024*1024,
5314 ($drive->{mbps_rd_max} || 0)*1024*1024,
5315 ($drive->{mbps_wr_max} || 0)*1024*1024,
5316 $drive->{iops_max} || 0,
5317 $drive->{iops_rd_max} || 0,
5318 $drive->{iops_wr_max} || 0,
5319 $drive->{bps_max_length} || 1,
5320 $drive->{bps_rd_max_length} || 1,
5321 $drive->{bps_wr_max_length} || 1,
5322 $drive->{iops_max_length} || 1,
5323 $drive->{iops_rd_max_length} || 1,
5324 $drive->{iops_wr_max_length} || 1,
5325 );
a05cff86 5326
4df98f2f 5327 }
a1b7d579 5328
4df98f2f
TL
5329 return 1;
5330 }
4de1bb25 5331
4df98f2f 5332 } else { # cdrom
a1b7d579 5333
4df98f2f
TL
5334 if ($drive->{file} eq 'none') {
5335 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
5336 if (drive_is_cloudinit($old_drive)) {
5337 vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive);
5338 }
5339 } else {
5340 my $path = get_iso_path($storecfg, $vmid, $drive->{file});
ce9fce79 5341
4df98f2f
TL
5342 # force eject if locked
5343 mon_cmd($vmid, "eject", force => JSON::true, id => "$opt");
ce9fce79 5344
4df98f2f
TL
5345 if ($path) {
5346 mon_cmd($vmid, "blockdev-change-medium",
5347 id => "$opt", filename => "$path");
4de1bb25 5348 }
a05cff86 5349 }
4df98f2f
TL
5350
5351 return 1;
a05cff86
DM
5352 }
5353 }
5354
a1b7d579 5355 die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/;
4de1bb25 5356 # hotplug new disks
f7b4356f 5357 PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|;
d559309f 5358 vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive, $arch, $machine_type);
a05cff86
DM
5359}
5360
9687287b
AD
5361sub vmconfig_update_cloudinit_drive {
5362 my ($storecfg, $conf, $vmid) = @_;
5363
5364 my $cloudinit_ds = undef;
5365 my $cloudinit_drive = undef;
5366
5367 PVE::QemuConfig->foreach_volume($conf, sub {
5368 my ($ds, $drive) = @_;
5369 if (PVE::QemuServer::drive_is_cloudinit($drive)) {
5370 $cloudinit_ds = $ds;
5371 $cloudinit_drive = $drive;
5372 }
5373 });
5374
5375 return if !$cloudinit_drive;
5376
b137c30c
TL
5377 $conf->{cloudinit} = PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
5378 # FIXME: write out changed config here? needs to be sure that config is locked though!
9687287b
AD
5379 my $running = PVE::QemuServer::check_running($vmid);
5380
5381 if ($running) {
5382 my $path = PVE::Storage::path($storecfg, $cloudinit_drive->{file});
5383 if ($path) {
5384 mon_cmd($vmid, "eject", force => JSON::true, id => "$cloudinit_ds");
5385 mon_cmd($vmid, "blockdev-change-medium", id => "$cloudinit_ds", filename => "$path");
5386 }
5387 }
5388}
5389
13cfe3b7 5390# called in locked context by incoming migration
ba5396b5
FG
5391sub vm_migrate_get_nbd_disks {
5392 my ($storecfg, $conf, $replicated_volumes) = @_;
13cfe3b7
FG
5393
5394 my $local_volumes = {};
912792e2 5395 PVE::QemuConfig->foreach_volume($conf, sub {
13cfe3b7
FG
5396 my ($ds, $drive) = @_;
5397
5398 return if drive_is_cdrom($drive);
41c8671e 5399 return if $ds eq 'tpmstate0';
13cfe3b7
FG
5400
5401 my $volid = $drive->{file};
5402
5403 return if !$volid;
5404
5405 my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid);
5406
5407 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5408 return if $scfg->{shared};
ba5396b5
FG
5409
5410 # replicated disks re-use existing state via bitmap
5411 my $use_existing = $replicated_volumes->{$volid} ? 1 : 0;
5412 $local_volumes->{$ds} = [$volid, $storeid, $volname, $drive, $use_existing];
13cfe3b7 5413 });
ba5396b5
FG
5414 return $local_volumes;
5415}
5416
5417# called in locked context by incoming migration
5418sub vm_migrate_alloc_nbd_disks {
5419 my ($storecfg, $vmid, $source_volumes, $storagemap) = @_;
13cfe3b7 5420
13cfe3b7 5421 my $nbd = {};
ba5396b5 5422 foreach my $opt (sort keys %$source_volumes) {
5668463b 5423 my ($volid, $storeid, $volname, $drive, $use_existing, $format) = @{$source_volumes->{$opt}};
ba5396b5
FG
5424
5425 if ($use_existing) {
5426 $nbd->{$opt}->{drivestr} = print_drive($drive);
5427 $nbd->{$opt}->{volid} = $volid;
5428 $nbd->{$opt}->{replicated} = 1;
13cfe3b7
FG
5429 next;
5430 }
13cfe3b7 5431
5668463b
FG
5432 # storage mapping + volname = regular migration
5433 # storage mapping + format = remote migration
5434 # order of precedence, filtered by whether storage supports it:
5435 # 1. explicit requested format
5436 # 2. format of current volume
5437 # 3. default format of storage
bf8fc5a3 5438 if (!$storagemap->{identity}) {
82a03671 5439 $storeid = PVE::JSONSchema::map_id($storagemap, $storeid);
13cfe3b7 5440 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
5668463b
FG
5441 if (!$format || !grep { $format eq $_ } @$validFormats) {
5442 if ($volname) {
5443 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5444 my $fileFormat = qemu_img_format($scfg, $volname);
5445 $format = $fileFormat
5446 if grep { $fileFormat eq $_ } @$validFormats;
5447 }
5448 $format //= $defFormat;
5449 }
13cfe3b7 5450 } else {
5668463b 5451 # can't happen for remote migration, so $volname is always defined
13cfe3b7
FG
5452 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
5453 $format = qemu_img_format($scfg, $volname);
5454 }
5455
4df98f2f
TL
5456 my $size = $drive->{size} / 1024;
5457 my $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $format, undef, $size);
13cfe3b7
FG
5458 my $newdrive = $drive;
5459 $newdrive->{format} = $format;
5460 $newdrive->{file} = $newvolid;
5461 my $drivestr = print_drive($newdrive);
ba5396b5
FG
5462 $nbd->{$opt}->{drivestr} = $drivestr;
5463 $nbd->{$opt}->{volid} = $newvolid;
13cfe3b7
FG
5464 }
5465
5466 return $nbd;
5467}
5468
5469# see vm_start_nolock for parameters, additionally:
5470# migrate_opts:
bf8fc5a3 5471# storagemap = parsed storage map for allocating NBD disks
3898a563
FG
5472sub vm_start {
5473 my ($storecfg, $vmid, $params, $migrate_opts) = @_;
5474
84da8217 5475 return PVE::QemuConfig->lock_config($vmid, sub {
3898a563
FG
5476 my $conf = PVE::QemuConfig->load_config($vmid, $migrate_opts->{migratedfrom});
5477
4ef13a7f
FG
5478 die "you can't start a vm if it's a template\n"
5479 if !$params->{skiptemplate} && PVE::QemuConfig->is_template($conf);
3898a563 5480
d544e0e0 5481 my $has_suspended_lock = PVE::QemuConfig->has_lock($conf, 'suspended');
8e0c97bb
SR
5482 my $has_backup_lock = PVE::QemuConfig->has_lock($conf, 'backup');
5483
5484 my $running = check_running($vmid, undef, $migrate_opts->{migratedfrom});
5485
5486 if ($has_backup_lock && $running) {
5487 # a backup is currently running, attempt to start the guest in the
5488 # existing QEMU instance
5489 return vm_resume($vmid);
5490 }
3898a563
FG
5491
5492 PVE::QemuConfig->check_lock($conf)
d544e0e0
FE
5493 if !($params->{skiplock} || $has_suspended_lock);
5494
5495 $params->{resume} = $has_suspended_lock || defined($conf->{vmstate});
3898a563 5496
8e0c97bb 5497 die "VM $vmid already running\n" if $running;
3898a563 5498
ba5396b5
FG
5499 if (my $storagemap = $migrate_opts->{storagemap}) {
5500 my $replicated = $migrate_opts->{replicated_volumes};
5501 my $disks = vm_migrate_get_nbd_disks($storecfg, $conf, $replicated);
5502 $migrate_opts->{nbd} = vm_migrate_alloc_nbd_disks($storecfg, $vmid, $disks, $storagemap);
5503
5504 foreach my $opt (keys %{$migrate_opts->{nbd}}) {
5505 $conf->{$opt} = $migrate_opts->{nbd}->{$opt}->{drivestr};
5506 }
5507 }
13cfe3b7 5508
84da8217 5509 return vm_start_nolock($storecfg, $vmid, $conf, $params, $migrate_opts);
3898a563
FG
5510 });
5511}
5512
5513
0c498cca
FG
5514# params:
5515# statefile => 'tcp', 'unix' for migration or path/volid for RAM state
5516# skiplock => 0/1, skip checking for config lock
4ef13a7f 5517# skiptemplate => 0/1, skip checking whether VM is template
0c498cca 5518# forcemachine => to force Qemu machine (rollback/migration)
58c64ad5 5519# forcecpu => a QEMU '-cpu' argument string to override get_cpu_options
0c498cca
FG
5520# timeout => in seconds
5521# paused => start VM in paused state (backup)
3898a563 5522# resume => resume from hibernation
5921764c
SR
5523# pbs-backing => {
5524# sata0 => {
5525# repository
5526# snapshot
5527# keyfile
5528# archive
5529# },
5530# virtio2 => ...
5531# }
0c498cca 5532# migrate_opts:
ba5396b5 5533# nbd => volumes for NBD exports (vm_migrate_alloc_nbd_disks)
0c498cca
FG
5534# migratedfrom => source node
5535# spice_ticket => used for spice migration, passed via tunnel/stdin
5536# network => CIDR of migration network
5537# type => secure/insecure - tunnel over encrypted connection or plain-text
0c498cca 5538# nbd_proto_version => int, 0 for TCP, 1 for UNIX
fd95d780 5539# replicated_volumes => which volids should be re-used with bitmaps for nbd migration
13d121d7
FE
5540# offline_volumes => new volids of offline migrated disks like tpmstate and cloudinit, not yet
5541# contained in config
3898a563
FG
5542sub vm_start_nolock {
5543 my ($storecfg, $vmid, $conf, $params, $migrate_opts) = @_;
1e3baf05 5544
3898a563
FG
5545 my $statefile = $params->{statefile};
5546 my $resume = $params->{resume};
3dcb98d5 5547
3898a563
FG
5548 my $migratedfrom = $migrate_opts->{migratedfrom};
5549 my $migration_type = $migrate_opts->{type};
7ceade4c 5550
84da8217
FG
5551 my $res = {};
5552
3898a563
FG
5553 # clean up leftover reboot request files
5554 eval { clear_reboot_request($vmid); };
5555 warn $@ if $@;
1e3baf05 5556
3898a563
FG
5557 if (!$statefile && scalar(keys %{$conf->{pending}})) {
5558 vmconfig_apply_pending($vmid, $conf, $storecfg);
5559 $conf = PVE::QemuConfig->load_config($vmid); # update/reload
b137c30c
TL
5560 } elsif (!$migratedfrom) {
5561 # don't regenerate the ISO if the VM is started as part of a live migration
5562 # this way we can reuse the old ISO with the correct config
5563 $conf->{cloudinit} = PVE::QemuServer::Cloudinit::generate_cloudinitconfig($conf, $vmid);
5564 # FIXME: write out changed config here? if any changes
3898a563 5565 }
64457ed4 5566
13d121d7
FE
5567 # override offline migrated volumes, conf is out of date still
5568 if (my $offline_volumes = $migrate_opts->{offline_volumes}) {
5569 for my $key (sort keys $offline_volumes->%*) {
5570 my $parsed = parse_drive($key, $conf->{$key});
5571 $parsed->{file} = $offline_volumes->{$key};
5572 $conf->{$key} = print_drive($parsed);
5573 }
fd95d780
FG
5574 }
5575
3898a563 5576 my $defaults = load_defaults();
0c9a7596 5577
3898a563
FG
5578 # set environment variable useful inside network script
5579 $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom;
6c47d546 5580
3898a563 5581 PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-start', 1);
9e784b11 5582
3898a563 5583 my $forcemachine = $params->{forcemachine};
ea1c2110 5584 my $forcecpu = $params->{forcecpu};
3898a563 5585 if ($resume) {
ea1c2110 5586 # enforce machine and CPU type on suspended vm to ensure HW compatibility
3898a563 5587 $forcemachine = $conf->{runningmachine};
ea1c2110 5588 $forcecpu = $conf->{runningcpu};
3898a563
FG
5589 print "Resuming suspended VM\n";
5590 }
7ceade4c 5591
5921764c
SR
5592 my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid,
5593 $conf, $defaults, $forcemachine, $forcecpu, $params->{'pbs-backing'});
6c47d546 5594
3898a563
FG
5595 my $migration_ip;
5596 my $get_migration_ip = sub {
5597 my ($nodename) = @_;
b24e1ac2 5598
3898a563 5599 return $migration_ip if defined($migration_ip);
b24e1ac2 5600
3898a563 5601 my $cidr = $migrate_opts->{network};
0c498cca 5602
3898a563
FG
5603 if (!defined($cidr)) {
5604 my $dc_conf = PVE::Cluster::cfs_read_file('datacenter.cfg');
5605 $cidr = $dc_conf->{migration}->{network};
5606 }
b24e1ac2 5607
3898a563
FG
5608 if (defined($cidr)) {
5609 my $ips = PVE::Network::get_local_ip_from_cidr($cidr);
b24e1ac2 5610
3898a563
FG
5611 die "could not get IP: no address configured on local " .
5612 "node for network '$cidr'\n" if scalar(@$ips) == 0;
b24e1ac2 5613
3898a563
FG
5614 die "could not get IP: multiple addresses configured on local " .
5615 "node for network '$cidr'\n" if scalar(@$ips) > 1;
b24e1ac2 5616
3898a563
FG
5617 $migration_ip = @$ips[0];
5618 }
b24e1ac2 5619
3898a563
FG
5620 $migration_ip = PVE::Cluster::remote_node_ip($nodename, 1)
5621 if !defined($migration_ip);
b24e1ac2 5622
3898a563
FG
5623 return $migration_ip;
5624 };
b24e1ac2 5625
3898a563
FG
5626 my $migrate_uri;
5627 if ($statefile) {
5628 if ($statefile eq 'tcp') {
5629 my $localip = "localhost";
5630 my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter.cfg');
5631 my $nodename = nodename();
2de2d6f7 5632
3898a563
FG
5633 if (!defined($migration_type)) {
5634 if (defined($datacenterconf->{migration}->{type})) {
5635 $migration_type = $datacenterconf->{migration}->{type};
5636 } else {
5637 $migration_type = 'secure';
b7a5a225 5638 }
3898a563 5639 }
b7a5a225 5640
3898a563
FG
5641 if ($migration_type eq 'insecure') {
5642 $localip = $get_migration_ip->($nodename);
5643 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5644 }
2de2d6f7 5645
3898a563
FG
5646 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5647 my $migrate_port = PVE::Tools::next_migrate_port($pfamily);
5648 $migrate_uri = "tcp:${localip}:${migrate_port}";
5649 push @$cmd, '-incoming', $migrate_uri;
5650 push @$cmd, '-S';
1c9d54bf 5651
3898a563
FG
5652 } elsif ($statefile eq 'unix') {
5653 # should be default for secure migrations as a ssh TCP forward
5654 # tunnel is not deterministic reliable ready and fails regurarly
5655 # to set up in time, so use UNIX socket forwards
5656 my $socket_addr = "/run/qemu-server/$vmid.migrate";
5657 unlink $socket_addr;
54323eed 5658
3898a563 5659 $migrate_uri = "unix:$socket_addr";
1c9d54bf 5660
3898a563
FG
5661 push @$cmd, '-incoming', $migrate_uri;
5662 push @$cmd, '-S';
1c9d54bf 5663
3898a563
FG
5664 } elsif (-e $statefile) {
5665 push @$cmd, '-loadstate', $statefile;
5666 } else {
5667 my $statepath = PVE::Storage::path($storecfg, $statefile);
5668 push @$vollist, $statefile;
5669 push @$cmd, '-loadstate', $statepath;
5670 }
5671 } elsif ($params->{paused}) {
5672 push @$cmd, '-S';
5673 }
5674
1fb1822e
DC
5675 my $start_timeout = $params->{timeout} // config_aware_timeout($conf, $resume);
5676
5677 my $pci_devices = {}; # host pci devices
74c17b7a 5678 for (my $i = 0; $i < $PVE::QemuServer::PCI::MAX_HOSTPCI_DEVICES; $i++) {
1fb1822e
DC
5679 my $dev = $conf->{"hostpci$i"} or next;
5680 $pci_devices->{$i} = parse_hostpci($dev);
5681 }
5682
9c85548f
DC
5683 # do not reserve pciid for mediated devices, sysfs will error out for duplicate assignment
5684 my $real_pci_devices = [ grep { !(defined($_->{mdev}) && scalar($_->{pciid}->@*) == 1) } values $pci_devices->%* ];
5685
5686 # map to a flat list of pci ids
5687 my $pci_id_list = [ map { $_->{id} } map { $_->{pciid}->@* } $real_pci_devices->@* ];
5688
1fb1822e
DC
5689 # reserve all PCI IDs before actually doing anything with them
5690 PVE::QemuServer::PCI::reserve_pci_usage($pci_id_list, $vmid, $start_timeout);
5691
5692 eval {
bbf96e0f 5693 my $uuid;
1fb1822e
DC
5694 for my $id (sort keys %$pci_devices) {
5695 my $d = $pci_devices->{$id};
5696 for my $dev ($d->{pciid}->@*) {
bbf96e0f
DC
5697 my $info = PVE::QemuServer::PCI::prepare_pci_device($vmid, $dev->{id}, $id, $d->{mdev});
5698
5699 # nvidia grid needs the uuid of the mdev as qemu parameter
5700 if ($d->{mdev} && !defined($uuid) && $info->{vendor} eq '10de') {
5701 $uuid = PVE::QemuServer::PCI::generate_mdev_uuid($vmid, $id);
5702 }
1fb1822e
DC
5703 }
5704 }
bbf96e0f 5705 push @$cmd, '-uuid', $uuid if defined($uuid);
1fb1822e
DC
5706 };
5707 if (my $err = $@) {
1b189121 5708 eval { cleanup_pci_devices($vmid, $conf) };
1fb1822e
DC
5709 warn $@ if $@;
5710 die $err;
3898a563 5711 }
1e3baf05 5712
3898a563 5713 PVE::Storage::activate_volumes($storecfg, $vollist);
1e3baf05 5714
3898a563 5715 eval {
6bbcd71f 5716 run_command(['/bin/systemctl', 'stop', "$vmid.scope"], outfunc => sub{}, errfunc => sub{});
3898a563
FG
5717 };
5718 # Issues with the above 'stop' not being fully completed are extremely rare, a very low
5719 # timeout should be more than enough here...
39abafc8 5720 PVE::Systemd::wait_for_unit_removed("$vmid.scope", 20);
3898a563 5721
6b7ef5e5 5722 my $cpuunits = PVE::CGroup::clamp_cpu_shares($conf->{cpuunits});
3898a563 5723
3898a563
FG
5724 my %run_params = (
5725 timeout => $statefile ? undef : $start_timeout,
5726 umask => 0077,
5727 noerr => 1,
5728 );
1e3baf05 5729
3898a563
FG
5730 # when migrating, prefix QEMU output so other side can pick up any
5731 # errors that might occur and show the user
5732 if ($migratedfrom) {
5733 $run_params{quiet} = 1;
5734 $run_params{logfunc} = sub { print "QEMU: $_[0]\n" };
5735 }
8bf30c2a 5736
212220a4 5737 my %systemd_properties = (
3898a563 5738 Slice => 'qemu.slice',
354e61aa
SR
5739 KillMode => 'process',
5740 SendSIGKILL => 0,
5741 TimeoutStopUSec => ULONG_MAX, # infinity
3898a563 5742 );
7023f3ea 5743
6cbd3eb8 5744 if (PVE::CGroup::cgroup_mode() == 2) {
212220a4 5745 $systemd_properties{CPUWeight} = $cpuunits;
6cbd3eb8 5746 } else {
212220a4 5747 $systemd_properties{CPUShares} = $cpuunits;
6cbd3eb8
AD
5748 }
5749
3898a563 5750 if (my $cpulimit = $conf->{cpulimit}) {
212220a4 5751 $systemd_properties{CPUQuota} = int($cpulimit * 100);
3898a563 5752 }
212220a4 5753 $systemd_properties{timeout} = 10 if $statefile; # setting up the scope shoul be quick
7023f3ea 5754
3898a563
FG
5755 my $run_qemu = sub {
5756 PVE::Tools::run_fork sub {
212220a4 5757 PVE::Systemd::enter_systemd_scope($vmid, "Proxmox VE VM $vmid", %systemd_properties);
6e0216d8 5758
f9dde219
SR
5759 my $tpmpid;
5760 if (my $tpm = $conf->{tpmstate0}) {
5761 # start the TPM emulator so QEMU can connect on start
5762 $tpmpid = start_swtpm($storecfg, $vmid, $tpm, $migratedfrom);
5763 }
5764
3898a563 5765 my $exitcode = run_command($cmd, %run_params);
f9dde219 5766 if ($exitcode) {
23bee97d
FE
5767 if ($tpmpid) {
5768 warn "stopping swtpm instance (pid $tpmpid) due to QEMU startup error\n";
5769 kill 'TERM', $tpmpid;
5770 }
f9dde219
SR
5771 die "QEMU exited with code $exitcode\n";
5772 }
503308ed 5773 };
3898a563 5774 };
503308ed 5775
3898a563 5776 if ($conf->{hugepages}) {
7023f3ea 5777
3898a563
FG
5778 my $code = sub {
5779 my $hugepages_topology = PVE::QemuServer::Memory::hugepages_topology($conf);
5780 my $hugepages_host_topology = PVE::QemuServer::Memory::hugepages_host_topology();
7023f3ea 5781
3898a563
FG
5782 PVE::QemuServer::Memory::hugepages_mount();
5783 PVE::QemuServer::Memory::hugepages_allocate($hugepages_topology, $hugepages_host_topology);
7023f3ea 5784
503308ed 5785 eval { $run_qemu->() };
3898a563 5786 if (my $err = $@) {
f36e9894
SR
5787 PVE::QemuServer::Memory::hugepages_reset($hugepages_host_topology)
5788 if !$conf->{keephugepages};
3898a563
FG
5789 die $err;
5790 }
77cde36b 5791
f36e9894
SR
5792 PVE::QemuServer::Memory::hugepages_pre_deallocate($hugepages_topology)
5793 if !$conf->{keephugepages};
3898a563
FG
5794 };
5795 eval { PVE::QemuServer::Memory::hugepages_update_locked($code); };
1e3baf05 5796
3898a563
FG
5797 } else {
5798 eval { $run_qemu->() };
5799 }
afdb31d5 5800
3898a563
FG
5801 if (my $err = $@) {
5802 # deactivate volumes if start fails
5803 eval { PVE::Storage::deactivate_volumes($storecfg, $vollist); };
1b189121
DC
5804 warn $@ if $@;
5805 eval { cleanup_pci_devices($vmid, $conf) };
5806 warn $@ if $@;
1fb1822e 5807
3898a563
FG
5808 die "start failed: $err";
5809 }
62de2cbd 5810
1fb1822e
DC
5811 # re-reserve all PCI IDs now that we can know the actual VM PID
5812 my $pid = PVE::QemuServer::Helpers::vm_running_locally($vmid);
5813 eval { PVE::QemuServer::PCI::reserve_pci_usage($pci_id_list, $vmid, undef, $pid) };
5814 warn $@ if $@;
5815
3898a563 5816 print "migration listens on $migrate_uri\n" if $migrate_uri;
84da8217 5817 $res->{migrate_uri} = $migrate_uri;
eb8cddb5 5818
3898a563
FG
5819 if ($statefile && $statefile ne 'tcp' && $statefile ne 'unix') {
5820 eval { mon_cmd($vmid, "cont"); };
5821 warn $@ if $@;
5822 }
2189246c 5823
3898a563 5824 #start nbd server for storage migration
13cfe3b7 5825 if (my $nbd = $migrate_opts->{nbd}) {
3898a563 5826 my $nbd_protocol_version = $migrate_opts->{nbd_proto_version} // 0;
2189246c 5827
3898a563
FG
5828 my $migrate_storage_uri;
5829 # nbd_protocol_version > 0 for unix socket support
5830 if ($nbd_protocol_version > 0 && $migration_type eq 'secure') {
5831 my $socket_path = "/run/qemu-server/$vmid\_nbd.migrate";
5832 mon_cmd($vmid, "nbd-server-start", addr => { type => 'unix', data => { path => $socket_path } } );
5833 $migrate_storage_uri = "nbd:unix:$socket_path";
5834 } else {
5835 my $nodename = nodename();
5836 my $localip = $get_migration_ip->($nodename);
5837 my $pfamily = PVE::Tools::get_host_address_family($nodename);
5838 my $storage_migrate_port = PVE::Tools::next_migrate_port($pfamily);
5839
4df98f2f
TL
5840 mon_cmd($vmid, "nbd-server-start", addr => {
5841 type => 'inet',
5842 data => {
5843 host => "${localip}",
5844 port => "${storage_migrate_port}",
5845 },
5846 });
3898a563
FG
5847 $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip);
5848 $migrate_storage_uri = "nbd:${localip}:${storage_migrate_port}";
2189246c
AD
5849 }
5850
84da8217
FG
5851 $res->{migrate_storage_uri} = $migrate_storage_uri;
5852
13cfe3b7 5853 foreach my $opt (sort keys %$nbd) {
ba5396b5
FG
5854 my $drivestr = $nbd->{$opt}->{drivestr};
5855 my $volid = $nbd->{$opt}->{volid};
3898a563 5856 mon_cmd($vmid, "nbd-server-add", device => "drive-$opt", writable => JSON::true );
84da8217
FG
5857 my $nbd_uri = "$migrate_storage_uri:exportname=drive-$opt";
5858 print "storage migration listens on $nbd_uri volume:$drivestr\n";
ba5396b5
FG
5859 print "re-using replicated volume: $opt - $volid\n"
5860 if $nbd->{$opt}->{replicated};
84da8217
FG
5861
5862 $res->{drives}->{$opt} = $nbd->{$opt};
5863 $res->{drives}->{$opt}->{nbd_uri} = $nbd_uri;
3898a563
FG
5864 }
5865 }
a89fded1 5866
3898a563
FG
5867 if ($migratedfrom) {
5868 eval {
5869 set_migration_caps($vmid);
5870 };
5871 warn $@ if $@;
5872
5873 if ($spice_port) {
5874 print "spice listens on port $spice_port\n";
84da8217 5875 $res->{spice_port} = $spice_port;
3898a563 5876 if ($migrate_opts->{spice_ticket}) {
4df98f2f
TL
5877 mon_cmd($vmid, "set_password", protocol => 'spice', password =>
5878 $migrate_opts->{spice_ticket});
3898a563 5879 mon_cmd($vmid, "expire_password", protocol => 'spice', time => "+30");
95a4b4a9 5880 }
3898a563 5881 }
95a4b4a9 5882
3898a563
FG
5883 } else {
5884 mon_cmd($vmid, "balloon", value => $conf->{balloon}*1024*1024)
5885 if !$statefile && $conf->{balloon};
25088687 5886
3898a563
FG
5887 foreach my $opt (keys %$conf) {
5888 next if $opt !~ m/^net\d+$/;
5889 my $nicconf = parse_net($conf->{$opt});
5890 qemu_set_link_status($vmid, $opt, 0) if $nicconf->{link_down};
e18b0b99 5891 }
21947fea 5892 add_nets_bridge_fdb($conf, $vmid);
3898a563 5893 }
a1b7d579 5894
3898a563
FG
5895 mon_cmd($vmid, 'qom-set',
5896 path => "machine/peripheral/balloon0",
5897 property => "guest-stats-polling-interval",
5898 value => 2) if (!defined($conf->{balloon}) || $conf->{balloon});
eb065317 5899
3898a563
FG
5900 if ($resume) {
5901 print "Resumed VM, removing state\n";
5902 if (my $vmstate = $conf->{vmstate}) {
5903 PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
5904 PVE::Storage::vdisk_free($storecfg, $vmstate);
7ceade4c 5905 }
ea1c2110 5906 delete $conf->@{qw(lock vmstate runningmachine runningcpu)};
3898a563
FG
5907 PVE::QemuConfig->write_config($vmid, $conf);
5908 }
7ceade4c 5909
3898a563 5910 PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'post-start');
84da8217
FG
5911
5912 return $res;
1e3baf05
DM
5913}
5914
1e3baf05 5915sub vm_commandline {
b14477e7 5916 my ($storecfg, $vmid, $snapname) = @_;
1e3baf05 5917
ffda963f 5918 my $conf = PVE::QemuConfig->load_config($vmid);
1e3baf05 5919
e8a26810 5920 my ($forcemachine, $forcecpu);
b14477e7
RV
5921 if ($snapname) {
5922 my $snapshot = $conf->{snapshots}->{$snapname};
87d92707
TL
5923 die "snapshot '$snapname' does not exist\n" if !defined($snapshot);
5924
ea1c2110
SR
5925 # check for machine or CPU overrides in snapshot
5926 $forcemachine = $snapshot->{runningmachine};
5927 $forcecpu = $snapshot->{runningcpu};
092868c4 5928
87d92707 5929 $snapshot->{digest} = $conf->{digest}; # keep file digest for API
b14477e7 5930
b14477e7
RV
5931 $conf = $snapshot;
5932 }
5933
1e3baf05
DM
5934 my $defaults = load_defaults();
5935
e8a26810 5936 my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu);
1e3baf05 5937
5930c1ff 5938 return PVE::Tools::cmd2string($cmd);
1e3baf05
DM
5939}
5940
5941sub vm_reset {
5942 my ($vmid, $skiplock) = @_;
5943
ffda963f 5944 PVE::QemuConfig->lock_config($vmid, sub {
1e3baf05 5945
ffda963f 5946 my $conf = PVE::QemuConfig->load_config($vmid);
1e3baf05 5947
ffda963f 5948 PVE::QemuConfig->check_lock($conf) if !$skiplock;
1e3baf05 5949
0a13e08e 5950 mon_cmd($vmid, "system_reset");
ff1a2432
DM
5951 });
5952}
5953
5954sub get_vm_volumes {
5955 my ($conf) = @_;
1e3baf05 5956
ff1a2432 5957 my $vollist = [];
d5769dc2 5958 foreach_volid($conf, sub {
392f8b5d 5959 my ($volid, $attr) = @_;
ff1a2432 5960
d5769dc2 5961 return if $volid =~ m|^/|;
ff1a2432 5962
d5769dc2
DM
5963 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
5964 return if !$sid;
ff1a2432
DM
5965
5966 push @$vollist, $volid;
1e3baf05 5967 });
ff1a2432
DM
5968
5969 return $vollist;
5970}
5971
1b189121
DC
5972sub cleanup_pci_devices {
5973 my ($vmid, $conf) = @_;
5974
5975 foreach my $key (keys %$conf) {
5976 next if $key !~ m/^hostpci(\d+)$/;
5977 my $hostpciindex = $1;
5978 my $uuid = PVE::SysFSTools::generate_mdev_uuid($vmid, $hostpciindex);
5979 my $d = parse_hostpci($conf->{$key});
faf72d6c
TL
5980 if ($d->{mdev}) {
5981 # NOTE: avoid PVE::SysFSTools::pci_cleanup_mdev_device as it requires PCI ID and we
5982 # don't want to break ABI just for this two liner
5983 my $dev_sysfs_dir = "/sys/bus/mdev/devices/$uuid";
5984 PVE::SysFSTools::file_write("$dev_sysfs_dir/remove", "1") if -e $dev_sysfs_dir;
5985 }
1b189121
DC
5986 }
5987 PVE::QemuServer::PCI::remove_pci_reservation($vmid);
5988}
5989
ff1a2432 5990sub vm_stop_cleanup {
70b04821 5991 my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
ff1a2432 5992
745fed70 5993 eval {
ff1a2432 5994
254575e9
DM
5995 if (!$keepActive) {
5996 my $vollist = get_vm_volumes($conf);
5997 PVE::Storage::deactivate_volumes($storecfg, $vollist);
f9dde219
SR
5998
5999 if (my $tpmdrive = $conf->{tpmstate0}) {
6000 my $tpm = parse_drive("tpmstate0", $tpmdrive);
6001 my ($storeid, $volname) = PVE::Storage::parse_volume_id($tpm->{file}, 1);
6002 if ($storeid) {
6003 PVE::Storage::unmap_volume($storecfg, $tpm->{file});
6004 }
6005 }
254575e9 6006 }
a1b7d579 6007
ab6a046f 6008 foreach my $ext (qw(mon qmp pid vnc qga)) {
961bfcb2
DM
6009 unlink "/var/run/qemu-server/${vmid}.$ext";
6010 }
a1b7d579 6011
6dbcb073 6012 if ($conf->{ivshmem}) {
4df98f2f 6013 my $ivshmem = parse_property_string($ivshmem_fmt, $conf->{ivshmem});
4c5a6a24
TL
6014 # just delete it for now, VMs which have this already open do not
6015 # are affected, but new VMs will get a separated one. If this
6016 # becomes an issue we either add some sort of ref-counting or just
6017 # add a "don't delete on stop" flag to the ivshmem format.
6dbcb073
DC
6018 unlink '/dev/shm/pve-shm-' . ($ivshmem->{name} // $vmid);
6019 }
6020
1b189121 6021 cleanup_pci_devices($vmid, $conf);
6ab45bd7 6022
70b04821 6023 vmconfig_apply_pending($vmid, $conf, $storecfg) if $apply_pending_changes;
745fed70
DM
6024 };
6025 warn $@ if $@; # avoid errors - just warn
1e3baf05
DM
6026}
6027
575d19da
DC
6028# call only in locked context
6029sub _do_vm_stop {
6030 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive) = @_;
9269013a 6031
575d19da
DC
6032 my $pid = check_running($vmid, $nocheck);
6033 return if !$pid;
1e3baf05 6034
575d19da
DC
6035 my $conf;
6036 if (!$nocheck) {
6037 $conf = PVE::QemuConfig->load_config($vmid);
6038 PVE::QemuConfig->check_lock($conf) if !$skiplock;
6039 if (!defined($timeout) && $shutdown && $conf->{startup}) {
6040 my $opts = PVE::JSONSchema::pve_parse_startup_order($conf->{startup});
6041 $timeout = $opts->{down} if $opts->{down};
e6c3b671 6042 }
575d19da
DC
6043 PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'pre-stop');
6044 }
19672434 6045
575d19da
DC
6046 eval {
6047 if ($shutdown) {
a2af1bbe 6048 if (defined($conf) && get_qga_key($conf, 'enabled')) {
0a13e08e 6049 mon_cmd($vmid, "guest-shutdown", timeout => $timeout);
9269013a 6050 } else {
0a13e08e 6051 mon_cmd($vmid, "system_powerdown");
1e3baf05
DM
6052 }
6053 } else {
0a13e08e 6054 mon_cmd($vmid, "quit");
1e3baf05 6055 }
575d19da
DC
6056 };
6057 my $err = $@;
1e3baf05 6058
575d19da
DC
6059 if (!$err) {
6060 $timeout = 60 if !defined($timeout);
1e3baf05
DM
6061
6062 my $count = 0;
e6c3b671 6063 while (($count < $timeout) && check_running($vmid, $nocheck)) {
1e3baf05
DM
6064 $count++;
6065 sleep 1;
6066 }
6067
6068 if ($count >= $timeout) {
575d19da
DC
6069 if ($force) {
6070 warn "VM still running - terminating now with SIGTERM\n";
6071 kill 15, $pid;
6072 } else {
6073 die "VM quit/powerdown failed - got timeout\n";
6074 }
6075 } else {
6076 vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6077 return;
1e3baf05 6078 }
575d19da 6079 } else {
d60cbb97
TL
6080 if (!check_running($vmid, $nocheck)) {
6081 warn "Unexpected: VM shutdown command failed, but VM not running anymore..\n";
6082 return;
6083 }
6084 if ($force) {
575d19da
DC
6085 warn "VM quit/powerdown failed - terminating now with SIGTERM\n";
6086 kill 15, $pid;
6087 } else {
6088 die "VM quit/powerdown failed\n";
6089 }
6090 }
6091
6092 # wait again
6093 $timeout = 10;
6094
6095 my $count = 0;
6096 while (($count < $timeout) && check_running($vmid, $nocheck)) {
6097 $count++;
6098 sleep 1;
6099 }
6100
6101 if ($count >= $timeout) {
6102 warn "VM still running - terminating now with SIGKILL\n";
6103 kill 9, $pid;
6104 sleep 1;
6105 }
1e3baf05 6106
575d19da
DC
6107 vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 1) if $conf;
6108}
6109
6110# Note: use $nocheck to skip tests if VM configuration file exists.
6111# We need that when migration VMs to other nodes (files already moved)
6112# Note: we set $keepActive in vzdump stop mode - volumes need to stay active
6113sub vm_stop {
6114 my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_;
6115
6116 $force = 1 if !defined($force) && !$shutdown;
6117
6118 if ($migratedfrom){
6119 my $pid = check_running($vmid, $nocheck, $migratedfrom);
6120 kill 15, $pid if $pid;
6121 my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom);
6122 vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 0);
6123 return;
6124 }
6125
6126 PVE::QemuConfig->lock_config($vmid, sub {
6127 _do_vm_stop($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive);
ff1a2432 6128 });
1e3baf05
DM
6129}
6130
165411f0
DC
6131sub vm_reboot {
6132 my ($vmid, $timeout) = @_;
6133
6134 PVE::QemuConfig->lock_config($vmid, sub {
66026117 6135 eval {
165411f0 6136
66026117
OB
6137 # only reboot if running, as qmeventd starts it again on a stop event
6138 return if !check_running($vmid);
165411f0 6139
66026117 6140 create_reboot_request($vmid);
165411f0 6141
66026117
OB
6142 my $storecfg = PVE::Storage::config();
6143 _do_vm_stop($storecfg, $vmid, undef, undef, $timeout, 1);
165411f0 6144
66026117
OB
6145 };
6146 if (my $err = $@) {
3c1c3fe6 6147 # avoid that the next normal shutdown will be confused for a reboot
66026117
OB
6148 clear_reboot_request($vmid);
6149 die $err;
6150 }
165411f0
DC
6151 });
6152}
6153
75c24bba 6154# note: if using the statestorage parameter, the caller has to check privileges
1e3baf05 6155sub vm_suspend {
48b4cdc2 6156 my ($vmid, $skiplock, $includestate, $statestorage) = @_;
159719e5
DC
6157
6158 my $conf;
6159 my $path;
6160 my $storecfg;
6161 my $vmstate;
1e3baf05 6162
ffda963f 6163 PVE::QemuConfig->lock_config($vmid, sub {
1e3baf05 6164
159719e5 6165 $conf = PVE::QemuConfig->load_config($vmid);
1e3baf05 6166
159719e5 6167 my $is_backing_up = PVE::QemuConfig->has_lock($conf, 'backup');
e79706d4 6168 PVE::QemuConfig->check_lock($conf)
159719e5
DC
6169 if !($skiplock || $is_backing_up);
6170
6171 die "cannot suspend to disk during backup\n"
6172 if $is_backing_up && $includestate;
bcb7c9cf 6173
159719e5
DC
6174 if ($includestate) {
6175 $conf->{lock} = 'suspending';
6176 my $date = strftime("%Y-%m-%d", localtime(time()));
6177 $storecfg = PVE::Storage::config();
75c24bba
DC
6178 if (!$statestorage) {
6179 $statestorage = find_vmstate_storage($conf, $storecfg);
6180 # check permissions for the storage
6181 my $rpcenv = PVE::RPCEnvironment::get();
6182 if ($rpcenv->{type} ne 'cli') {
6183 my $authuser = $rpcenv->get_user();
6184 $rpcenv->check($authuser, "/storage/$statestorage", ['Datastore.AllocateSpace']);
6185 }
6186 }
6187
6188
4df98f2f
TL
6189 $vmstate = PVE::QemuConfig->__snapshot_save_vmstate(
6190 $vmid, $conf, "suspend-$date", $storecfg, $statestorage, 1);
159719e5
DC
6191 $path = PVE::Storage::path($storecfg, $vmstate);
6192 PVE::QemuConfig->write_config($vmid, $conf);
6193 } else {
0a13e08e 6194 mon_cmd($vmid, "stop");
159719e5 6195 }
1e3baf05 6196 });
159719e5
DC
6197
6198 if ($includestate) {
6199 # save vm state
6200 PVE::Storage::activate_volumes($storecfg, [$vmstate]);
6201
6202 eval {
27a5be53 6203 set_migration_caps($vmid, 1);
0a13e08e 6204 mon_cmd($vmid, "savevm-start", statefile => $path);
159719e5 6205 for(;;) {
0a13e08e 6206 my $state = mon_cmd($vmid, "query-savevm");
159719e5
DC
6207 if (!$state->{status}) {
6208 die "savevm not active\n";
6209 } elsif ($state->{status} eq 'active') {
6210 sleep(1);
6211 next;
6212 } elsif ($state->{status} eq 'completed') {
b0a9a385 6213 print "State saved, quitting\n";
159719e5
DC
6214 last;
6215 } elsif ($state->{status} eq 'failed' && $state->{error}) {
6216 die "query-savevm failed with error '$state->{error}'\n"
6217 } else {
6218 die "query-savevm returned status '$state->{status}'\n";
6219 }
6220 }
6221 };
6222 my $err = $@;
6223
6224 PVE::QemuConfig->lock_config($vmid, sub {
6225 $conf = PVE::QemuConfig->load_config($vmid);
6226 if ($err) {
6227 # cleanup, but leave suspending lock, to indicate something went wrong
6228 eval {
0a13e08e 6229 mon_cmd($vmid, "savevm-end");
159719e5
DC
6230 PVE::Storage::deactivate_volumes($storecfg, [$vmstate]);
6231 PVE::Storage::vdisk_free($storecfg, $vmstate);
ea1c2110 6232 delete $conf->@{qw(vmstate runningmachine runningcpu)};
159719e5
DC
6233 PVE::QemuConfig->write_config($vmid, $conf);
6234 };
6235 warn $@ if $@;
6236 die $err;
6237 }
6238
6239 die "lock changed unexpectedly\n"
6240 if !PVE::QemuConfig->has_lock($conf, 'suspending');
6241
0a13e08e 6242 mon_cmd($vmid, "quit");
159719e5
DC
6243 $conf->{lock} = 'suspended';
6244 PVE::QemuConfig->write_config($vmid, $conf);
6245 });
6246 }
1e3baf05
DM
6247}
6248
6249sub vm_resume {
289e0b85 6250 my ($vmid, $skiplock, $nocheck) = @_;
1e3baf05 6251
ffda963f 6252 PVE::QemuConfig->lock_config($vmid, sub {
0a13e08e 6253 my $res = mon_cmd($vmid, 'query-status');
c2786bed 6254 my $resume_cmd = 'cont';
8e0c97bb 6255 my $reset = 0;
21947fea 6256 my $conf = PVE::QemuConfig->load_config($vmid);
c2786bed 6257
8e0c97bb
SR
6258 if ($res->{status}) {
6259 return if $res->{status} eq 'running'; # job done, go home
6260 $resume_cmd = 'system_wakeup' if $res->{status} eq 'suspended';
6261 $reset = 1 if $res->{status} eq 'shutdown';
c2786bed
DC
6262 }
6263
289e0b85 6264 if (!$nocheck) {
1e3baf05 6265
e79706d4
FG
6266 PVE::QemuConfig->check_lock($conf)
6267 if !($skiplock || PVE::QemuConfig->has_lock($conf, 'backup'));
289e0b85 6268 }
3e24733b 6269
8e0c97bb
SR
6270 if ($reset) {
6271 # required if a VM shuts down during a backup and we get a resume
6272 # request before the backup finishes for example
6273 mon_cmd($vmid, "system_reset");
6274 }
21947fea
AD
6275
6276 add_nets_bridge_fdb($conf, $vmid) if $resume_cmd eq 'cont';
6277
0a13e08e 6278 mon_cmd($vmid, $resume_cmd);
1e3baf05
DM
6279 });
6280}
6281
5fdbe4f0
DM
6282sub vm_sendkey {
6283 my ($vmid, $skiplock, $key) = @_;
1e3baf05 6284
ffda963f 6285 PVE::QemuConfig->lock_config($vmid, sub {
1e3baf05 6286
ffda963f 6287 my $conf = PVE::QemuConfig->load_config($vmid);
f5eb281a 6288
7b7c6d1b 6289 # there is no qmp command, so we use the human monitor command
0a13e08e 6290 my $res = PVE::QemuServer::Monitor::hmp_cmd($vmid, "sendkey $key");
d30820d6 6291 die $res if $res ne '';
1e3baf05
DM
6292 });
6293}
6294
3e16d5fc
DM
6295# vzdump restore implementaion
6296
ed221350 6297sub tar_archive_read_firstfile {
3e16d5fc 6298 my $archive = shift;
afdb31d5 6299
3e16d5fc
DM
6300 die "ERROR: file '$archive' does not exist\n" if ! -f $archive;
6301
6302 # try to detect archive type first
387ba257 6303 my $pid = open (my $fh, '-|', 'tar', 'tf', $archive) ||
3e16d5fc 6304 die "unable to open file '$archive'\n";
387ba257 6305 my $firstfile = <$fh>;
3e16d5fc 6306 kill 15, $pid;
387ba257 6307 close $fh;
3e16d5fc
DM
6308
6309 die "ERROR: archive contaions no data\n" if !$firstfile;
6310 chomp $firstfile;
6311
6312 return $firstfile;
6313}
6314
ed221350
DM
6315sub tar_restore_cleanup {
6316 my ($storecfg, $statfile) = @_;
3e16d5fc
DM
6317
6318 print STDERR "starting cleanup\n";
6319
6320 if (my $fd = IO::File->new($statfile, "r")) {
6321 while (defined(my $line = <$fd>)) {
6322 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
6323 my $volid = $2;
6324 eval {
6325 if ($volid =~ m|^/|) {
6326 unlink $volid || die 'unlink failed\n';
6327 } else {
ed221350 6328 PVE::Storage::vdisk_free($storecfg, $volid);
3e16d5fc 6329 }
afdb31d5 6330 print STDERR "temporary volume '$volid' sucessfuly removed\n";
3e16d5fc
DM
6331 };
6332 print STDERR "unable to cleanup '$volid' - $@" if $@;
6333 } else {
6334 print STDERR "unable to parse line in statfile - $line";
afdb31d5 6335 }
3e16d5fc
DM
6336 }
6337 $fd->close();
6338 }
6339}
6340
d1e92cf6 6341sub restore_file_archive {
a0d1b1a2 6342 my ($archive, $vmid, $user, $opts) = @_;
3e16d5fc 6343
a2ec5a67
FG
6344 return restore_vma_archive($archive, $vmid, $user, $opts)
6345 if $archive eq '-';
6346
c6d51783
AA
6347 my $info = PVE::Storage::archive_info($archive);
6348 my $format = $opts->{format} // $info->{format};
6349 my $comp = $info->{compression};
91bd6c90
DM
6350
6351 # try to detect archive format
6352 if ($format eq 'tar') {
6353 return restore_tar_archive($archive, $vmid, $user, $opts);
6354 } else {
6355 return restore_vma_archive($archive, $vmid, $user, $opts, $comp);
6356 }
6357}
6358
d1e92cf6
DM
6359# hepler to remove disks that will not be used after restore
6360my $restore_cleanup_oldconf = sub {
6361 my ($storecfg, $vmid, $oldconf, $virtdev_hash) = @_;
6362
01a4377f
FE
6363 my $kept_disks = {};
6364
912792e2 6365 PVE::QemuConfig->foreach_volume($oldconf, sub {
d1e92cf6
DM
6366 my ($ds, $drive) = @_;
6367
6368 return if drive_is_cdrom($drive, 1);
6369
6370 my $volid = $drive->{file};
6371 return if !$volid || $volid =~ m|^/|;
6372
6373 my ($path, $owner) = PVE::Storage::path($storecfg, $volid);
6374 return if !$path || !$owner || ($owner != $vmid);
6375
6376 # Note: only delete disk we want to restore
6377 # other volumes will become unused
6378 if ($virtdev_hash->{$ds}) {
6379 eval { PVE::Storage::vdisk_free($storecfg, $volid); };
6380 if (my $err = $@) {
6381 warn $err;
6382 }
01a4377f
FE
6383 } else {
6384 $kept_disks->{$volid} = 1;
d1e92cf6
DM
6385 }
6386 });
6387
01a4377f
FE
6388 # after the restore we have no snapshots anymore
6389 for my $snapname (keys $oldconf->{snapshots}->%*) {
d1e92cf6
DM
6390 my $snap = $oldconf->{snapshots}->{$snapname};
6391 if ($snap->{vmstate}) {
6392 eval { PVE::Storage::vdisk_free($storecfg, $snap->{vmstate}); };
6393 if (my $err = $@) {
6394 warn $err;
6395 }
6396 }
01a4377f
FE
6397
6398 for my $volid (keys $kept_disks->%*) {
6399 eval { PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snapname); };
6400 warn $@ if $@;
6401 }
d1e92cf6
DM
6402 }
6403};
6404
9f3d73bc
DM
6405# Helper to parse vzdump backup device hints
6406#
6407# $rpcenv: Environment, used to ckeck storage permissions
6408# $user: User ID, to check storage permissions
6409# $storecfg: Storage configuration
6410# $fh: the file handle for reading the configuration
6411# $devinfo: should contain device sizes for all backu-up'ed devices
6412# $options: backup options (pool, default storage)
6413#
6414# Return: $virtdev_hash, updates $devinfo (add devname, virtdev, format, storeid)
6415my $parse_backup_hints = sub {
6416 my ($rpcenv, $user, $storecfg, $fh, $devinfo, $options) = @_;
d1e92cf6 6417
36d4bdcb
TL
6418 my $check_storage = sub { # assert if an image can be allocate
6419 my ($storeid, $scfg) = @_;
6420 die "Content type 'images' is not available on storage '$storeid'\n"
6421 if !$scfg->{content}->{images};
6422 $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace'])
6423 if $user ne 'root@pam';
6424 };
d1e92cf6 6425
36d4bdcb 6426 my $virtdev_hash = {};
9f3d73bc
DM
6427 while (defined(my $line = <$fh>)) {
6428 if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) {
6429 my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4);
6430 die "archive does not contain data for drive '$virtdev'\n"
6431 if !$devinfo->{$devname};
6432
6433 if (defined($options->{storage})) {
6434 $storeid = $options->{storage} || 'local';
6435 } elsif (!$storeid) {
6436 $storeid = 'local';
d1e92cf6 6437 }
9f3d73bc
DM
6438 $format = 'raw' if !$format;
6439 $devinfo->{$devname}->{devname} = $devname;
6440 $devinfo->{$devname}->{virtdev} = $virtdev;
6441 $devinfo->{$devname}->{format} = $format;
6442 $devinfo->{$devname}->{storeid} = $storeid;
6443
62af60cd 6444 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
36d4bdcb 6445 $check_storage->($storeid, $scfg); # permission and content type check
d1e92cf6 6446
9f3d73bc
DM
6447 $virtdev_hash->{$virtdev} = $devinfo->{$devname};
6448 } elsif ($line =~ m/^((?:ide|sata|scsi)\d+):\s*(.*)\s*$/) {
6449 my $virtdev = $1;
6450 my $drive = parse_drive($virtdev, $2);
36d4bdcb 6451
9f3d73bc
DM
6452 if (drive_is_cloudinit($drive)) {
6453 my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
5364990d
TL
6454 $storeid = $options->{storage} if defined ($options->{storage});
6455 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
9f3d73bc 6456 my $format = qemu_img_format($scfg, $volname); # has 'raw' fallback
d1e92cf6 6457
36d4bdcb 6458 $check_storage->($storeid, $scfg); # permission and content type check
9f8ba326 6459
9f3d73bc
DM
6460 $virtdev_hash->{$virtdev} = {
6461 format => $format,
5364990d 6462 storeid => $storeid,
9f3d73bc
DM
6463 size => PVE::QemuServer::Cloudinit::CLOUDINIT_DISK_SIZE,
6464 is_cloudinit => 1,
6465 };
d1e92cf6 6466 }
9f3d73bc
DM
6467 }
6468 }
d1e92cf6 6469
9f3d73bc
DM
6470 return $virtdev_hash;
6471};
d1e92cf6 6472
9f3d73bc
DM
6473# Helper to allocate and activate all volumes required for a restore
6474#
6475# $storecfg: Storage configuration
6476# $virtdev_hash: as returned by parse_backup_hints()
6477#
6478# Returns: { $virtdev => $volid }
6479my $restore_allocate_devices = sub {
6480 my ($storecfg, $virtdev_hash, $vmid) = @_;
d1e92cf6 6481
9f3d73bc
DM
6482 my $map = {};
6483 foreach my $virtdev (sort keys %$virtdev_hash) {
6484 my $d = $virtdev_hash->{$virtdev};
6485 my $alloc_size = int(($d->{size} + 1024 - 1)/1024);
6486 my $storeid = $d->{storeid};
6487 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
d1e92cf6 6488
9f3d73bc
DM
6489 # test if requested format is supported
6490 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
6491 my $supported = grep { $_ eq $d->{format} } @$validFormats;
6492 $d->{format} = $defFormat if !$supported;
d1e92cf6 6493
9f3d73bc
DM
6494 my $name;
6495 if ($d->{is_cloudinit}) {
6496 $name = "vm-$vmid-cloudinit";
c997e24a
ML
6497 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
6498 if ($scfg->{path}) {
6499 $name .= ".$d->{format}";
6500 }
d1e92cf6
DM
6501 }
6502
4df98f2f
TL
6503 my $volid = PVE::Storage::vdisk_alloc(
6504 $storecfg, $storeid, $vmid, $d->{format}, $name, $alloc_size);
d1e92cf6 6505
9f3d73bc
DM
6506 print STDERR "new volume ID is '$volid'\n";
6507 $d->{volid} = $volid;
d1e92cf6 6508
9f3d73bc 6509 PVE::Storage::activate_volumes($storecfg, [$volid]);
d1e92cf6 6510
9f3d73bc 6511 $map->{$virtdev} = $volid;
d1e92cf6
DM
6512 }
6513
9f3d73bc
DM
6514 return $map;
6515};
d1e92cf6 6516
c62d7cf5 6517sub restore_update_config_line {
eabac302 6518 my ($cookie, $map, $line, $unique) = @_;
91bd6c90 6519
98a4b3fb
FE
6520 return '' if $line =~ m/^\#qmdump\#/;
6521 return '' if $line =~ m/^\#vzdump\#/;
6522 return '' if $line =~ m/^lock:/;
6523 return '' if $line =~ m/^unused\d+:/;
6524 return '' if $line =~ m/^parent:/;
6525
6526 my $res = '';
91bd6c90 6527
b5b99790 6528 my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
91bd6c90
DM
6529 if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) {
6530 # try to convert old 1.X settings
6531 my ($id, $ind, $ethcfg) = ($1, $2, $3);
6532 foreach my $devconfig (PVE::Tools::split_list($ethcfg)) {
6533 my ($model, $macaddr) = split(/\=/, $devconfig);
b5b99790 6534 $macaddr = PVE::Tools::random_ether_addr($dc->{mac_prefix}) if !$macaddr || $unique;
91bd6c90
DM
6535 my $net = {
6536 model => $model,
6537 bridge => "vmbr$ind",
6538 macaddr => $macaddr,
6539 };
6540 my $netstr = print_net($net);
6541
98a4b3fb 6542 $res .= "net$cookie->{netcount}: $netstr\n";
91bd6c90
DM
6543 $cookie->{netcount}++;
6544 }
6545 } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) {
6546 my ($id, $netstr) = ($1, $2);
6547 my $net = parse_net($netstr);
b5b99790 6548 $net->{macaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix}) if $net->{macaddr};
91bd6c90 6549 $netstr = print_net($net);
98a4b3fb 6550 $res .= "$id: $netstr\n";
f9dde219 6551 } elsif ($line =~ m/^((ide|scsi|virtio|sata|efidisk|tpmstate)\d+):\s*(\S+)\s*$/) {
91bd6c90 6552 my $virtdev = $1;
907ea891 6553 my $value = $3;
d9faf790
WB
6554 my $di = parse_drive($virtdev, $value);
6555 if (defined($di->{backup}) && !$di->{backup}) {
98a4b3fb 6556 $res .= "#$line";
c0f7406e 6557 } elsif ($map->{$virtdev}) {
8fd57431 6558 delete $di->{format}; # format can change on restore
91bd6c90 6559 $di->{file} = $map->{$virtdev};
71c58bb7 6560 $value = print_drive($di);
98a4b3fb 6561 $res .= "$virtdev: $value\n";
91bd6c90 6562 } else {
98a4b3fb 6563 $res .= $line;
91bd6c90 6564 }
1a0c2f03 6565 } elsif (($line =~ m/^vmgenid: (.*)/)) {
babecffe 6566 my $vmgenid = $1;
6ee499ff 6567 if ($vmgenid ne '0') {
1a0c2f03 6568 # always generate a new vmgenid if there was a valid one setup
6ee499ff
DC
6569 $vmgenid = generate_uuid();
6570 }
98a4b3fb 6571 $res .= "vmgenid: $vmgenid\n";
19a5dd55
WL
6572 } elsif (($line =~ m/^(smbios1: )(.*)/) && $unique) {
6573 my ($uuid, $uuid_str);
6574 UUID::generate($uuid);
6575 UUID::unparse($uuid, $uuid_str);
6576 my $smbios1 = parse_smbios1($2);
6577 $smbios1->{uuid} = $uuid_str;
98a4b3fb 6578 $res .= $1.print_smbios1($smbios1)."\n";
91bd6c90 6579 } else {
98a4b3fb 6580 $res .= $line;
91bd6c90 6581 }
98a4b3fb
FE
6582
6583 return $res;
c62d7cf5 6584}
9f3d73bc
DM
6585
6586my $restore_deactivate_volumes = sub {
e8b07b29 6587 my ($storecfg, $virtdev_hash) = @_;
9f3d73bc
DM
6588
6589 my $vollist = [];
e8b07b29
FE
6590 for my $dev (values $virtdev_hash->%*) {
6591 push $vollist->@*, $dev->{volid} if $dev->{volid};
9f3d73bc
DM
6592 }
6593
ff86112c
FE
6594 eval { PVE::Storage::deactivate_volumes($storecfg, $vollist); };
6595 print STDERR $@ if $@;
9f3d73bc
DM
6596};
6597
6598my $restore_destroy_volumes = sub {
e8b07b29 6599 my ($storecfg, $virtdev_hash) = @_;
9f3d73bc 6600
e8b07b29
FE
6601 for my $dev (values $virtdev_hash->%*) {
6602 my $volid = $dev->{volid} or next;
9f3d73bc 6603 eval {
e60afe82 6604 PVE::Storage::vdisk_free($storecfg, $volid);
9f3d73bc
DM
6605 print STDERR "temporary volume '$volid' sucessfuly removed\n";
6606 };
6607 print STDERR "unable to cleanup '$volid' - $@" if $@;
6608 }
6609};
91bd6c90 6610
202a2a0b
FE
6611my $restore_merge_config = sub {
6612 my ($filename, $backup_conf_raw, $override_conf) = @_;
6613
6614 my $backup_conf = parse_vm_config($filename, $backup_conf_raw);
6615 for my $key (keys $override_conf->%*) {
6616 $backup_conf->{$key} = $override_conf->{$key};
6617 }
6618
6619 return $backup_conf;
6620};
6621
91bd6c90 6622sub scan_volids {
9a8ba127 6623 my ($cfg, $vmid) = @_;
91bd6c90 6624
9a8ba127 6625 my $info = PVE::Storage::vdisk_list($cfg, undef, $vmid, undef, 'images');
91bd6c90
DM
6626
6627 my $volid_hash = {};
6628 foreach my $storeid (keys %$info) {
6629 foreach my $item (@{$info->{$storeid}}) {
6630 next if !($item->{volid} && $item->{size});
5996a936 6631 $item->{path} = PVE::Storage::path($cfg, $item->{volid});
91bd6c90
DM
6632 $volid_hash->{$item->{volid}} = $item;
6633 }
6634 }
6635
6636 return $volid_hash;
6637}
6638
68b108ee 6639sub update_disk_config {
91bd6c90 6640 my ($vmid, $conf, $volid_hash) = @_;
be190583 6641
91bd6c90 6642 my $changes;
9b29cbd0 6643 my $prefix = "VM $vmid";
91bd6c90 6644
c449137a
DC
6645 # used and unused disks
6646 my $referenced = {};
91bd6c90 6647
5996a936
DM
6648 # Note: it is allowed to define multiple storages with same path (alias), so
6649 # we need to check both 'volid' and real 'path' (two different volid can point
6650 # to the same path).
6651
c449137a 6652 my $referencedpath = {};
be190583 6653
91bd6c90 6654 # update size info
0c4fef3f 6655 PVE::QemuConfig->foreach_volume($conf, sub {
ca04977c 6656 my ($opt, $drive) = @_;
91bd6c90 6657
ca04977c
FE
6658 my $volid = $drive->{file};
6659 return if !$volid;
4df98f2f 6660 my $volume = $volid_hash->{$volid};
91bd6c90 6661
ca04977c
FE
6662 # mark volid as "in-use" for next step
6663 $referenced->{$volid} = 1;
4df98f2f 6664 if ($volume && (my $path = $volume->{path})) {
ca04977c 6665 $referencedpath->{$path} = 1;
91bd6c90 6666 }
ca04977c
FE
6667
6668 return if drive_is_cdrom($drive);
4df98f2f 6669 return if !$volume;
ca04977c 6670
4df98f2f 6671 my ($updated, $msg) = PVE::QemuServer::Drive::update_disksize($drive, $volume->{size});
ca04977c
FE
6672 if (defined($updated)) {
6673 $changes = 1;
6674 $conf->{$opt} = print_drive($updated);
9b29cbd0 6675 print "$prefix ($opt): $msg\n";
ca04977c
FE
6676 }
6677 });
91bd6c90 6678
5996a936 6679 # remove 'unusedX' entry if volume is used
ca04977c
FE
6680 PVE::QemuConfig->foreach_unused_volume($conf, sub {
6681 my ($opt, $drive) = @_;
6682
6683 my $volid = $drive->{file};
6684 return if !$volid;
6685
f7d1505b
TL
6686 my $path;
6687 $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid};
c449137a 6688 if ($referenced->{$volid} || ($path && $referencedpath->{$path})) {
68b108ee 6689 print "$prefix remove entry '$opt', its volume '$volid' is in use\n";
5996a936
DM
6690 $changes = 1;
6691 delete $conf->{$opt};
6692 }
c449137a
DC
6693
6694 $referenced->{$volid} = 1;
6695 $referencedpath->{$path} = 1 if $path;
ca04977c 6696 });
5996a936 6697
91bd6c90
DM
6698 foreach my $volid (sort keys %$volid_hash) {
6699 next if $volid =~ m/vm-$vmid-state-/;
c449137a 6700 next if $referenced->{$volid};
5996a936
DM
6701 my $path = $volid_hash->{$volid}->{path};
6702 next if !$path; # just to be sure
c449137a 6703 next if $referencedpath->{$path};
91bd6c90 6704 $changes = 1;
53b81297 6705 my $key = PVE::QemuConfig->add_unused_volume($conf, $volid);
68b108ee 6706 print "$prefix add unreferenced volume '$volid' as '$key' to config\n";
c449137a 6707 $referencedpath->{$path} = 1; # avoid to add more than once (aliases)
91bd6c90
DM
6708 }
6709
6710 return $changes;
6711}
6712
6713sub rescan {
9224dcee 6714 my ($vmid, $nolock, $dryrun) = @_;
91bd6c90 6715
20519efc 6716 my $cfg = PVE::Storage::config();
91bd6c90 6717
53b81297 6718 print "rescan volumes...\n";
9a8ba127 6719 my $volid_hash = scan_volids($cfg, $vmid);
91bd6c90
DM
6720
6721 my $updatefn = sub {
6722 my ($vmid) = @_;
6723
ffda963f 6724 my $conf = PVE::QemuConfig->load_config($vmid);
be190583 6725
ffda963f 6726 PVE::QemuConfig->check_lock($conf);
91bd6c90 6727
03da3f0d
DM
6728 my $vm_volids = {};
6729 foreach my $volid (keys %$volid_hash) {
6730 my $info = $volid_hash->{$volid};
6731 $vm_volids->{$volid} = $info if $info->{vmid} && $info->{vmid} == $vmid;
6732 }
6733
68b108ee 6734 my $changes = update_disk_config($vmid, $conf, $vm_volids);
91bd6c90 6735
9224dcee 6736 PVE::QemuConfig->write_config($vmid, $conf) if $changes && !$dryrun;
91bd6c90
DM
6737 };
6738
6739 if (defined($vmid)) {
6740 if ($nolock) {
6741 &$updatefn($vmid);
6742 } else {
ffda963f 6743 PVE::QemuConfig->lock_config($vmid, $updatefn, $vmid);
91bd6c90
DM
6744 }
6745 } else {
6746 my $vmlist = config_list();
6747 foreach my $vmid (keys %$vmlist) {
6748 if ($nolock) {
6749 &$updatefn($vmid);
6750 } else {
ffda963f 6751 PVE::QemuConfig->lock_config($vmid, $updatefn, $vmid);
be190583 6752 }
91bd6c90
DM
6753 }
6754 }
6755}
6756
9f3d73bc
DM
6757sub restore_proxmox_backup_archive {
6758 my ($archive, $vmid, $user, $options) = @_;
6759
6760 my $storecfg = PVE::Storage::config();
6761
6762 my ($storeid, $volname) = PVE::Storage::parse_volume_id($archive);
6763 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
6764
9f3d73bc 6765 my $fingerprint = $scfg->{fingerprint};
503e96f8 6766 my $keyfile = PVE::Storage::PBSPlugin::pbs_encryption_key_file_name($storecfg, $storeid);
9f3d73bc 6767
fbec3f89 6768 my $repo = PVE::PBSClient::get_repository($scfg);
2dda626d 6769 my $namespace = $scfg->{namespace};
dea4b04c 6770
26731a3c 6771 # This is only used for `pbs-restore` and the QEMU PBS driver (live-restore)
9f3d73bc
DM
6772 my $password = PVE::Storage::PBSPlugin::pbs_get_password($scfg, $storeid);
6773 local $ENV{PBS_PASSWORD} = $password;
6774 local $ENV{PBS_FINGERPRINT} = $fingerprint if defined($fingerprint);
6775
6776 my ($vtype, $pbs_backup_name, undef, undef, undef, undef, $format) =
6777 PVE::Storage::parse_volname($storecfg, $archive);
6778
6779 die "got unexpected vtype '$vtype'\n" if $vtype ne 'backup';
6780
6781 die "got unexpected backup format '$format'\n" if $format ne 'pbs-vm';
6782
6783 my $tmpdir = "/var/tmp/vzdumptmp$$";
6784 rmtree $tmpdir;
6785 mkpath $tmpdir;
6786
6787 my $conffile = PVE::QemuConfig->config_file($vmid);
9f3d73bc
DM
6788 # disable interrupts (always do cleanups)
6789 local $SIG{INT} =
6790 local $SIG{TERM} =
6791 local $SIG{QUIT} =
6792 local $SIG{HUP} = sub { print STDERR "got interrupt - ignored\n"; };
6793
6794 # Note: $oldconf is undef if VM does not exists
6795 my $cfs_path = PVE::QemuConfig->cfs_config_path($vmid);
6796 my $oldconf = PVE::Cluster::cfs_read_file($cfs_path);
98a4b3fb 6797 my $new_conf_raw = '';
9f3d73bc
DM
6798
6799 my $rpcenv = PVE::RPCEnvironment::get();
e8b07b29
FE
6800 my $devinfo = {}; # info about drives included in backup
6801 my $virtdev_hash = {}; # info about allocated drives
9f3d73bc
DM
6802
6803 eval {
6804 # enable interrupts
6805 local $SIG{INT} =
6806 local $SIG{TERM} =
6807 local $SIG{QUIT} =
6808 local $SIG{HUP} =
6809 local $SIG{PIPE} = sub { die "interrupted by signal\n"; };
6810
6811 my $cfgfn = "$tmpdir/qemu-server.conf";
6812 my $firewall_config_fn = "$tmpdir/fw.conf";
6813 my $index_fn = "$tmpdir/index.json";
6814
6815 my $cmd = "restore";
6816
6817 my $param = [$pbs_backup_name, "index.json", $index_fn];
6818 PVE::Storage::PBSPlugin::run_raw_client_cmd($scfg, $storeid, $cmd, $param);
6819 my $index = PVE::Tools::file_get_contents($index_fn);
6820 $index = decode_json($index);
6821
9f3d73bc
DM
6822 foreach my $info (@{$index->{files}}) {
6823 if ($info->{filename} =~ m/^(drive-\S+).img.fidx$/) {
6824 my $devname = $1;
6825 if ($info->{size} =~ m/^(\d+)$/) { # untaint size
6826 $devinfo->{$devname}->{size} = $1;
6827 } else {
6828 die "unable to parse file size in 'index.json' - got '$info->{size}'\n";
6829 }
6830 }
6831 }
6832
4df98f2f
TL
6833 my $is_qemu_server_backup = scalar(
6834 grep { $_->{filename} eq 'qemu-server.conf.blob' } @{$index->{files}}
6835 );
9f3d73bc
DM
6836 if (!$is_qemu_server_backup) {
6837 die "backup does not look like a qemu-server backup (missing 'qemu-server.conf' file)\n";
6838 }
6839 my $has_firewall_config = scalar(grep { $_->{filename} eq 'fw.conf.blob' } @{$index->{files}});
6840
6841 $param = [$pbs_backup_name, "qemu-server.conf", $cfgfn];
6842 PVE::Storage::PBSPlugin::run_raw_client_cmd($scfg, $storeid, $cmd, $param);
6843
6844 if ($has_firewall_config) {
6845 $param = [$pbs_backup_name, "fw.conf", $firewall_config_fn];
6846 PVE::Storage::PBSPlugin::run_raw_client_cmd($scfg, $storeid, $cmd, $param);
6847
6848 my $pve_firewall_dir = '/etc/pve/firewall';
6849 mkdir $pve_firewall_dir; # make sure the dir exists
6850 PVE::Tools::file_copy($firewall_config_fn, "${pve_firewall_dir}/$vmid.fw");
6851 }
6852
6853 my $fh = IO::File->new($cfgfn, "r") ||
a1cbe55c 6854 die "unable to read qemu-server.conf - $!\n";
9f3d73bc 6855
e8b07b29 6856 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $storecfg, $fh, $devinfo, $options);
9f3d73bc
DM
6857
6858 # fixme: rate limit?
6859
6860 # create empty/temp config
6861 PVE::Tools::file_set_contents($conffile, "memory: 128\nlock: create");
6862
6863 $restore_cleanup_oldconf->($storecfg, $vmid, $oldconf, $virtdev_hash) if $oldconf;
6864
6865 # allocate volumes
6866 my $map = $restore_allocate_devices->($storecfg, $virtdev_hash, $vmid);
6867
6f94e162
SR
6868 foreach my $virtdev (sort keys %$virtdev_hash) {
6869 my $d = $virtdev_hash->{$virtdev};
6870 next if $d->{is_cloudinit}; # no need to restore cloudinit
9f3d73bc 6871
55c7f9cf 6872 # this fails if storage is unavailable
6f94e162 6873 my $volid = $d->{volid};
6f94e162 6874 my $path = PVE::Storage::path($storecfg, $volid);
9f3d73bc 6875
f9dde219
SR
6876 # for live-restore we only want to preload the efidisk and TPM state
6877 next if $options->{live} && $virtdev ne 'efidisk0' && $virtdev ne 'tpmstate0';
55c7f9cf 6878
21a9ec2a
WB
6879 my @ns_arg;
6880 if (defined(my $ns = $scfg->{namespace})) {
6881 @ns_arg = ('--ns', $ns);
6882 }
6883
6f94e162
SR
6884 my $pbs_restore_cmd = [
6885 '/usr/bin/pbs-restore',
6886 '--repository', $repo,
21a9ec2a 6887 @ns_arg,
6f94e162
SR
6888 $pbs_backup_name,
6889 "$d->{devname}.img.fidx",
6890 $path,
6891 '--verbose',
6892 ];
55fb78aa 6893
6f94e162
SR
6894 push @$pbs_restore_cmd, '--format', $d->{format} if $d->{format};
6895 push @$pbs_restore_cmd, '--keyfile', $keyfile if -e $keyfile;
9f3d73bc 6896
6f94e162
SR
6897 if (PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $volid)) {
6898 push @$pbs_restore_cmd, '--skip-zero';
26731a3c 6899 }
6f94e162
SR
6900
6901 my $dbg_cmdstring = PVE::Tools::cmd2string($pbs_restore_cmd);
6902 print "restore proxmox backup image: $dbg_cmdstring\n";
6903 run_command($pbs_restore_cmd);
9f3d73bc
DM
6904 }
6905
6906 $fh->seek(0, 0) || die "seek failed - $!\n";
6907
9f3d73bc
DM
6908 my $cookie = { netcount => 0 };
6909 while (defined(my $line = <$fh>)) {
c62d7cf5 6910 $new_conf_raw .= restore_update_config_line(
98a4b3fb 6911 $cookie,
98a4b3fb
FE
6912 $map,
6913 $line,
6914 $options->{unique},
6915 );
9f3d73bc
DM
6916 }
6917
6918 $fh->close();
9f3d73bc
DM
6919 };
6920 my $err = $@;
6921
26731a3c 6922 if ($err || !$options->{live}) {
e8b07b29 6923 $restore_deactivate_volumes->($storecfg, $virtdev_hash);
26731a3c 6924 }
9f3d73bc
DM
6925
6926 rmtree $tmpdir;
6927
6928 if ($err) {
e8b07b29 6929 $restore_destroy_volumes->($storecfg, $virtdev_hash);
9f3d73bc
DM
6930 die $err;
6931 }
6932
f7551170
SR
6933 if ($options->{live}) {
6934 # keep lock during live-restore
6935 $new_conf_raw .= "\nlock: create";
6936 }
6937
202a2a0b
FE
6938 my $new_conf = $restore_merge_config->($conffile, $new_conf_raw, $options->{override_conf});
6939 PVE::QemuConfig->write_config($vmid, $new_conf);
9f3d73bc
DM
6940
6941 eval { rescan($vmid, 1); };
6942 warn $@ if $@;
26731a3c
SR
6943
6944 PVE::AccessControl::add_vm_to_pool($vmid, $options->{pool}) if $options->{pool};
6945
6946 if ($options->{live}) {
fefd65a1
SR
6947 # enable interrupts
6948 local $SIG{INT} =
6949 local $SIG{TERM} =
6950 local $SIG{QUIT} =
6951 local $SIG{HUP} =
6952 local $SIG{PIPE} = sub { die "got signal ($!) - abort\n"; };
26731a3c 6953
fefd65a1
SR
6954 my $conf = PVE::QemuConfig->load_config($vmid);
6955 die "cannot do live-restore for template\n" if PVE::QemuConfig->is_template($conf);
26731a3c 6956
f9dde219
SR
6957 # these special drives are already restored before start
6958 delete $devinfo->{'drive-efidisk0'};
6959 delete $devinfo->{'drive-tpmstate0-backup'};
2dda626d
DC
6960
6961 my $pbs_opts = {
6962 repo => $repo,
6963 keyfile => $keyfile,
6964 snapshot => $pbs_backup_name,
6965 namespace => $namespace,
6966 };
6967 pbs_live_restore($vmid, $conf, $storecfg, $devinfo, $pbs_opts);
f7551170
SR
6968
6969 PVE::QemuConfig->remove_lock($vmid, "create");
26731a3c
SR
6970 }
6971}
6972
6973sub pbs_live_restore {
2dda626d 6974 my ($vmid, $conf, $storecfg, $restored_disks, $opts) = @_;
26731a3c 6975
88cabb62 6976 print "starting VM for live-restore\n";
2dda626d 6977 print "repository: '$opts->{repo}', snapshot: '$opts->{snapshot}'\n";
26731a3c
SR
6978
6979 my $pbs_backing = {};
8986e36e 6980 for my $ds (keys %$restored_disks) {
26731a3c 6981 $ds =~ m/^drive-(.*)$/;
88cabb62
SR
6982 my $confname = $1;
6983 $pbs_backing->{$confname} = {
2dda626d
DC
6984 repository => $opts->{repo},
6985 snapshot => $opts->{snapshot},
26731a3c
SR
6986 archive => "$ds.img.fidx",
6987 };
2dda626d
DC
6988 $pbs_backing->{$confname}->{keyfile} = $opts->{keyfile} if -e $opts->{keyfile};
6989 $pbs_backing->{$confname}->{namespace} = $opts->{namespace} if defined($opts->{namespace});
88cabb62
SR
6990
6991 my $drive = parse_drive($confname, $conf->{$confname});
6992 print "restoring '$ds' to '$drive->{file}'\n";
26731a3c
SR
6993 }
6994
fd70c843 6995 my $drives_streamed = 0;
26731a3c
SR
6996 eval {
6997 # make sure HA doesn't interrupt our restore by stopping the VM
6998 if (PVE::HA::Config::vm_is_ha_managed($vmid)) {
fd70c843 6999 run_command(['ha-manager', 'set', "vm:$vmid", '--state', 'started']);
26731a3c
SR
7000 }
7001
fd70c843
TL
7002 # start VM with backing chain pointing to PBS backup, environment vars for PBS driver
7003 # in QEMU (PBS_PASSWORD and PBS_FINGERPRINT) are already set by our caller
bfb12678 7004 vm_start_nolock($storecfg, $vmid, $conf, {paused => 1, 'pbs-backing' => $pbs_backing}, {});
26731a3c 7005
26697640
SR
7006 my $qmeventd_fd = register_qmeventd_handle($vmid);
7007
26731a3c
SR
7008 # begin streaming, i.e. data copy from PBS to target disk for every vol,
7009 # this will effectively collapse the backing image chain consisting of
7010 # [target <- alloc-track -> PBS snapshot] to just [target] (alloc-track
7011 # removes itself once all backing images vanish with 'auto-remove=on')
7012 my $jobs = {};
8986e36e 7013 for my $ds (sort keys %$restored_disks) {
26731a3c
SR
7014 my $job_id = "restore-$ds";
7015 mon_cmd($vmid, 'block-stream',
7016 'job-id' => $job_id,
7017 device => "$ds",
7018 );
7019 $jobs->{$job_id} = {};
7020 }
7021
7022 mon_cmd($vmid, 'cont');
7023 qemu_drive_mirror_monitor($vmid, undef, $jobs, 'auto', 0, 'stream');
7024
a09b39f1
TL
7025 print "restore-drive jobs finished successfully, removing all tracking block devices"
7026 ." to disconnect from Proxmox Backup Server\n";
7027
8986e36e 7028 for my $ds (sort keys %$restored_disks) {
26731a3c
SR
7029 mon_cmd($vmid, 'blockdev-del', 'node-name' => "$ds-pbs");
7030 }
26697640
SR
7031
7032 close($qmeventd_fd);
26731a3c
SR
7033 };
7034
7035 my $err = $@;
7036
7037 if ($err) {
8b8893c3 7038 warn "An error occurred during live-restore: $err\n";
26731a3c
SR
7039 _do_vm_stop($storecfg, $vmid, 1, 1, 10, 0, 1);
7040 die "live-restore failed\n";
7041 }
9f3d73bc
DM
7042}
7043
91bd6c90
DM
7044sub restore_vma_archive {
7045 my ($archive, $vmid, $user, $opts, $comp) = @_;
7046
91bd6c90
DM
7047 my $readfrom = $archive;
7048
7c536e11
WB
7049 my $cfg = PVE::Storage::config();
7050 my $commands = [];
7051 my $bwlimit = $opts->{bwlimit};
7052
7053 my $dbg_cmdstring = '';
7054 my $add_pipe = sub {
7055 my ($cmd) = @_;
7056 push @$commands, $cmd;
7057 $dbg_cmdstring .= ' | ' if length($dbg_cmdstring);
7058 $dbg_cmdstring .= PVE::Tools::cmd2string($cmd);
91bd6c90 7059 $readfrom = '-';
7c536e11
WB
7060 };
7061
7062 my $input = undef;
7063 if ($archive eq '-') {
7064 $input = '<&STDIN';
7065 } else {
7066 # If we use a backup from a PVE defined storage we also consider that
7067 # storage's rate limit:
7068 my (undef, $volid) = PVE::Storage::path_to_volume_id($cfg, $archive);
7069 if (defined($volid)) {
7070 my ($sid, undef) = PVE::Storage::parse_volume_id($volid);
7071 my $readlimit = PVE::Storage::get_bandwidth_limit('restore', [$sid], $bwlimit);
7072 if ($readlimit) {
7073 print STDERR "applying read rate limit: $readlimit\n";
9444c6e4 7074 my $cstream = ['cstream', '-t', $readlimit*1024, '--', $readfrom];
7c536e11
WB
7075 $add_pipe->($cstream);
7076 }
7077 }
7078 }
7079
7080 if ($comp) {
c6d51783
AA
7081 my $info = PVE::Storage::decompressor_info('vma', $comp);
7082 my $cmd = $info->{decompressor};
7083 push @$cmd, $readfrom;
7c536e11 7084 $add_pipe->($cmd);
91bd6c90
DM
7085 }
7086
7087 my $tmpdir = "/var/tmp/vzdumptmp$$";
7088 rmtree $tmpdir;
7089
7090 # disable interrupts (always do cleanups)
5b97ef24
TL
7091 local $SIG{INT} =
7092 local $SIG{TERM} =
7093 local $SIG{QUIT} =
7094 local $SIG{HUP} = sub { warn "got interrupt - ignored\n"; };
91bd6c90
DM
7095
7096 my $mapfifo = "/var/tmp/vzdumptmp$$.fifo";
7097 POSIX::mkfifo($mapfifo, 0600);
7098 my $fifofh;
808a65b5 7099 my $openfifo = sub { open($fifofh, '>', $mapfifo) or die $! };
91bd6c90 7100
7c536e11 7101 $add_pipe->(['vma', 'extract', '-v', '-r', $mapfifo, $readfrom, $tmpdir]);
91bd6c90
DM
7102
7103 my $oldtimeout;
7104 my $timeout = 5;
7105
e8b07b29
FE
7106 my $devinfo = {}; # info about drives included in backup
7107 my $virtdev_hash = {}; # info about allocated drives
91bd6c90
DM
7108
7109 my $rpcenv = PVE::RPCEnvironment::get();
7110
ffda963f 7111 my $conffile = PVE::QemuConfig->config_file($vmid);
91bd6c90 7112
ae200950 7113 # Note: $oldconf is undef if VM does not exist
ffda963f
FG
7114 my $cfs_path = PVE::QemuConfig->cfs_config_path($vmid);
7115 my $oldconf = PVE::Cluster::cfs_read_file($cfs_path);
98a4b3fb 7116 my $new_conf_raw = '';
ed221350 7117
7c536e11
WB
7118 my %storage_limits;
7119
91bd6c90 7120 my $print_devmap = sub {
91bd6c90
DM
7121 my $cfgfn = "$tmpdir/qemu-server.conf";
7122
7123 # we can read the config - that is already extracted
7124 my $fh = IO::File->new($cfgfn, "r") ||
a1cbe55c 7125 die "unable to read qemu-server.conf - $!\n";
91bd6c90 7126
6738ab9c 7127 my $fwcfgfn = "$tmpdir/qemu-server.fw";
3457d090
WL
7128 if (-f $fwcfgfn) {
7129 my $pve_firewall_dir = '/etc/pve/firewall';
7130 mkdir $pve_firewall_dir; # make sure the dir exists
7131 PVE::Tools::file_copy($fwcfgfn, "${pve_firewall_dir}/$vmid.fw");
7132 }
6738ab9c 7133
e8b07b29 7134 $virtdev_hash = $parse_backup_hints->($rpcenv, $user, $cfg, $fh, $devinfo, $opts);
91bd6c90 7135
c8964278
FE
7136 foreach my $info (values %{$virtdev_hash}) {
7137 my $storeid = $info->{storeid};
7138 next if defined($storage_limits{$storeid});
7139
7140 my $limit = PVE::Storage::get_bandwidth_limit('restore', [$storeid], $bwlimit) // 0;
7141 print STDERR "rate limit for storage $storeid: $limit KiB/s\n" if $limit;
7142 $storage_limits{$storeid} = $limit * 1024;
7c536e11
WB
7143 }
7144
91bd6c90 7145 foreach my $devname (keys %$devinfo) {
be190583
DM
7146 die "found no device mapping information for device '$devname'\n"
7147 if !$devinfo->{$devname}->{virtdev};
91bd6c90
DM
7148 }
7149
ed221350 7150 # create empty/temp config
be190583 7151 if ($oldconf) {
ed221350 7152 PVE::Tools::file_set_contents($conffile, "memory: 128\n");
d1e92cf6 7153 $restore_cleanup_oldconf->($cfg, $vmid, $oldconf, $virtdev_hash);
ed221350
DM
7154 }
7155
9f3d73bc
DM
7156 # allocate volumes
7157 my $map = $restore_allocate_devices->($cfg, $virtdev_hash, $vmid);
7158
7159 # print restore information to $fifofh
91bd6c90
DM
7160 foreach my $virtdev (sort keys %$virtdev_hash) {
7161 my $d = $virtdev_hash->{$virtdev};
9f3d73bc
DM
7162 next if $d->{is_cloudinit}; # no need to restore cloudinit
7163
7c536e11 7164 my $storeid = $d->{storeid};
9f3d73bc 7165 my $volid = $d->{volid};
7c536e11
WB
7166
7167 my $map_opts = '';
7168 if (my $limit = $storage_limits{$storeid}) {
7169 $map_opts .= "throttling.bps=$limit:throttling.group=$storeid:";
7170 }
8fd57431 7171
91bd6c90 7172 my $write_zeros = 1;
88240a83 7173 if (PVE::Storage::volume_has_feature($cfg, 'sparseinit', $volid)) {
91bd6c90
DM
7174 $write_zeros = 0;
7175 }
7176
9f3d73bc 7177 my $path = PVE::Storage::path($cfg, $volid);
87056e18 7178
9f3d73bc 7179 print $fifofh "${map_opts}format=$d->{format}:${write_zeros}:$d->{devname}=$path\n";
91bd6c90 7180
9f3d73bc 7181 print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n";
91bd6c90
DM
7182 }
7183
7184 $fh->seek(0, 0) || die "seek failed - $!\n";
7185
91bd6c90
DM
7186 my $cookie = { netcount => 0 };
7187 while (defined(my $line = <$fh>)) {
c62d7cf5 7188 $new_conf_raw .= restore_update_config_line(
98a4b3fb 7189 $cookie,
98a4b3fb
FE
7190 $map,
7191 $line,
7192 $opts->{unique},
7193 );
91bd6c90
DM
7194 }
7195
7196 $fh->close();
91bd6c90
DM
7197 };
7198
7199 eval {
7200 # enable interrupts
6cb0144a
EK
7201 local $SIG{INT} =
7202 local $SIG{TERM} =
7203 local $SIG{QUIT} =
7204 local $SIG{HUP} =
7205 local $SIG{PIPE} = sub { die "interrupted by signal\n"; };
91bd6c90
DM
7206 local $SIG{ALRM} = sub { die "got timeout\n"; };
7207
7208 $oldtimeout = alarm($timeout);
7209
7210 my $parser = sub {
7211 my $line = shift;
7212
7213 print "$line\n";
7214
7215 if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) {
7216 my ($dev_id, $size, $devname) = ($1, $2, $3);
7217 $devinfo->{$devname} = { size => $size, dev_id => $dev_id };
7218 } elsif ($line =~ m/^CTIME: /) {
46f58b5f 7219 # we correctly received the vma config, so we can disable
3cf90d7a
DM
7220 # the timeout now for disk allocation (set to 10 minutes, so
7221 # that we always timeout if something goes wrong)
7222 alarm(600);
91bd6c90
DM
7223 &$print_devmap();
7224 print $fifofh "done\n";
7225 my $tmp = $oldtimeout || 0;
7226 $oldtimeout = undef;
7227 alarm($tmp);
7228 close($fifofh);
808a65b5 7229 $fifofh = undef;
91bd6c90
DM
7230 }
7231 };
be190583 7232
7c536e11
WB
7233 print "restore vma archive: $dbg_cmdstring\n";
7234 run_command($commands, input => $input, outfunc => $parser, afterfork => $openfifo);
91bd6c90
DM
7235 };
7236 my $err = $@;
7237
7238 alarm($oldtimeout) if $oldtimeout;
7239
e8b07b29 7240 $restore_deactivate_volumes->($cfg, $virtdev_hash);
5f96f4df 7241
808a65b5 7242 close($fifofh) if $fifofh;
91bd6c90 7243 unlink $mapfifo;
9f3d73bc 7244 rmtree $tmpdir;
91bd6c90
DM
7245
7246 if ($err) {
e8b07b29 7247 $restore_destroy_volumes->($cfg, $virtdev_hash);
91bd6c90
DM
7248 die $err;
7249 }
7250
202a2a0b
FE
7251 my $new_conf = $restore_merge_config->($conffile, $new_conf_raw, $opts->{override_conf});
7252 PVE::QemuConfig->write_config($vmid, $new_conf);
ed221350 7253
91bd6c90
DM
7254 eval { rescan($vmid, 1); };
7255 warn $@ if $@;
26731a3c
SR
7256
7257 PVE::AccessControl::add_vm_to_pool($vmid, $opts->{pool}) if $opts->{pool};
91bd6c90
DM
7258}
7259
7260sub restore_tar_archive {
7261 my ($archive, $vmid, $user, $opts) = @_;
7262
202a2a0b
FE
7263 if (scalar(keys $opts->{override_conf}->%*) > 0) {
7264 my $keystring = join(' ', keys $opts->{override_conf}->%*);
7265 die "cannot pass along options ($keystring) when restoring from tar archive\n";
7266 }
7267
9c502e26 7268 if ($archive ne '-') {
ed221350 7269 my $firstfile = tar_archive_read_firstfile($archive);
32e54050 7270 die "ERROR: file '$archive' does not look like a QemuServer vzdump backup\n"
9c502e26
DM
7271 if $firstfile ne 'qemu-server.conf';
7272 }
3e16d5fc 7273
20519efc 7274 my $storecfg = PVE::Storage::config();
ebb55558 7275
4b026937
TL
7276 # avoid zombie disks when restoring over an existing VM -> cleanup first
7277 # pass keep_empty_config=1 to keep the config (thus VMID) reserved for us
7278 # skiplock=1 because qmrestore has set the 'create' lock itself already
ffda963f 7279 my $vmcfgfn = PVE::QemuConfig->config_file($vmid);
b04ea584 7280 destroy_vm($storecfg, $vmid, 1, { lock => 'restore' }) if -f $vmcfgfn;
ed221350 7281
3e16d5fc
DM
7282 my $tocmd = "/usr/lib/qemu-server/qmextract";
7283
2415a446 7284 $tocmd .= " --storage " . PVE::Tools::shellquote($opts->{storage}) if $opts->{storage};
a0d1b1a2 7285 $tocmd .= " --pool " . PVE::Tools::shellquote($opts->{pool}) if $opts->{pool};
3e16d5fc
DM
7286 $tocmd .= ' --prealloc' if $opts->{prealloc};
7287 $tocmd .= ' --info' if $opts->{info};
7288
a0d1b1a2 7289 # tar option "xf" does not autodetect compression when read from STDIN,
9c502e26 7290 # so we pipe to zcat
2415a446
DM
7291 my $cmd = "zcat -f|tar xf " . PVE::Tools::shellquote($archive) . " " .
7292 PVE::Tools::shellquote("--to-command=$tocmd");
3e16d5fc
DM
7293
7294 my $tmpdir = "/var/tmp/vzdumptmp$$";
7295 mkpath $tmpdir;
7296
7297 local $ENV{VZDUMP_TMPDIR} = $tmpdir;
7298 local $ENV{VZDUMP_VMID} = $vmid;
a0d1b1a2 7299 local $ENV{VZDUMP_USER} = $user;
3e16d5fc 7300
ffda963f 7301 my $conffile = PVE::QemuConfig->config_file($vmid);
98a4b3fb 7302 my $new_conf_raw = '';
3e16d5fc
DM
7303
7304 # disable interrupts (always do cleanups)
6cb0144a
EK
7305 local $SIG{INT} =
7306 local $SIG{TERM} =
7307 local $SIG{QUIT} =
7308 local $SIG{HUP} = sub { print STDERR "got interrupt - ignored\n"; };
3e16d5fc 7309
afdb31d5 7310 eval {
3e16d5fc 7311 # enable interrupts
6cb0144a
EK
7312 local $SIG{INT} =
7313 local $SIG{TERM} =
7314 local $SIG{QUIT} =
7315 local $SIG{HUP} =
7316 local $SIG{PIPE} = sub { die "interrupted by signal\n"; };
3e16d5fc 7317
9c502e26
DM
7318 if ($archive eq '-') {
7319 print "extracting archive from STDIN\n";
7320 run_command($cmd, input => "<&STDIN");
7321 } else {
7322 print "extracting archive '$archive'\n";
7323 run_command($cmd);
7324 }
3e16d5fc
DM
7325
7326 return if $opts->{info};
7327
7328 # read new mapping
7329 my $map = {};
7330 my $statfile = "$tmpdir/qmrestore.stat";
7331 if (my $fd = IO::File->new($statfile, "r")) {
7332 while (defined (my $line = <$fd>)) {
7333 if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) {
7334 $map->{$1} = $2 if $1;
7335 } else {
7336 print STDERR "unable to parse line in statfile - $line\n";
7337 }
7338 }
7339 $fd->close();
7340 }
7341
7342 my $confsrc = "$tmpdir/qemu-server.conf";
7343
f7d1505b 7344 my $srcfd = IO::File->new($confsrc, "r") || die "unable to open file '$confsrc'\n";
3e16d5fc 7345
91bd6c90 7346 my $cookie = { netcount => 0 };
3e16d5fc 7347 while (defined (my $line = <$srcfd>)) {
c62d7cf5 7348 $new_conf_raw .= restore_update_config_line(
98a4b3fb 7349 $cookie,
98a4b3fb
FE
7350 $map,
7351 $line,
7352 $opts->{unique},
7353 );
3e16d5fc
DM
7354 }
7355
7356 $srcfd->close();
3e16d5fc 7357 };
7dc7f315 7358 if (my $err = $@) {
ed221350 7359 tar_restore_cleanup($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info};
3e16d5fc 7360 die $err;
afdb31d5 7361 }
3e16d5fc
DM
7362
7363 rmtree $tmpdir;
7364
98a4b3fb 7365 PVE::Tools::file_set_contents($conffile, $new_conf_raw);
91bd6c90 7366
ed221350
DM
7367 PVE::Cluster::cfs_update(); # make sure we read new file
7368
91bd6c90
DM
7369 eval { rescan($vmid, 1); };
7370 warn $@ if $@;
3e16d5fc
DM
7371};
7372
65a5ce88 7373sub foreach_storage_used_by_vm {
18bfb361
DM
7374 my ($conf, $func) = @_;
7375
7376 my $sidhash = {};
7377
912792e2 7378 PVE::QemuConfig->foreach_volume($conf, sub {
8ddbcf8b
FG
7379 my ($ds, $drive) = @_;
7380 return if drive_is_cdrom($drive);
18bfb361
DM
7381
7382 my $volid = $drive->{file};
7383
7384 my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1);
be190583 7385 $sidhash->{$sid} = $sid if $sid;
8ddbcf8b 7386 });
18bfb361
DM
7387
7388 foreach my $sid (sort keys %$sidhash) {
7389 &$func($sid);
7390 }
7391}
7392
6c9f59c1
TL
7393my $qemu_snap_storage = {
7394 rbd => 1,
7395};
e5eaa028 7396sub do_snapshots_with_qemu {
9d83932d
SR
7397 my ($storecfg, $volid, $deviceid) = @_;
7398
7399 return if $deviceid =~ m/tpmstate0/;
e5eaa028
WL
7400
7401 my $storage_name = PVE::Storage::parse_volume_id($volid);
8aa2ed7c 7402 my $scfg = $storecfg->{ids}->{$storage_name};
f7d1505b 7403 die "could not find storage '$storage_name'\n" if !defined($scfg);
e5eaa028 7404
8aa2ed7c 7405 if ($qemu_snap_storage->{$scfg->{type}} && !$scfg->{krbd}){
e5eaa028
WL
7406 return 1;
7407 }
7408
7409 if ($volid =~ m/\.(qcow2|qed)$/){
7410 return 1;
7411 }
7412
d1c1af4b 7413 return;
e5eaa028
WL
7414}
7415
4dcc780c 7416sub qga_check_running {
a4938c72 7417 my ($vmid, $nowarn) = @_;
4dcc780c 7418
0a13e08e 7419 eval { mon_cmd($vmid, "guest-ping", timeout => 3); };
4dcc780c 7420 if ($@) {
a4938c72 7421 warn "Qemu Guest Agent is not running - $@" if !$nowarn;
4dcc780c
WL
7422 return 0;
7423 }
7424 return 1;
7425}
7426
04a69bb4
AD
7427sub template_create {
7428 my ($vmid, $conf, $disk) = @_;
7429
04a69bb4 7430 my $storecfg = PVE::Storage::config();
04a69bb4 7431
912792e2 7432 PVE::QemuConfig->foreach_volume($conf, sub {
9cd07842
DM
7433 my ($ds, $drive) = @_;
7434
7435 return if drive_is_cdrom($drive);
7436 return if $disk && $ds ne $disk;
7437
7438 my $volid = $drive->{file};
bbd56097 7439 return if !PVE::Storage::volume_has_feature($storecfg, 'template', $volid);
9cd07842 7440
04a69bb4
AD
7441 my $voliddst = PVE::Storage::vdisk_create_base($storecfg, $volid);
7442 $drive->{file} = $voliddst;
71c58bb7 7443 $conf->{$ds} = print_drive($drive);
ffda963f 7444 PVE::QemuConfig->write_config($vmid, $conf);
04a69bb4 7445 });
04a69bb4
AD
7446}
7447
92bdc3f0
DC
7448sub convert_iscsi_path {
7449 my ($path) = @_;
7450
7451 if ($path =~ m|^iscsi://([^/]+)/([^/]+)/(.+)$|) {
7452 my $portal = $1;
7453 my $target = $2;
7454 my $lun = $3;
7455
7456 my $initiator_name = get_initiator_name();
7457
7458 return "file.driver=iscsi,file.transport=tcp,file.initiator-name=$initiator_name,".
7459 "file.portal=$portal,file.target=$target,file.lun=$lun,driver=raw";
7460 }
7461
7462 die "cannot convert iscsi path '$path', unkown format\n";
7463}
7464
5133de42 7465sub qemu_img_convert {
988e2714 7466 my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_;
5133de42
AD
7467
7468 my $storecfg = PVE::Storage::config();
7469 my ($src_storeid, $src_volname) = PVE::Storage::parse_volume_id($src_volid, 1);
7470 my ($dst_storeid, $dst_volname) = PVE::Storage::parse_volume_id($dst_volid, 1);
7471
af1f1ec0 7472 die "destination '$dst_volid' is not a valid volid form qemu-img convert\n" if !$dst_storeid;
6bb91c17 7473
af1f1ec0
DC
7474 my $cachemode;
7475 my $src_path;
7476 my $src_is_iscsi = 0;
bdd1feef 7477 my $src_format;
6bb91c17 7478
af1f1ec0
DC
7479 if ($src_storeid) {
7480 PVE::Storage::activate_volumes($storecfg, [$src_volid], $snapname);
5133de42 7481 my $src_scfg = PVE::Storage::storage_config($storecfg, $src_storeid);
af1f1ec0
DC
7482 $src_format = qemu_img_format($src_scfg, $src_volname);
7483 $src_path = PVE::Storage::path($storecfg, $src_volid, $snapname);
7484 $src_is_iscsi = ($src_path =~ m|^iscsi://|);
7485 $cachemode = 'none' if $src_scfg->{type} eq 'zfspool';
a23d57d5 7486 } elsif (-f $src_volid || -b $src_volid) {
af1f1ec0 7487 $src_path = $src_volid;
e0fd2b2f 7488 if ($src_path =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
af1f1ec0
DC
7489 $src_format = $1;
7490 }
7491 }
5133de42 7492
af1f1ec0 7493 die "source '$src_volid' is not a valid volid nor path for qemu-img convert\n" if !$src_path;
5133de42 7494
af1f1ec0
DC
7495 my $dst_scfg = PVE::Storage::storage_config($storecfg, $dst_storeid);
7496 my $dst_format = qemu_img_format($dst_scfg, $dst_volname);
7497 my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
7498 my $dst_is_iscsi = ($dst_path =~ m|^iscsi://|);
5133de42 7499
af1f1ec0
DC
7500 my $cmd = [];
7501 push @$cmd, '/usr/bin/qemu-img', 'convert', '-p', '-n';
bdd1feef
TL
7502 push @$cmd, '-l', "snapshot.name=$snapname"
7503 if $snapname && $src_format && $src_format eq "qcow2";
af1f1ec0
DC
7504 push @$cmd, '-t', 'none' if $dst_scfg->{type} eq 'zfspool';
7505 push @$cmd, '-T', $cachemode if defined($cachemode);
7506
7507 if ($src_is_iscsi) {
7508 push @$cmd, '--image-opts';
7509 $src_path = convert_iscsi_path($src_path);
bdd1feef 7510 } elsif ($src_format) {
af1f1ec0
DC
7511 push @$cmd, '-f', $src_format;
7512 }
92bdc3f0 7513
af1f1ec0
DC
7514 if ($dst_is_iscsi) {
7515 push @$cmd, '--target-image-opts';
7516 $dst_path = convert_iscsi_path($dst_path);
7517 } else {
7518 push @$cmd, '-O', $dst_format;
7519 }
92bdc3f0 7520
af1f1ec0 7521 push @$cmd, $src_path;
92bdc3f0 7522
af1f1ec0
DC
7523 if (!$dst_is_iscsi && $is_zero_initialized) {
7524 push @$cmd, "zeroinit:$dst_path";
7525 } else {
7526 push @$cmd, $dst_path;
7527 }
92bdc3f0 7528
af1f1ec0
DC
7529 my $parser = sub {
7530 my $line = shift;
7531 if($line =~ m/\((\S+)\/100\%\)/){
7532 my $percent = $1;
7533 my $transferred = int($size * $percent / 100);
b5e9d97b
TL
7534 my $total_h = render_bytes($size, 1);
7535 my $transferred_h = render_bytes($transferred, 1);
92bdc3f0 7536
6629f976 7537 print "transferred $transferred_h of $total_h ($percent%)\n";
988e2714 7538 }
5133de42 7539
af1f1ec0 7540 };
5133de42 7541
af1f1ec0
DC
7542 eval { run_command($cmd, timeout => undef, outfunc => $parser); };
7543 my $err = $@;
7544 die "copy failed: $err" if $err;
5133de42
AD
7545}
7546
7547sub qemu_img_format {
7548 my ($scfg, $volname) = @_;
7549
e0fd2b2f 7550 if ($scfg->{path} && $volname =~ m/\.($PVE::QemuServer::Drive::QEMU_FORMAT_RE)$/) {
5133de42 7551 return $1;
be190583 7552 } else {
5133de42 7553 return "raw";
5133de42
AD
7554 }
7555}
7556
cfad42af 7557sub qemu_drive_mirror {
bc6c8231 7558 my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized, $jobs, $completion, $qga, $bwlimit, $src_bitmap) = @_;
cfad42af 7559
5a345967
AD
7560 $jobs = {} if !$jobs;
7561
7562 my $qemu_target;
7563 my $format;
35e4ab04 7564 $jobs->{"drive-$drive"} = {};
152fe752 7565
1e5143de 7566 if ($dst_volid =~ /^nbd:/) {
87955688 7567 $qemu_target = $dst_volid;
5a345967 7568 $format = "nbd";
5a345967 7569 } else {
5a345967
AD
7570 my $storecfg = PVE::Storage::config();
7571 my ($dst_storeid, $dst_volname) = PVE::Storage::parse_volume_id($dst_volid);
7572
7573 my $dst_scfg = PVE::Storage::storage_config($storecfg, $dst_storeid);
cfad42af 7574
5a345967 7575 $format = qemu_img_format($dst_scfg, $dst_volname);
21ccdb50 7576
5a345967 7577 my $dst_path = PVE::Storage::path($storecfg, $dst_volid);
21ccdb50 7578
5a345967
AD
7579 $qemu_target = $is_zero_initialized ? "zeroinit:$dst_path" : $dst_path;
7580 }
988e2714
WB
7581
7582 my $opts = { timeout => 10, device => "drive-$drive", mode => "existing", sync => "full", target => $qemu_target };
88383920
DM
7583 $opts->{format} = $format if $format;
7584
bc6c8231
FG
7585 if (defined($src_bitmap)) {
7586 $opts->{sync} = 'incremental';
7587 $opts->{bitmap} = $src_bitmap;
7588 print "drive mirror re-using dirty bitmap '$src_bitmap'\n";
7589 }
7590
9fa05d31 7591 if (defined($bwlimit)) {
f6409f61
TL
7592 $opts->{speed} = $bwlimit * 1024;
7593 print "drive mirror is starting for drive-$drive with bandwidth limit: ${bwlimit} KB/s\n";
9fa05d31
SI
7594 } else {
7595 print "drive mirror is starting for drive-$drive\n";
7596 }
21ccdb50 7597
6dde5ea2 7598 # if a job already runs for this device we get an error, catch it for cleanup
0a13e08e 7599 eval { mon_cmd($vmid, "drive-mirror", %$opts); };
5a345967
AD
7600 if (my $err = $@) {
7601 eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
6dde5ea2
TL
7602 warn "$@\n" if $@;
7603 die "mirroring error: $err\n";
5a345967
AD
7604 }
7605
e02fb126 7606 qemu_drive_mirror_monitor ($vmid, $vmiddst, $jobs, $completion, $qga);
5a345967
AD
7607}
7608
db1f8b39
FG
7609# $completion can be either
7610# 'complete': wait until all jobs are ready, block-job-complete them (default)
7611# 'cancel': wait until all jobs are ready, block-job-cancel them
7612# 'skip': wait until all jobs are ready, return with block jobs in ready state
9e671722 7613# 'auto': wait until all jobs disappear, only use for jobs which complete automatically
5a345967 7614sub qemu_drive_mirror_monitor {
9e671722 7615 my ($vmid, $vmiddst, $jobs, $completion, $qga, $op) = @_;
e02fb126 7616
db1f8b39 7617 $completion //= 'complete';
9e671722 7618 $op //= "mirror";
2e953867 7619
08ac653f 7620 eval {
5a345967
AD
7621 my $err_complete = 0;
7622
3b56383b 7623 my $starttime = time ();
08ac653f 7624 while (1) {
9e671722 7625 die "block job ('$op') timed out\n" if $err_complete > 300;
5a345967 7626
0a13e08e 7627 my $stats = mon_cmd($vmid, "query-block-jobs");
3b56383b 7628 my $ctime = time();
08ac653f 7629
9e671722 7630 my $running_jobs = {};
0ea24bf0 7631 for my $stat (@$stats) {
9e671722
SR
7632 next if $stat->{type} ne $op;
7633 $running_jobs->{$stat->{device}} = $stat;
5a345967 7634 }
08ac653f 7635
5a345967 7636 my $readycounter = 0;
67fb9de6 7637
0ea24bf0 7638 for my $job_id (sort keys %$jobs) {
1057fc74 7639 my $job = $running_jobs->{$job_id};
5a345967 7640
1057fc74 7641 my $vanished = !defined($job);
0ea24bf0 7642 my $complete = defined($jobs->{$job_id}->{complete}) && $vanished;
9e671722 7643 if($complete || ($vanished && $completion eq 'auto')) {
3b56383b 7644 print "$job_id: $op-job finished\n";
0ea24bf0 7645 delete $jobs->{$job_id};
5a345967
AD
7646 next;
7647 }
7648
1057fc74 7649 die "$job_id: '$op' has been cancelled\n" if !defined($job);
f34ebd52 7650
1057fc74
TL
7651 my $busy = $job->{busy};
7652 my $ready = $job->{ready};
7653 if (my $total = $job->{len}) {
7654 my $transferred = $job->{offset} || 0;
5a345967
AD
7655 my $remaining = $total - $transferred;
7656 my $percent = sprintf "%.2f", ($transferred * 100 / $total);
08ac653f 7657
3b56383b
TL
7658 my $duration = $ctime - $starttime;
7659 my $total_h = render_bytes($total, 1);
7660 my $transferred_h = render_bytes($transferred, 1);
7661
7662 my $status = sprintf(
7663 "transferred $transferred_h of $total_h ($percent%%) in %s",
7664 render_duration($duration),
7665 );
7666
7667 if ($ready) {
7668 if ($busy) {
7669 $status .= ", still busy"; # shouldn't even happen? but mirror is weird
7670 } else {
7671 $status .= ", ready";
7672 }
7673 }
67daf692
TL
7674 print "$job_id: $status\n" if !$jobs->{$job_id}->{ready};
7675 $jobs->{$job_id}->{ready} = $ready;
5a345967 7676 }
f34ebd52 7677
1057fc74 7678 $readycounter++ if $job->{ready};
5a345967 7679 }
b467f79a 7680
5a345967
AD
7681 last if scalar(keys %$jobs) == 0;
7682
7683 if ($readycounter == scalar(keys %$jobs)) {
9e671722
SR
7684 print "all '$op' jobs are ready\n";
7685
7686 # do the complete later (or has already been done)
7687 last if $completion eq 'skip' || $completion eq 'auto';
5a345967
AD
7688
7689 if ($vmiddst && $vmiddst != $vmid) {
1a988fd2
DC
7690 my $agent_running = $qga && qga_check_running($vmid);
7691 if ($agent_running) {
5619e74a 7692 print "freeze filesystem\n";
0a13e08e 7693 eval { mon_cmd($vmid, "guest-fsfreeze-freeze"); };
d6cdfae4 7694 warn $@ if $@;
5619e74a
AD
7695 } else {
7696 print "suspend vm\n";
7697 eval { PVE::QemuServer::vm_suspend($vmid, 1); };
d6cdfae4 7698 warn $@ if $@;
5619e74a
AD
7699 }
7700
5a345967
AD
7701 # if we clone a disk for a new target vm, we don't switch the disk
7702 PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs);
5619e74a 7703
1a988fd2 7704 if ($agent_running) {
5619e74a 7705 print "unfreeze filesystem\n";
0a13e08e 7706 eval { mon_cmd($vmid, "guest-fsfreeze-thaw"); };
d6cdfae4 7707 warn $@ if $@;
5619e74a
AD
7708 } else {
7709 print "resume vm\n";
d6cdfae4
FE
7710 eval { PVE::QemuServer::vm_resume($vmid, 1, 1); };
7711 warn $@ if $@;
5619e74a
AD
7712 }
7713
2e953867 7714 last;
5a345967
AD
7715 } else {
7716
0ea24bf0 7717 for my $job_id (sort keys %$jobs) {
5a345967 7718 # try to switch the disk if source and destination are on the same guest
0ea24bf0 7719 print "$job_id: Completing block job_id...\n";
5a345967 7720
e02fb126 7721 my $op;
db1f8b39 7722 if ($completion eq 'complete') {
e02fb126 7723 $op = 'block-job-complete';
db1f8b39 7724 } elsif ($completion eq 'cancel') {
e02fb126
ML
7725 $op = 'block-job-cancel';
7726 } else {
7727 die "invalid completion value: $completion\n";
7728 }
0ea24bf0 7729 eval { mon_cmd($vmid, $op, device => $job_id) };
5a345967 7730 if ($@ =~ m/cannot be completed/) {
3b56383b 7731 print "$job_id: block job cannot be completed, trying again.\n";
5a345967
AD
7732 $err_complete++;
7733 }else {
0ea24bf0
TL
7734 print "$job_id: Completed successfully.\n";
7735 $jobs->{$job_id}->{complete} = 1;
5a345967
AD
7736 }
7737 }
2e953867 7738 }
08ac653f 7739 }
08ac653f 7740 sleep 1;
cfad42af 7741 }
08ac653f 7742 };
88383920 7743 my $err = $@;
08ac653f 7744
88383920 7745 if ($err) {
5a345967 7746 eval { PVE::QemuServer::qemu_blockjobs_cancel($vmid, $jobs) };
3b56383b 7747 die "block job ($op) error: $err";
88383920 7748 }
5a345967
AD
7749}
7750
7751sub qemu_blockjobs_cancel {
7752 my ($vmid, $jobs) = @_;
7753
7754 foreach my $job (keys %$jobs) {
bd2d5fe6 7755 print "$job: Cancelling block job\n";
0a13e08e 7756 eval { mon_cmd($vmid, "block-job-cancel", device => $job); };
5a345967
AD
7757 $jobs->{$job}->{cancel} = 1;
7758 }
7759
7760 while (1) {
0a13e08e 7761 my $stats = mon_cmd($vmid, "query-block-jobs");
5a345967
AD
7762
7763 my $running_jobs = {};
7764 foreach my $stat (@$stats) {
7765 $running_jobs->{$stat->{device}} = $stat;
7766 }
7767
7768 foreach my $job (keys %$jobs) {
7769
bd2d5fe6
WB
7770 if (defined($jobs->{$job}->{cancel}) && !defined($running_jobs->{$job})) {
7771 print "$job: Done.\n";
5a345967
AD
7772 delete $jobs->{$job};
7773 }
7774 }
7775
7776 last if scalar(keys %$jobs) == 0;
7777
7778 sleep 1;
cfad42af
AD
7779 }
7780}
7781
152fe752 7782sub clone_disk {
1196086f
FE
7783 my ($storecfg, $source, $dest, $full, $newvollist, $jobs, $completion, $qga, $bwlimit) = @_;
7784
7785 my ($vmid, $running) = $source->@{qw(vmid running)};
25166060 7786 my ($src_drivename, $drive, $snapname) = $source->@{qw(drivename drive snapname)};
1196086f 7787
25166060 7788 my ($newvmid, $dst_drivename, $efisize) = $dest->@{qw(vmid drivename efisize)};
1196086f 7789 my ($storage, $format) = $dest->@{qw(storage format)};
152fe752 7790
5f957592
FE
7791 my $use_drive_mirror = $full && $running && $src_drivename && !$snapname;
7792
25166060
FE
7793 if ($src_drivename && $dst_drivename && $src_drivename ne $dst_drivename) {
7794 die "cloning from/to EFI disk requires EFI disk\n"
7795 if $src_drivename eq 'efidisk0' || $dst_drivename eq 'efidisk0';
7796 die "cloning from/to TPM state requires TPM state\n"
7797 if $src_drivename eq 'tpmstate0' || $dst_drivename eq 'tpmstate0';
5f957592
FE
7798
7799 # This would lead to two device nodes in QEMU pointing to the same backing image!
7800 die "cannot change drive name when cloning disk from/to the same VM\n"
7801 if $use_drive_mirror && $vmid == $newvmid;
25166060
FE
7802 }
7803
1d1f8f9a
FE
7804 die "cannot move TPM state while VM is running\n"
7805 if $use_drive_mirror && $src_drivename eq 'tpmstate0';
7806
152fe752
DM
7807 my $newvolid;
7808
25166060
FE
7809 print "create " . ($full ? 'full' : 'linked') . " clone of drive ";
7810 print "$src_drivename " if $src_drivename;
7811 print "($drive->{file})\n";
7812
152fe752 7813 if (!$full) {
258e646c 7814 $newvolid = PVE::Storage::vdisk_clone($storecfg, $drive->{file}, $newvmid, $snapname);
152fe752
DM
7815 push @$newvollist, $newvolid;
7816 } else {
5a345967 7817
152fe752
DM
7818 my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file});
7819 $storeid = $storage if $storage;
7820
44549149 7821 my $dst_format = resolve_dst_disk_format($storecfg, $storeid, $volname, $format);
152fe752 7822
931432bd 7823 my $name = undef;
d0abc774 7824 my $size = undef;
7fe8b44c
TL
7825 if (drive_is_cloudinit($drive)) {
7826 $name = "vm-$newvmid-cloudinit";
c997e24a
ML
7827 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
7828 if ($scfg->{path}) {
7829 $name .= ".$dst_format";
7830 }
7fe8b44c
TL
7831 $snapname = undef;
7832 $size = PVE::QemuServer::Cloudinit::CLOUDINIT_DISK_SIZE;
25166060 7833 } elsif ($dst_drivename eq 'efidisk0') {
7344af7b 7834 $size = $efisize or die "internal error - need to specify EFI disk size\n";
25166060 7835 } elsif ($dst_drivename eq 'tpmstate0') {
5f5aba25 7836 $dst_format = 'raw';
f9dde219 7837 $size = PVE::QemuServer::Drive::TPMSTATE_DISK_SIZE;
d0abc774 7838 } else {
3bae384f 7839 ($size) = PVE::Storage::volume_size_info($storecfg, $drive->{file}, 10);
7fe8b44c 7840 }
b5688f69
FE
7841 $newvolid = PVE::Storage::vdisk_alloc(
7842 $storecfg, $storeid, $newvmid, $dst_format, $name, ($size/1024)
7843 );
152fe752
DM
7844 push @$newvollist, $newvolid;
7845
3999f370 7846 PVE::Storage::activate_volumes($storecfg, [$newvolid]);
1dbd6d30 7847
7fe8b44c 7848 if (drive_is_cloudinit($drive)) {
1b485263
ML
7849 # when cloning multiple disks (e.g. during clone_vm) it might be the last disk
7850 # if this is the case, we have to complete any block-jobs still there from
7851 # previous drive-mirrors
7852 if (($completion eq 'complete') && (scalar(keys %$jobs) > 0)) {
7853 qemu_drive_mirror_monitor($vmid, $newvmid, $jobs, $completion, $qga);
7854 }
7fe8b44c
TL
7855 goto no_data_clone;
7856 }
7857
988e2714 7858 my $sparseinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $newvolid);
5f957592 7859 if ($use_drive_mirror) {
5f957592
FE
7860 qemu_drive_mirror($vmid, $src_drivename, $newvolid, $newvmid, $sparseinit, $jobs,
7861 $completion, $qga, $bwlimit);
7862 } else {
d189e590 7863 # TODO: handle bwlimits
25166060 7864 if ($dst_drivename eq 'efidisk0') {
818ce80e
DC
7865 # the relevant data on the efidisk may be smaller than the source
7866 # e.g. on RBD/ZFS, so we use dd to copy only the amount
7867 # that is given by the OVMF_VARS.fd
62375438 7868 my $src_path = PVE::Storage::path($storecfg, $drive->{file}, $snapname);
818ce80e 7869 my $dst_path = PVE::Storage::path($storecfg, $newvolid);
fdfdc80e 7870
62375438
FE
7871 my $src_format = (PVE::Storage::parse_volname($storecfg, $drive->{file}))[6];
7872
fdfdc80e
FE
7873 # better for Ceph if block size is not too small, see bug #3324
7874 my $bs = 1024*1024;
7875
62375438 7876 my $cmd = ['qemu-img', 'dd', '-n', '-O', $dst_format];
a9c45bd4
FE
7877
7878 if ($src_format eq 'qcow2' && $snapname) {
7879 die "cannot clone qcow2 EFI disk snapshot - requires QEMU >= 6.2\n"
7880 if !min_version(kvm_user_version(), 6, 2);
7881 push $cmd->@*, '-l', $snapname;
7882 }
62375438
FE
7883 push $cmd->@*, "bs=$bs", "osize=$size", "if=$src_path", "of=$dst_path";
7884 run_command($cmd);
818ce80e
DC
7885 } else {
7886 qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit);
7887 }
be190583 7888 }
152fe752
DM
7889 }
7890
7fe8b44c 7891no_data_clone:
3bae384f 7892 my ($size) = eval { PVE::Storage::volume_size_info($storecfg, $newvolid, 10) };
152fe752 7893
3b53c471
FE
7894 my $disk = dclone($drive);
7895 delete $disk->{format};
152fe752 7896 $disk->{file} = $newvolid;
3bae384f 7897 $disk->{size} = $size if defined($size);
152fe752
DM
7898
7899 return $disk;
7900}
7901
98cfd8b6
AD
7902sub get_running_qemu_version {
7903 my ($vmid) = @_;
0a13e08e 7904 my $res = mon_cmd($vmid, "query-version");
98cfd8b6
AD
7905 return "$res->{qemu}->{major}.$res->{qemu}->{minor}";
7906}
7907
249c4a6c
AD
7908sub qemu_use_old_bios_files {
7909 my ($machine_type) = @_;
7910
7911 return if !$machine_type;
7912
7913 my $use_old_bios_files = undef;
7914
7915 if ($machine_type =~ m/^(\S+)\.pxe$/) {
7916 $machine_type = $1;
7917 $use_old_bios_files = 1;
7918 } else {
4df98f2f 7919 my $version = extract_version($machine_type, kvm_user_version());
249c4a6c
AD
7920 # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we
7921 # load new efi bios files on migration. So this hack is required to allow
7922 # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when
7923 # updrading from proxmox-ve-3.X to proxmox-ve 4.0
2ea5fb7e 7924 $use_old_bios_files = !min_version($version, 2, 4);
249c4a6c
AD
7925 }
7926
7927 return ($use_old_bios_files, $machine_type);
7928}
7929
818ce80e 7930sub get_efivars_size {
ff84f0e3
FE
7931 my ($conf, $efidisk) = @_;
7932
818ce80e 7933 my $arch = get_vm_arch($conf);
ff84f0e3 7934 $efidisk //= $conf->{efidisk0} ? parse_drive('efidisk0', $conf->{efidisk0}) : undef;
90b20b15
DC
7935 my $smm = PVE::QemuServer::Machine::machine_type_is_q35($conf);
7936 my (undef, $ovmf_vars) = get_ovmf_files($arch, $efidisk, $smm);
818ce80e
DC
7937 die "uefi vars image '$ovmf_vars' not found\n" if ! -f $ovmf_vars;
7938 return -s $ovmf_vars;
7939}
7940
7941sub update_efidisk_size {
7942 my ($conf) = @_;
7943
7944 return if !defined($conf->{efidisk0});
7945
7946 my $disk = PVE::QemuServer::parse_drive('efidisk0', $conf->{efidisk0});
7947 $disk->{size} = get_efivars_size($conf);
7948 $conf->{efidisk0} = print_drive($disk);
7949
7950 return;
7951}
7952
f9dde219
SR
7953sub update_tpmstate_size {
7954 my ($conf) = @_;
7955
7956 my $disk = PVE::QemuServer::parse_drive('tpmstate0', $conf->{tpmstate0});
7957 $disk->{size} = PVE::QemuServer::Drive::TPMSTATE_DISK_SIZE;
7958 $conf->{tpmstate0} = print_drive($disk);
7959}
7960
90b20b15
DC
7961sub create_efidisk($$$$$$$) {
7962 my ($storecfg, $storeid, $vmid, $fmt, $arch, $efidisk, $smm) = @_;
3e1f1122 7963
90b20b15 7964 my (undef, $ovmf_vars) = get_ovmf_files($arch, $efidisk, $smm);
96ed3574 7965 die "EFI vars default image not found\n" if ! -f $ovmf_vars;
3e1f1122 7966
af1f1ec0
DC
7967 my $vars_size_b = -s $ovmf_vars;
7968 my $vars_size = PVE::Tools::convert_size($vars_size_b, 'b' => 'kb');
3e1f1122
TL
7969 my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid, $fmt, undef, $vars_size);
7970 PVE::Storage::activate_volumes($storecfg, [$volid]);
7971
af1f1ec0 7972 qemu_img_convert($ovmf_vars, $volid, $vars_size_b, undef, 0);
340dbcf7 7973 my ($size) = PVE::Storage::volume_size_info($storecfg, $volid, 3);
3e1f1122 7974
340dbcf7 7975 return ($volid, $size/1024);
3e1f1122
TL
7976}
7977
22de899a
AD
7978sub vm_iothreads_list {
7979 my ($vmid) = @_;
7980
0a13e08e 7981 my $res = mon_cmd($vmid, 'query-iothreads');
22de899a
AD
7982
7983 my $iothreads = {};
7984 foreach my $iothread (@$res) {
7985 $iothreads->{ $iothread->{id} } = $iothread->{"thread-id"};
7986 }
7987
7988 return $iothreads;
7989}
7990
ee034f5c
AD
7991sub scsihw_infos {
7992 my ($conf, $drive) = @_;
7993
7994 my $maxdev = 0;
7995
7fe1b688 7996 if (!$conf->{scsihw} || ($conf->{scsihw} =~ m/^lsi/)) {
ee034f5c 7997 $maxdev = 7;
a1511b3c 7998 } elsif ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) {
ee034f5c
AD
7999 $maxdev = 1;
8000 } else {
8001 $maxdev = 256;
8002 }
8003
8004 my $controller = int($drive->{index} / $maxdev);
4df98f2f
TL
8005 my $controller_prefix = ($conf->{scsihw} && $conf->{scsihw} eq 'virtio-scsi-single')
8006 ? "virtioscsi"
8007 : "scsihw";
ee034f5c
AD
8008
8009 return ($maxdev, $controller, $controller_prefix);
8010}
a1511b3c 8011
44549149
EK
8012sub resolve_dst_disk_format {
8013 my ($storecfg, $storeid, $src_volname, $format) = @_;
8014 my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid);
8015
8016 if (!$format) {
8017 # if no target format is specified, use the source disk format as hint
8018 if ($src_volname) {
8019 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
8020 $format = qemu_img_format($scfg, $src_volname);
8021 } else {
8022 return $defFormat;
8023 }
8024 }
8025
8026 # test if requested format is supported - else use default
8027 my $supported = grep { $_ eq $format } @$validFormats;
8028 $format = $defFormat if !$supported;
8029 return $format;
8030}
8031
66cebc46
DC
8032# NOTE: if this logic changes, please update docs & possibly gui logic
8033sub find_vmstate_storage {
8034 my ($conf, $storecfg) = @_;
8035
8036 # first, return storage from conf if set
8037 return $conf->{vmstatestorage} if $conf->{vmstatestorage};
8038
8039 my ($target, $shared, $local);
8040
8041 foreach_storage_used_by_vm($conf, sub {
8042 my ($sid) = @_;
8043 my $scfg = PVE::Storage::storage_config($storecfg, $sid);
8044 my $dst = $scfg->{shared} ? \$shared : \$local;
8045 $$dst = $sid if !$$dst || $scfg->{path}; # prefer file based storage
8046 });
8047
8048 # second, use shared storage where VM has at least one disk
8049 # third, use local storage where VM has at least one disk
8050 # fall back to local storage
8051 $target = $shared // $local // 'local';
8052
8053 return $target;
8054}
8055
6ee499ff 8056sub generate_uuid {
ae2fcb3b
EK
8057 my ($uuid, $uuid_str);
8058 UUID::generate($uuid);
8059 UUID::unparse($uuid, $uuid_str);
6ee499ff
DC
8060 return $uuid_str;
8061}
8062
8063sub generate_smbios1_uuid {
8064 return "uuid=".generate_uuid();
ae2fcb3b
EK
8065}
8066
9c152e87
TL
8067sub nbd_stop {
8068 my ($vmid) = @_;
8069
0a13e08e 8070 mon_cmd($vmid, 'nbd-server-stop');
9c152e87
TL
8071}
8072
dae98db9
DC
8073sub create_reboot_request {
8074 my ($vmid) = @_;
8075 open(my $fh, '>', "/run/qemu-server/$vmid.reboot")
8076 or die "failed to create reboot trigger file: $!\n";
8077 close($fh);
8078}
8079
8080sub clear_reboot_request {
8081 my ($vmid) = @_;
8082 my $path = "/run/qemu-server/$vmid.reboot";
8083 my $res = 0;
8084
8085 $res = unlink($path);
8086 die "could not remove reboot request for $vmid: $!"
8087 if !$res && $! != POSIX::ENOENT;
8088
8089 return $res;
8090}
8091
5cfa9f5f
SR
8092sub bootorder_from_legacy {
8093 my ($conf, $bootcfg) = @_;
8094
8095 my $boot = $bootcfg->{legacy} || $boot_fmt->{legacy}->{default};
8096 my $bootindex_hash = {};
8097 my $i = 1;
8098 foreach my $o (split(//, $boot)) {
8099 $bootindex_hash->{$o} = $i*100;
8100 $i++;
8101 }
8102
8103 my $bootorder = {};
8104
8105 PVE::QemuConfig->foreach_volume($conf, sub {
8106 my ($ds, $drive) = @_;
8107
8108 if (drive_is_cdrom ($drive, 1)) {
8109 if ($bootindex_hash->{d}) {
8110 $bootorder->{$ds} = $bootindex_hash->{d};
8111 $bootindex_hash->{d} += 1;
8112 }
8113 } elsif ($bootindex_hash->{c}) {
8114 $bootorder->{$ds} = $bootindex_hash->{c}
8115 if $conf->{bootdisk} && $conf->{bootdisk} eq $ds;
8116 $bootindex_hash->{c} += 1;
8117 }
8118 });
8119
8120 if ($bootindex_hash->{n}) {
8121 for (my $i = 0; $i < $MAX_NETS; $i++) {
8122 my $netname = "net$i";
8123 next if !$conf->{$netname};
8124 $bootorder->{$netname} = $bootindex_hash->{n};
8125 $bootindex_hash->{n} += 1;
8126 }
8127 }
8128
8129 return $bootorder;
8130}
8131
8132# Generate default device list for 'boot: order=' property. Matches legacy
8133# default boot order, but with explicit device names. This is important, since
8134# the fallback for when neither 'order' nor the old format is specified relies
8135# on 'bootorder_from_legacy' above, and it would be confusing if this diverges.
8136sub get_default_bootdevices {
8137 my ($conf) = @_;
8138
8139 my @ret = ();
8140
8141 # harddisk
8142 my $first = PVE::QemuServer::Drive::resolve_first_disk($conf, 0);
8143 push @ret, $first if $first;
8144
8145 # cdrom
8146 $first = PVE::QemuServer::Drive::resolve_first_disk($conf, 1);
8147 push @ret, $first if $first;
8148
8149 # network
8150 for (my $i = 0; $i < $MAX_NETS; $i++) {
8151 my $netname = "net$i";
8152 next if !$conf->{$netname};
8153 push @ret, $netname;
8154 last;
8155 }
8156
8157 return \@ret;
8158}
8159
e5d611c3
TL
8160sub device_bootorder {
8161 my ($conf) = @_;
8162
8163 return bootorder_from_legacy($conf) if !defined($conf->{boot});
8164
8165 my $boot = parse_property_string($boot_fmt, $conf->{boot});
8166
8167 my $bootorder = {};
8168 if (!defined($boot) || $boot->{legacy}) {
8169 $bootorder = bootorder_from_legacy($conf, $boot);
8170 } elsif ($boot->{order}) {
8171 my $i = 100; # start at 100 to allow user to insert devices before us with -args
8172 for my $dev (PVE::Tools::split_list($boot->{order})) {
8173 $bootorder->{$dev} = $i++;
8174 }
8175 }
8176
8177 return $bootorder;
8178}
8179
65911545
SR
8180sub register_qmeventd_handle {
8181 my ($vmid) = @_;
8182
8183 my $fh;
8184 my $peer = "/var/run/qmeventd.sock";
8185 my $count = 0;
8186
8187 for (;;) {
8188 $count++;
8189 $fh = IO::Socket::UNIX->new(Peer => $peer, Blocking => 0, Timeout => 1);
8190 last if $fh;
8191 if ($! != EINTR && $! != EAGAIN) {
8192 die "unable to connect to qmeventd socket (vmid: $vmid) - $!\n";
8193 }
8194 if ($count > 4) {
8195 die "unable to connect to qmeventd socket (vmid: $vmid) - timeout "
8196 . "after $count retries\n";
8197 }
8198 usleep(25000);
8199 }
8200
8201 # send handshake to mark VM as backing up
8202 print $fh to_json({vzdump => {vmid => "$vmid"}});
8203
8204 # return handle to be closed later when inhibit is no longer required
8205 return $fh;
8206}
8207
65e866e5
DM
8208# bash completion helper
8209
8210sub complete_backup_archives {
8211 my ($cmdname, $pname, $cvalue) = @_;
8212
8213 my $cfg = PVE::Storage::config();
8214
8215 my $storeid;
8216
8217 if ($cvalue =~ m/^([^:]+):/) {
8218 $storeid = $1;
8219 }
8220
8221 my $data = PVE::Storage::template_list($cfg, $storeid, 'backup');
8222
8223 my $res = [];
8224 foreach my $id (keys %$data) {
8225 foreach my $item (@{$data->{$id}}) {
f43a4f12 8226 next if $item->{format} !~ m/^vma\.(${\PVE::Storage::Plugin::COMPRESSOR_RE})$/;
65e866e5
DM
8227 push @$res, $item->{volid} if defined($item->{volid});
8228 }
8229 }
8230
8231 return $res;
8232}
8233
8234my $complete_vmid_full = sub {
8235 my ($running) = @_;
8236
8237 my $idlist = vmstatus();
8238
8239 my $res = [];
8240
8241 foreach my $id (keys %$idlist) {
8242 my $d = $idlist->{$id};
8243 if (defined($running)) {
8244 next if $d->{template};
8245 next if $running && $d->{status} ne 'running';
8246 next if !$running && $d->{status} eq 'running';
8247 }
8248 push @$res, $id;
8249
8250 }
8251 return $res;
8252};
8253
8254sub complete_vmid {
8255 return &$complete_vmid_full();
8256}
8257
8258sub complete_vmid_stopped {
8259 return &$complete_vmid_full(0);
8260}
8261
8262sub complete_vmid_running {
8263 return &$complete_vmid_full(1);
8264}
8265
335af808
DM
8266sub complete_storage {
8267
8268 my $cfg = PVE::Storage::config();
8269 my $ids = $cfg->{ids};
8270
8271 my $res = [];
8272 foreach my $sid (keys %$ids) {
8273 next if !PVE::Storage::storage_check_enabled($cfg, $sid, undef, 1);
c4c844ef 8274 next if !$ids->{$sid}->{content}->{images};
335af808
DM
8275 push @$res, $sid;
8276 }
8277
8278 return $res;
8279}
8280
255e9c54
AL
8281sub complete_migration_storage {
8282 my ($cmd, $param, $current_value, $all_args) = @_;
8283
8284 my $targetnode = @$all_args[1];
8285
8286 my $cfg = PVE::Storage::config();
8287 my $ids = $cfg->{ids};
8288
8289 my $res = [];
8290 foreach my $sid (keys %$ids) {
8291 next if !PVE::Storage::storage_check_enabled($cfg, $sid, $targetnode, 1);
8292 next if !$ids->{$sid}->{content}->{images};
8293 push @$res, $sid;
8294 }
8295
8296 return $res;
8297}
8298
b08c37c3
DC
8299sub vm_is_paused {
8300 my ($vmid) = @_;
8301 my $qmpstatus = eval {
8302 PVE::QemuConfig::assert_config_exists_on_node($vmid);
8303 mon_cmd($vmid, "query-status");
8304 };
8305 warn "$@\n" if $@;
8306 return $qmpstatus && $qmpstatus->{status} eq "paused";
8307}
8308
3f11f0d7
LS
8309sub check_volume_storage_type {
8310 my ($storecfg, $vol) = @_;
8311
8312 my ($storeid, $volname) = PVE::Storage::parse_volume_id($vol);
8313 my $scfg = PVE::Storage::storage_config($storecfg, $storeid);
8314 my ($vtype) = PVE::Storage::parse_volname($storecfg, $vol);
8315
8316 die "storage '$storeid' does not support content-type '$vtype'\n"
8317 if !$scfg->{content}->{$vtype};
8318
8319 return 1;
8320}
8321
21947fea
AD
8322sub add_nets_bridge_fdb {
8323 my ($conf, $vmid) = @_;
8324
1b5ba4dd
TL
8325 for my $opt (keys %$conf) {
8326 next if $opt !~ m/^net(\d+)$/;
8327 my $iface = "tap${vmid}i$1";
4ddd2ca2
TL
8328 # NOTE: expect setups with learning off to *not* use auto-random-generation of MAC on start
8329 my $net = parse_net($conf->{$opt}, 1) or next;
8330
8331 my $mac = $net->{macaddr};
8332 if (!$mac) {
8333 log_warn("MAC learning disabled, but vNIC '$iface' has no static MAC to add to forwarding DB!")
8334 if !file_read_firstline("/sys/class/net/$iface/brport/learning");
8335 next;
8336 }
21947fea 8337
1b5ba4dd
TL
8338 if ($have_sdn) {
8339 PVE::Network::SDN::Zones::add_bridge_fdb($iface, $mac, $net->{bridge}, $net->{firewall});
8340 } else {
8341 PVE::Network::add_bridge_fdb($iface, $mac, $net->{firewall});
21947fea
AD
8342 }
8343 }
8344}
1b5ba4dd 8345
73ed6496
AD
8346sub del_nets_bridge_fdb {
8347 my ($conf, $vmid) = @_;
8348
8349 for my $opt (keys %$conf) {
8350 next if $opt !~ m/^net(\d+)$/;
8351 my $iface = "tap${vmid}i$1";
8352
8353 my $net = parse_net($conf->{$opt}) or next;
8354 my $mac = $net->{macaddr} or next;
8355
8356 if ($have_sdn) {
8357 PVE::Network::SDN::Zones::del_bridge_fdb($iface, $mac, $net->{bridge}, $net->{firewall});
8358 } else {
8359 PVE::Network::del_bridge_fdb($iface, $mac, $net->{firewall});
8360 }
8361 }
8362}
8363
1e3baf05 83641;