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