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