]>
Commit | Line | Data |
---|---|---|
1e3baf05 DM |
1 | package PVE::QemuServer; |
2 | ||
3 | use strict; | |
990fc5e2 | 4 | use warnings; |
1e3baf05 DM |
5 | use POSIX; |
6 | use IO::Handle; | |
7 | use IO::Select; | |
8 | use IO::File; | |
9 | use IO::Dir; | |
10 | use IO::Socket::UNIX; | |
11 | use File::Basename; | |
12 | use File::Path; | |
13 | use File::stat; | |
14 | use Getopt::Long; | |
fc1ddcdc | 15 | use Digest::SHA; |
1e3baf05 DM |
16 | use Fcntl ':flock'; |
17 | use Cwd 'abs_path'; | |
18 | use IPC::Open3; | |
c971c4f2 | 19 | use JSON; |
1e3baf05 DM |
20 | use Fcntl; |
21 | use PVE::SafeSyslog; | |
22 | use Storable qw(dclone); | |
23 | use PVE::Exception qw(raise raise_param_exc); | |
24 | use PVE::Storage; | |
4543ecf0 | 25 | use PVE::Tools qw(run_command lock_file lock_file_full file_read_firstline dir_glob_foreach); |
b7ba6b79 | 26 | use PVE::JSONSchema qw(get_standard_option); |
1e3baf05 DM |
27 | use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file); |
28 | use PVE::INotify; | |
29 | use PVE::ProcFSTools; | |
ffda963f | 30 | use PVE::QemuConfig; |
26f11676 | 31 | use PVE::QMPClient; |
91bd6c90 | 32 | use PVE::RPCEnvironment; |
6b64503e | 33 | use Time::HiRes qw(gettimeofday); |
a783c78e | 34 | use File::Copy qw(copy); |
46630a5f | 35 | use URI::Escape; |
1e3baf05 | 36 | |
e5eaa028 WL |
37 | my $qemu_snap_storage = {rbd => 1, sheepdog => 1}; |
38 | ||
7f0b5beb | 39 | my $cpuinfo = PVE::ProcFSTools::read_cpuinfo(); |
1e3baf05 | 40 | |
19672434 | 41 | # Note about locking: we use flock on the config file protect |
1e3baf05 DM |
42 | # against concurent actions. |
43 | # Aditionaly, we have a 'lock' setting in the config file. This | |
22c377f0 | 44 | # can be set to 'migrate', 'backup', 'snapshot' or 'rollback'. Most actions are not |
1e3baf05 DM |
45 | # allowed when such lock is set. But you can ignore this kind of |
46 | # lock with the --skiplock flag. | |
47 | ||
97d62eb7 | 48 | cfs_register_file('/qemu-server/', |
1858638f DM |
49 | \&parse_vm_config, |
50 | \&write_vm_config); | |
1e3baf05 | 51 | |
3ea94c60 DM |
52 | PVE::JSONSchema::register_standard_option('skiplock', { |
53 | description => "Ignore locks - only root is allowed to use this option.", | |
afdb31d5 | 54 | type => 'boolean', |
3ea94c60 DM |
55 | optional => 1, |
56 | }); | |
57 | ||
58 | PVE::JSONSchema::register_standard_option('pve-qm-stateuri', { | |
59 | description => "Some command save/restore state from this location.", | |
60 | type => 'string', | |
61 | maxLength => 128, | |
62 | optional => 1, | |
63 | }); | |
64 | ||
8abd398b DM |
65 | PVE::JSONSchema::register_standard_option('pve-snapshot-name', { |
66 | description => "The name of the snapshot.", | |
67 | type => 'string', format => 'pve-configid', | |
68 | maxLength => 40, | |
69 | }); | |
70 | ||
1e3baf05 DM |
71 | #no warnings 'redefine'; |
72 | ||
c8effec3 AD |
73 | sub cgroups_write { |
74 | my ($controller, $vmid, $option, $value) = @_; | |
75 | ||
3a515a88 DM |
76 | my $path = "/sys/fs/cgroup/$controller/qemu.slice/$vmid.scope/$option"; |
77 | PVE::ProcFSTools::write_proc_entry($path, $value); | |
c8effec3 AD |
78 | |
79 | } | |
80 | ||
1e3baf05 DM |
81 | my $nodename = PVE::INotify::nodename(); |
82 | ||
83 | mkdir "/etc/pve/nodes/$nodename"; | |
84 | my $confdir = "/etc/pve/nodes/$nodename/qemu-server"; | |
85 | mkdir $confdir; | |
86 | ||
87 | my $var_run_tmpdir = "/var/run/qemu-server"; | |
88 | mkdir $var_run_tmpdir; | |
89 | ||
90 | my $lock_dir = "/var/lock/qemu-server"; | |
91 | mkdir $lock_dir; | |
92 | ||
93 | my $pcisysfs = "/sys/bus/pci"; | |
94 | ||
8930da74 DM |
95 | my $cpu_vendor_list = { |
96 | # Intel CPUs | |
97 | 486 => 'GenuineIntel', | |
98 | pentium => 'GenuineIntel', | |
99 | pentium2 => 'GenuineIntel', | |
100 | pentium3 => 'GenuineIntel', | |
101 | coreduo => 'GenuineIntel', | |
102 | core2duo => 'GenuineIntel', | |
103 | Conroe => 'GenuineIntel', | |
104 | Penryn => 'GenuineIntel', | |
105 | Nehalem => 'GenuineIntel', | |
106 | Westmere => 'GenuineIntel', | |
107 | SandyBridge => 'GenuineIntel', | |
108 | IvyBridge => 'GenuineIntel', | |
109 | Haswell => 'GenuineIntel', | |
110 | 'Haswell-noTSX' => 'GenuineIntel', | |
111 | Broadwell => 'GenuineIntel', | |
112 | 'Broadwell-noTSX' => 'GenuineIntel', | |
113 | ||
114 | # AMD CPUs | |
115 | athlon => 'AuthenticAMD', | |
116 | phenom => 'AuthenticAMD', | |
117 | Opteron_G1 => 'AuthenticAMD', | |
118 | Opteron_G2 => 'AuthenticAMD', | |
119 | Opteron_G3 => 'AuthenticAMD', | |
120 | Opteron_G4 => 'AuthenticAMD', | |
121 | Opteron_G5 => 'AuthenticAMD', | |
122 | ||
123 | # generic types, use vendor from host node | |
124 | host => 'default', | |
125 | kvm32 => 'default', | |
126 | kvm64 => 'default', | |
127 | qemu32 => 'default', | |
128 | qemu64 => 'default', | |
129 | }; | |
130 | ||
ff6ffe20 | 131 | my $cpu_fmt = { |
16a91d65 WB |
132 | cputype => { |
133 | description => "Emulated CPU type.", | |
134 | type => 'string', | |
7f694a71 | 135 | enum => [ sort { "\L$a" cmp "\L$b" } keys %$cpu_vendor_list ], |
bb9207e0 | 136 | format_description => 'cputype', |
16a91d65 WB |
137 | default => 'kvm64', |
138 | default_key => 1, | |
139 | }, | |
140 | hidden => { | |
141 | description => "Do not identify as a KVM virtual machine.", | |
142 | type => 'boolean', | |
143 | optional => 1, | |
144 | default => 0 | |
145 | }, | |
146 | }; | |
147 | ||
ec3582b5 WB |
148 | my $watchdog_fmt = { |
149 | model => { | |
150 | default_key => 1, | |
151 | type => 'string', | |
152 | enum => [qw(i6300esb ib700)], | |
153 | description => "Watchdog type to emulate.", | |
154 | default => 'i6300esb', | |
155 | optional => 1, | |
156 | }, | |
157 | action => { | |
158 | type => 'string', | |
159 | enum => [qw(reset shutdown poweroff pause debug none)], | |
160 | description => "The action to perform if after activation the guest fails to poll the watchdog in time.", | |
161 | optional => 1, | |
162 | }, | |
163 | }; | |
164 | PVE::JSONSchema::register_format('pve-qm-watchdog', $watchdog_fmt); | |
165 | ||
1e3baf05 DM |
166 | my $confdesc = { |
167 | onboot => { | |
168 | optional => 1, | |
169 | type => 'boolean', | |
170 | description => "Specifies whether a VM will be started during system bootup.", | |
171 | default => 0, | |
172 | }, | |
173 | autostart => { | |
174 | optional => 1, | |
175 | type => 'boolean', | |
176 | description => "Automatic restart after crash (currently ignored).", | |
177 | default => 0, | |
178 | }, | |
2ff09f52 DA |
179 | hotplug => { |
180 | optional => 1, | |
b3c2bdd1 DM |
181 | type => 'string', format => 'pve-hotplug-features', |
182 | 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'.", | |
183 | default => 'network,disk,usb', | |
2ff09f52 | 184 | }, |
1e3baf05 DM |
185 | reboot => { |
186 | optional => 1, | |
187 | type => 'boolean', | |
188 | description => "Allow reboot. If set to '0' the VM exit on reboot.", | |
189 | default => 1, | |
190 | }, | |
191 | lock => { | |
192 | optional => 1, | |
193 | type => 'string', | |
194 | description => "Lock/unlock the VM.", | |
22c377f0 | 195 | enum => [qw(migrate backup snapshot rollback)], |
1e3baf05 DM |
196 | }, |
197 | cpulimit => { | |
198 | optional => 1, | |
c6f773b8 | 199 | type => 'number', |
52261945 DM |
200 | description => "Limit of CPU usage.", |
201 | 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 | 202 | minimum => 0, |
c6f773b8 | 203 | maximum => 128, |
52261945 | 204 | default => 0, |
1e3baf05 DM |
205 | }, |
206 | cpuunits => { | |
207 | optional => 1, | |
208 | type => 'integer', | |
52261945 DM |
209 | description => "CPU weight for a VM.", |
210 | 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.\n\nNOTE: You can disable fair-scheduler configuration by setting this to 0.", | |
1e3baf05 DM |
211 | minimum => 0, |
212 | maximum => 500000, | |
213 | default => 1000, | |
214 | }, | |
215 | memory => { | |
216 | optional => 1, | |
217 | type => 'integer', | |
7878afeb | 218 | description => "Amount of RAM for the VM in MB. This is the maximum available memory when you use the balloon device.", |
1e3baf05 DM |
219 | minimum => 16, |
220 | default => 512, | |
221 | }, | |
13a48620 DA |
222 | balloon => { |
223 | optional => 1, | |
224 | type => 'integer', | |
8b1accf7 DM |
225 | description => "Amount of target RAM for the VM in MB. Using zero disables the ballon driver.", |
226 | minimum => 0, | |
227 | }, | |
228 | shares => { | |
229 | optional => 1, | |
230 | type => 'integer', | |
231 | 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", | |
232 | minimum => 0, | |
233 | maximum => 50000, | |
234 | default => 1000, | |
13a48620 | 235 | }, |
1e3baf05 DM |
236 | keyboard => { |
237 | optional => 1, | |
238 | type => 'string', | |
fad17f04 | 239 | description => "Keybord layout for vnc server. Default is read from the '/etc/pve/datacenter.conf' configuration file.", |
e95fe75f | 240 | enum => PVE::Tools::kvmkeymaplist(), |
1e3baf05 DM |
241 | default => 'en-us', |
242 | }, | |
243 | name => { | |
244 | optional => 1, | |
7fabe17d | 245 | type => 'string', format => 'dns-name', |
1e3baf05 DM |
246 | description => "Set a name for the VM. Only used on the configuration web interface.", |
247 | }, | |
cdd20088 AD |
248 | scsihw => { |
249 | optional => 1, | |
250 | type => 'string', | |
52261945 | 251 | description => "SCSI controller model", |
6731a4cf | 252 | enum => [qw(lsi lsi53c810 virtio-scsi-pci virtio-scsi-single megasas pvscsi)], |
cdd20088 AD |
253 | default => 'lsi', |
254 | }, | |
1e3baf05 DM |
255 | description => { |
256 | optional => 1, | |
257 | type => 'string', | |
0581fe4f | 258 | description => "Description for the VM. Only used on the configuration web interface. This is saved as comment inside the configuration file.", |
1e3baf05 DM |
259 | }, |
260 | ostype => { | |
261 | optional => 1, | |
262 | type => 'string', | |
6b9d84cf | 263 | enum => [qw(other wxp w2k w2k3 w2k8 wvista win7 win8 l24 l26 solaris)], |
52261945 DM |
264 | description => "Specify guest operating system.", |
265 | verbose_description => <<EODESC, | |
266 | Specify guest operating system. This is used to enable special | |
267 | optimization/features for specific operating systems: | |
268 | ||
269 | [horizontal] | |
270 | other;; unspecified OS | |
271 | wxp;; Microsoft Windows XP | |
272 | w2k;; Microsoft Windows 2000 | |
273 | w2k3;; Microsoft Windows 2003 | |
274 | w2k8;; Microsoft Windows 2008 | |
275 | wvista;; Microsoft Windows Vista | |
276 | win7;; Microsoft Windows 7 | |
277 | win8;; Microsoft Windows 8/2012 | |
278 | l24;; Linux 2.4 Kernel | |
279 | l26;; Linux 2.6/3.X Kernel | |
280 | solaris;; Solaris/OpenSolaris/OpenIndiania kernel | |
1e3baf05 DM |
281 | EODESC |
282 | }, | |
283 | boot => { | |
284 | optional => 1, | |
285 | type => 'string', | |
286 | description => "Boot on floppy (a), hard disk (c), CD-ROM (d), or network (n).", | |
287 | pattern => '[acdn]{1,4}', | |
32baffb4 | 288 | default => 'cdn', |
1e3baf05 DM |
289 | }, |
290 | bootdisk => { | |
291 | optional => 1, | |
292 | type => 'string', format => 'pve-qm-bootdisk', | |
293 | description => "Enable booting from specified disk.", | |
03e480fc | 294 | pattern => '(ide|sata|scsi|virtio)\d+', |
1e3baf05 DM |
295 | }, |
296 | smp => { | |
297 | optional => 1, | |
298 | type => 'integer', | |
299 | description => "The number of CPUs. Please use option -sockets instead.", | |
300 | minimum => 1, | |
301 | default => 1, | |
302 | }, | |
303 | sockets => { | |
304 | optional => 1, | |
305 | type => 'integer', | |
306 | description => "The number of CPU sockets.", | |
307 | minimum => 1, | |
308 | default => 1, | |
309 | }, | |
310 | cores => { | |
311 | optional => 1, | |
312 | type => 'integer', | |
313 | description => "The number of cores per socket.", | |
314 | minimum => 1, | |
315 | default => 1, | |
316 | }, | |
8a010eae AD |
317 | numa => { |
318 | optional => 1, | |
319 | type => 'boolean', | |
1917695c | 320 | description => "Enable/disable NUMA.", |
8a010eae AD |
321 | default => 0, |
322 | }, | |
de9d1e55 | 323 | vcpus => { |
3bd18e48 AD |
324 | optional => 1, |
325 | type => 'integer', | |
de9d1e55 | 326 | description => "Number of hotplugged vcpus.", |
3bd18e48 | 327 | minimum => 1, |
de9d1e55 | 328 | default => 0, |
3bd18e48 | 329 | }, |
1e3baf05 DM |
330 | acpi => { |
331 | optional => 1, | |
332 | type => 'boolean', | |
333 | description => "Enable/disable ACPI.", | |
334 | default => 1, | |
335 | }, | |
bc84dcca | 336 | agent => { |
ab6a046f AD |
337 | optional => 1, |
338 | type => 'boolean', | |
339 | description => "Enable/disable Qemu GuestAgent.", | |
be79c214 | 340 | default => 0, |
ab6a046f | 341 | }, |
1e3baf05 DM |
342 | kvm => { |
343 | optional => 1, | |
344 | type => 'boolean', | |
345 | description => "Enable/disable KVM hardware virtualization.", | |
346 | default => 1, | |
347 | }, | |
348 | tdf => { | |
349 | optional => 1, | |
350 | type => 'boolean', | |
8c559505 DM |
351 | description => "Enable/disable time drift fix.", |
352 | default => 0, | |
1e3baf05 | 353 | }, |
19672434 | 354 | localtime => { |
1e3baf05 DM |
355 | optional => 1, |
356 | type => 'boolean', | |
357 | description => "Set the real time clock to local time. This is enabled by default if ostype indicates a Microsoft OS.", | |
358 | }, | |
359 | freeze => { | |
360 | optional => 1, | |
361 | type => 'boolean', | |
362 | description => "Freeze CPU at startup (use 'c' monitor command to start execution).", | |
363 | }, | |
364 | vga => { | |
365 | optional => 1, | |
366 | type => 'string', | |
52261945 DM |
367 | description => "Select the VGA type.", |
368 | verbose_description => "Select the VGA type. If you want to use high resolution" . | |
1917695c TL |
369 | " modes (>= 1280x1024x16) then you should use the options " . |
370 | "'std' or 'vmware'. Default is 'std' for win8/win7/w2k8, and " . | |
371 | "'cirrus' for other OS types. The 'qxl' option enables the SPICE " . | |
372 | "display sever. For win* OS you can select how many independent " . | |
373 | "displays you want, Linux guests can add displays them self. " . | |
374 | "You can also run without any graphic card, using a serial device" . | |
375 | " as terminal.", | |
2fa3151e | 376 | enum => [qw(std cirrus vmware qxl serial0 serial1 serial2 serial3 qxl2 qxl3 qxl4)], |
1e3baf05 | 377 | }, |
0ea9541d DM |
378 | watchdog => { |
379 | optional => 1, | |
380 | type => 'string', format => 'pve-qm-watchdog', | |
52261945 DM |
381 | description => "Create a virtual hardware watchdog device.", |
382 | verbose_description => "Create a virtual hardware watchdog device. Once enabled" . | |
1917695c TL |
383 | " (by a guest action), the watchdog must be periodically polled " . |
384 | "by an agent inside the guest or else the watchdog will reset " . | |
385 | "the guest (or execute the respective action specified)", | |
0ea9541d | 386 | }, |
1e3baf05 DM |
387 | startdate => { |
388 | optional => 1, | |
19672434 | 389 | type => 'string', |
1e3baf05 DM |
390 | typetext => "(now | YYYY-MM-DD | YYYY-MM-DDTHH:MM:SS)", |
391 | description => "Set the initial date of the real time clock. Valid format for date are: 'now' or '2006-06-17T16:01:21' or '2006-06-17'.", | |
392 | pattern => '(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)', | |
393 | default => 'now', | |
394 | }, | |
43574f73 | 395 | startup => get_standard_option('pve-startup-order'), |
68eda3ab AD |
396 | template => { |
397 | optional => 1, | |
398 | type => 'boolean', | |
399 | description => "Enable/disable Template.", | |
400 | default => 0, | |
401 | }, | |
1e3baf05 DM |
402 | args => { |
403 | optional => 1, | |
404 | type => 'string', | |
52261945 DM |
405 | description => "Arbitrary arguments passed to kvm.", |
406 | verbose_description => <<EODESCR, | |
c7a8aad6 | 407 | Arbitrary arguments passed to kvm, for example: |
1e3baf05 DM |
408 | |
409 | args: -no-reboot -no-hpet | |
c7a8aad6 FG |
410 | |
411 | NOTE: this option is for experts only. | |
1e3baf05 DM |
412 | EODESCR |
413 | }, | |
414 | tablet => { | |
415 | optional => 1, | |
416 | type => 'boolean', | |
417 | default => 1, | |
52261945 DM |
418 | description => "Enable/disable the USB tablet device.", |
419 | verbose_description => "Enable/disable the USB tablet device. This device is " . | |
1917695c TL |
420 | "usually needed to allow absolute mouse positioning with VNC. " . |
421 | "Else the mouse runs out of sync with normal VNC clients. " . | |
422 | "If you're running lots of console-only guests on one host, " . | |
423 | "you may consider disabling this to save some context switches. " . | |
424 | "This is turned off by default if you use spice (-vga=qxl).", | |
1e3baf05 DM |
425 | }, |
426 | migrate_speed => { | |
427 | optional => 1, | |
428 | type => 'integer', | |
429 | description => "Set maximum speed (in MB/s) for migrations. Value 0 is no limit.", | |
430 | minimum => 0, | |
431 | default => 0, | |
432 | }, | |
433 | migrate_downtime => { | |
434 | optional => 1, | |
04432191 | 435 | type => 'number', |
1e3baf05 DM |
436 | description => "Set maximum tolerated downtime (in seconds) for migrations.", |
437 | minimum => 0, | |
04432191 | 438 | default => 0.1, |
1e3baf05 DM |
439 | }, |
440 | cdrom => { | |
441 | optional => 1, | |
442 | type => 'string', format => 'pve-qm-drive', | |
443 | typetext => 'volume', | |
444 | description => "This is an alias for option -ide2", | |
445 | }, | |
446 | cpu => { | |
447 | optional => 1, | |
448 | description => "Emulated CPU type.", | |
449 | type => 'string', | |
ff6ffe20 | 450 | format => $cpu_fmt, |
1e3baf05 | 451 | }, |
b7ba6b79 DM |
452 | parent => get_standard_option('pve-snapshot-name', { |
453 | optional => 1, | |
454 | description => "Parent snapshot name. This is used internally, and should not be modified.", | |
455 | }), | |
982c7f12 DM |
456 | snaptime => { |
457 | optional => 1, | |
458 | description => "Timestamp for snapshots.", | |
459 | type => 'integer', | |
460 | minimum => 0, | |
461 | }, | |
18bfb361 DM |
462 | vmstate => { |
463 | optional => 1, | |
464 | type => 'string', format => 'pve-volume-id', | |
465 | description => "Reference to a volume which stores the VM state. This is used internally for snapshots.", | |
466 | }, | |
3bafc510 DM |
467 | machine => { |
468 | description => "Specific the Qemu machine type.", | |
469 | type => 'string', | |
7bac824e | 470 | pattern => '(pc|pc(-i440fx)?-\d+\.\d+(\.pxe)?|q35|pc-q35-\d+\.\d+(\.pxe)?)', |
3bafc510 DM |
471 | maxLength => 40, |
472 | optional => 1, | |
473 | }, | |
2796e7d5 DM |
474 | smbios1 => { |
475 | description => "Specify SMBIOS type 1 fields.", | |
476 | type => 'string', format => 'pve-qm-smbios1', | |
2796e7d5 DM |
477 | maxLength => 256, |
478 | optional => 1, | |
479 | }, | |
cb0e4540 AG |
480 | protection => { |
481 | optional => 1, | |
482 | type => 'boolean', | |
52261945 | 483 | description => "Sets the protection flag of the VM. This will disable the remove VM and remove disk operations.", |
cb0e4540 AG |
484 | default => 0, |
485 | }, | |
3edb45e7 | 486 | bios => { |
a783c78e | 487 | optional => 1, |
3edb45e7 DM |
488 | type => 'string', |
489 | enum => [ qw(seabios ovmf) ], | |
490 | description => "Select BIOS implementation.", | |
491 | default => 'seabios', | |
a783c78e | 492 | }, |
1e3baf05 DM |
493 | }; |
494 | ||
495 | # what about other qemu settings ? | |
496 | #cpu => 'string', | |
497 | #machine => 'string', | |
498 | #fda => 'file', | |
499 | #fdb => 'file', | |
500 | #mtdblock => 'file', | |
501 | #sd => 'file', | |
502 | #pflash => 'file', | |
503 | #snapshot => 'bool', | |
504 | #bootp => 'file', | |
505 | ##tftp => 'dir', | |
506 | ##smb => 'dir', | |
507 | #kernel => 'file', | |
508 | #append => 'string', | |
509 | #initrd => 'file', | |
510 | ##soundhw => 'string', | |
511 | ||
512 | while (my ($k, $v) = each %$confdesc) { | |
513 | PVE::JSONSchema::register_standard_option("pve-qm-$k", $v); | |
514 | } | |
515 | ||
516 | my $MAX_IDE_DISKS = 4; | |
f62db2a4 | 517 | my $MAX_SCSI_DISKS = 14; |
a2650619 | 518 | my $MAX_VIRTIO_DISKS = 16; |
cdb0931f | 519 | my $MAX_SATA_DISKS = 6; |
1e3baf05 | 520 | my $MAX_USB_DEVICES = 5; |
5bdcf937 | 521 | my $MAX_NETS = 32; |
1e3baf05 | 522 | my $MAX_UNUSED_DISKS = 8; |
5cffb2d2 | 523 | my $MAX_HOSTPCI_DEVICES = 4; |
bae179aa | 524 | my $MAX_SERIAL_PORTS = 4; |
1989a89c | 525 | my $MAX_PARALLEL_PORTS = 3; |
2ed5d572 | 526 | my $MAX_NUMA = 8; |
4d3f29ed AD |
527 | my $MAX_MEM = 4194304; |
528 | my $STATICMEM = 1024; | |
2ed5d572 | 529 | |
ffc0d8c7 WB |
530 | my $numa_fmt = { |
531 | cpus => { | |
532 | type => "string", | |
533 | pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/, | |
52261945 | 534 | description => "CPUs accessing this NUMA node.", |
ffc0d8c7 WB |
535 | format_description => "id[-id];...", |
536 | }, | |
537 | memory => { | |
538 | type => "number", | |
52261945 | 539 | description => "Amount of memory this NUMA node provides.", |
ffc0d8c7 WB |
540 | optional => 1, |
541 | }, | |
542 | hostnodes => { | |
543 | type => "string", | |
544 | pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/, | |
52261945 | 545 | description => "Host NUMA nodes to use.", |
ffc0d8c7 WB |
546 | format_description => "id[-id];...", |
547 | optional => 1, | |
548 | }, | |
549 | policy => { | |
550 | type => 'string', | |
551 | enum => [qw(preferred bind interleave)], | |
52261945 | 552 | description => "NUMA allocation policy.", |
ffc0d8c7 WB |
553 | optional => 1, |
554 | }, | |
555 | }; | |
556 | PVE::JSONSchema::register_format('pve-qm-numanode', $numa_fmt); | |
2ed5d572 AD |
557 | my $numadesc = { |
558 | optional => 1, | |
ffc0d8c7 | 559 | type => 'string', format => $numa_fmt, |
52261945 | 560 | description => "NUMA topology.", |
2ed5d572 AD |
561 | }; |
562 | PVE::JSONSchema::register_standard_option("pve-qm-numanode", $numadesc); | |
563 | ||
564 | for (my $i = 0; $i < $MAX_NUMA; $i++) { | |
565 | $confdesc->{"numa$i"} = $numadesc; | |
566 | } | |
1e3baf05 DM |
567 | |
568 | my $nic_model_list = ['rtl8139', 'ne2k_pci', 'e1000', 'pcnet', 'virtio', | |
55034103 KT |
569 | 'ne2k_isa', 'i82551', 'i82557b', 'i82559er', 'vmxnet3', |
570 | 'e1000-82540em', 'e1000-82544gc', 'e1000-82545em']; | |
6b64503e | 571 | my $nic_model_list_txt = join(' ', sort @$nic_model_list); |
1e3baf05 | 572 | |
52261945 DM |
573 | my $net_fmt_bridge_descr = <<__EOD__; |
574 | Bridge to attach the network device to. The Proxmox VE standard bridge | |
575 | is called 'vmbr0'. | |
576 | ||
577 | If you do not specify a bridge, we create a kvm user (NATed) network | |
578 | device, which provides DHCP and DNS services. The following addresses | |
579 | are used: | |
580 | ||
581 | 10.0.2.2 Gateway | |
582 | 10.0.2.3 DNS Server | |
583 | 10.0.2.4 SMB Server | |
584 | ||
585 | The DHCP server assign addresses to the guest starting from 10.0.2.15. | |
586 | __EOD__ | |
587 | ||
cd9c34d1 WB |
588 | my $net_fmt = { |
589 | macaddr => { | |
590 | type => 'string', | |
591 | pattern => qr/[0-9a-f]{2}(?::[0-9a-f]{2}){5}/i, | |
52261945 | 592 | description => "MAC address. That address must be unique withing your network. This is automatically generated if not specified.", |
cd9c34d1 WB |
593 | format_description => "XX:XX:XX:XX:XX:XX", |
594 | optional => 1, | |
595 | }, | |
7f694a71 DM |
596 | model => { |
597 | type => 'string', | |
52261945 | 598 | description => "Network Card Model. The 'virtio' model provides the best performance with very low CPU overhead. If your guest does not support this driver, it is usually best to use 'e1000'.", |
7f694a71 DM |
599 | format_description => 'model', |
600 | enum => $nic_model_list, | |
601 | default_key => 1, | |
602 | }, | |
603 | (map { $_ => { keyAlias => 'model', alias => 'macaddr' }} @$nic_model_list), | |
cd9c34d1 WB |
604 | bridge => { |
605 | type => 'string', | |
52261945 | 606 | description => $net_fmt_bridge_descr, |
cd9c34d1 WB |
607 | format_description => 'bridge', |
608 | optional => 1, | |
609 | }, | |
610 | queues => { | |
611 | type => 'integer', | |
612 | minimum => 0, maximum => 16, | |
613 | description => 'Number of packet queues to be used on the device.', | |
cd9c34d1 WB |
614 | optional => 1, |
615 | }, | |
616 | rate => { | |
617 | type => 'number', | |
618 | minimum => 0, | |
52261945 | 619 | description => "Rate limit in mbps (megabytes per second) as floating point number.", |
cd9c34d1 WB |
620 | optional => 1, |
621 | }, | |
622 | tag => { | |
623 | type => 'integer', | |
9f41a659 | 624 | minimum => 1, maximum => 4094, |
cd9c34d1 | 625 | description => 'VLAN tag to apply to packets on this interface.', |
cd9c34d1 WB |
626 | optional => 1, |
627 | }, | |
628 | trunks => { | |
629 | type => 'string', | |
630 | pattern => qr/\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*/, | |
631 | description => 'VLAN trunks to pass through this interface.', | |
7f694a71 | 632 | format_description => 'vlanid[;vlanid...]', |
cd9c34d1 WB |
633 | optional => 1, |
634 | }, | |
635 | firewall => { | |
636 | type => 'boolean', | |
637 | description => 'Whether this interface should be protected by the firewall.', | |
cd9c34d1 WB |
638 | optional => 1, |
639 | }, | |
640 | link_down => { | |
641 | type => 'boolean', | |
52261945 | 642 | description => 'Whether this interface should be disconnected (like pulling the plug).', |
cd9c34d1 WB |
643 | optional => 1, |
644 | }, | |
645 | }; | |
52261945 | 646 | |
1e3baf05 DM |
647 | my $netdesc = { |
648 | optional => 1, | |
7f694a71 | 649 | type => 'string', format => $net_fmt, |
52261945 | 650 | description => "Specify network devices.", |
1e3baf05 | 651 | }; |
52261945 | 652 | |
1e3baf05 DM |
653 | PVE::JSONSchema::register_standard_option("pve-qm-net", $netdesc); |
654 | ||
655 | for (my $i = 0; $i < $MAX_NETS; $i++) { | |
656 | $confdesc->{"net$i"} = $netdesc; | |
657 | } | |
658 | ||
ffa42b86 DC |
659 | PVE::JSONSchema::register_format('pve-volume-id-or-qm-path', \&verify_volume_id_or_qm_path); |
660 | sub verify_volume_id_or_qm_path { | |
822c8a07 WB |
661 | my ($volid, $noerr) = @_; |
662 | ||
ffa42b86 DC |
663 | if ($volid eq 'none' || $volid eq 'cdrom' || $volid =~ m|^/|) { |
664 | return $volid; | |
665 | } | |
666 | ||
667 | # if its neither 'none' nor 'cdrom' nor a path, check if its a volume-id | |
822c8a07 WB |
668 | $volid = eval { PVE::JSONSchema::check_format('pve-volume-id', $volid, '') }; |
669 | if ($@) { | |
670 | return undef if $noerr; | |
671 | die $@; | |
672 | } | |
673 | return $volid; | |
674 | } | |
675 | ||
1e3baf05 | 676 | my $drivename_hash; |
19672434 | 677 | |
0541eeb8 WB |
678 | my %drivedesc_base = ( |
679 | volume => { alias => 'file' }, | |
680 | file => { | |
93c0971c | 681 | type => 'string', |
ffa42b86 | 682 | format => 'pve-volume-id-or-qm-path', |
0541eeb8 WB |
683 | default_key => 1, |
684 | format_description => 'volume', | |
685 | description => "The drive's backing volume.", | |
686 | }, | |
687 | media => { | |
688 | type => 'string', | |
0541eeb8 WB |
689 | enum => [qw(cdrom disk)], |
690 | description => "The drive's media type.", | |
691 | default => 'disk', | |
692 | optional => 1 | |
693 | }, | |
694 | cyls => { | |
695 | type => 'integer', | |
0541eeb8 WB |
696 | description => "Force the drive's physical geometry to have a specific cylinder count.", |
697 | optional => 1 | |
698 | }, | |
699 | heads => { | |
700 | type => 'integer', | |
0541eeb8 WB |
701 | description => "Force the drive's physical geometry to have a specific head count.", |
702 | optional => 1 | |
703 | }, | |
704 | secs => { | |
705 | type => 'integer', | |
0541eeb8 WB |
706 | description => "Force the drive's physical geometry to have a specific sector count.", |
707 | optional => 1 | |
708 | }, | |
709 | trans => { | |
710 | type => 'string', | |
0541eeb8 WB |
711 | enum => [qw(none lba auto)], |
712 | description => "Force disk geometry bios translation mode.", | |
713 | optional => 1, | |
714 | }, | |
715 | snapshot => { | |
716 | type => 'boolean', | |
0541eeb8 WB |
717 | description => "Whether the drive should be included when making snapshots.", |
718 | optional => 1, | |
719 | }, | |
720 | cache => { | |
721 | type => 'string', | |
0541eeb8 WB |
722 | enum => [qw(none writethrough writeback unsafe directsync)], |
723 | description => "The drive's cache mode", | |
724 | optional => 1, | |
725 | }, | |
726 | format => { | |
727 | type => 'string', | |
7f694a71 | 728 | format_description => 'image format', |
0541eeb8 WB |
729 | enum => [qw(raw cow qcow qed qcow2 vmdk cloop)], |
730 | description => "The drive's backing file's data format.", | |
731 | optional => 1, | |
732 | }, | |
733 | size => { | |
47c28a68 WB |
734 | type => 'string', |
735 | format => 'disk-size', | |
7f694a71 | 736 | format_description => 'DiskSize', |
0541eeb8 WB |
737 | description => "Disk size. This is purely informational and has no effect.", |
738 | optional => 1, | |
739 | }, | |
740 | backup => { | |
741 | type => 'boolean', | |
0541eeb8 WB |
742 | description => "Whether the drive should be included when making backups.", |
743 | optional => 1, | |
744 | }, | |
745 | werror => { | |
746 | type => 'string', | |
0541eeb8 WB |
747 | enum => [qw(enospc ignore report stop)], |
748 | description => 'Write error action.', | |
749 | optional => 1, | |
750 | }, | |
751 | aio => { | |
752 | type => 'string', | |
0541eeb8 WB |
753 | enum => [qw(native threads)], |
754 | description => 'AIO type to use.', | |
755 | optional => 1, | |
756 | }, | |
757 | discard => { | |
758 | type => 'string', | |
0541eeb8 WB |
759 | enum => [qw(ignore on)], |
760 | description => 'Controls whether to pass discard/trim requests to the underlying storage.', | |
761 | optional => 1, | |
762 | }, | |
763 | detect_zeroes => { | |
764 | type => 'boolean', | |
765 | description => 'Controls whether to detect and try to optimize writes of zeroes.', | |
766 | optional => 1, | |
767 | }, | |
768 | serial => { | |
769 | type => 'string', | |
46630a5f | 770 | format => 'urlencoded', |
0541eeb8 | 771 | format_description => 'serial', |
ba8fc5d1 WB |
772 | maxLength => 20*3, # *3 since it's %xx url enoded |
773 | description => "The drive's reported serial number, url-encoded, up to 20 bytes long.", | |
0541eeb8 WB |
774 | optional => 1, |
775 | } | |
776 | ); | |
777 | ||
778 | my %rerror_fmt = ( | |
779 | rerror => { | |
780 | type => 'string', | |
0541eeb8 WB |
781 | enum => [qw(ignore report stop)], |
782 | description => 'Read error action.', | |
783 | optional => 1, | |
784 | }, | |
785 | ); | |
786 | ||
787 | my %iothread_fmt = ( iothread => { | |
788 | type => 'boolean', | |
0541eeb8 WB |
789 | description => "Whether to use iothreads for this drive", |
790 | optional => 1, | |
791 | }); | |
792 | ||
793 | my %model_fmt = ( | |
794 | model => { | |
795 | type => 'string', | |
46630a5f | 796 | format => 'urlencoded', |
0541eeb8 | 797 | format_description => 'model', |
ba8fc5d1 WB |
798 | maxLength => 40*3, # *3 since it's %xx url enoded |
799 | description => "The drive's reported model name, url-encoded, up to 40 bytes long.", | |
0541eeb8 WB |
800 | optional => 1, |
801 | }, | |
802 | ); | |
803 | ||
804 | my %queues_fmt = ( | |
805 | queues => { | |
806 | type => 'integer', | |
0541eeb8 WB |
807 | description => "Number of queues.", |
808 | minimum => 2, | |
809 | optional => 1 | |
810 | } | |
811 | ); | |
812 | ||
813 | my $add_throttle_desc = sub { | |
7f694a71 | 814 | my ($key, $type, $what, $unit, $longunit) = @_; |
0541eeb8 WB |
815 | $drivedesc_base{$key} = { |
816 | type => $type, | |
7f694a71 DM |
817 | format_description => $unit, |
818 | description => "Maximum $what speed in $longunit per second.", | |
0541eeb8 WB |
819 | optional => 1, |
820 | }; | |
821 | }; | |
822 | # throughput: (leaky bucket) | |
823 | $add_throttle_desc->('bps', 'integer', 'r/w speed', 'bps', 'bytes'); | |
824 | $add_throttle_desc->('bps_rd', 'integer', 'read speed', 'bps', 'bytes'); | |
825 | $add_throttle_desc->('bps_wr', 'integer', 'write speed', 'bps', 'bytes'); | |
93c0971c WB |
826 | $add_throttle_desc->('mbps', 'number', 'r/w speed', 'mbps', 'megabytes'); |
827 | $add_throttle_desc->('mbps_rd', 'number', 'read speed', 'mbps', 'megabytes'); | |
828 | $add_throttle_desc->('mbps_wr', 'number', 'write speed', 'mbps', 'megabytes'); | |
0541eeb8 WB |
829 | $add_throttle_desc->('iops', 'integer', 'r/w I/O', 'iops', 'operations'); |
830 | $add_throttle_desc->('iops_rd', 'integer', 'read I/O', 'iops', 'operations'); | |
831 | $add_throttle_desc->('iops_wr', 'integer', 'write I/O', 'iops', 'operations'); | |
832 | ||
833 | # pools: (pool of IO before throttling starts taking effect) | |
93c0971c WB |
834 | $add_throttle_desc->('mbps_max', 'number', 'unthrottled r/w pool', 'mbps', 'megabytes'); |
835 | $add_throttle_desc->('mbps_rd_max', 'number', 'unthrottled read pool', 'mbps', 'megabytes'); | |
836 | $add_throttle_desc->('mbps_wr_max', 'number', 'unthrottled write pool', 'mbps', 'megabytes'); | |
0541eeb8 WB |
837 | $add_throttle_desc->('iops_max', 'integer', 'unthrottled r/w I/O pool', 'iops', 'operations'); |
838 | $add_throttle_desc->('iops_rd_max', 'integer', 'unthrottled read I/O pool', 'iops', 'operations'); | |
839 | $add_throttle_desc->('iops_wr_max', 'integer', 'unthrottled write I/O pool', 'iops', 'operations'); | |
840 | ||
841 | my $ide_fmt = { | |
842 | %drivedesc_base, | |
843 | %rerror_fmt, | |
844 | %model_fmt, | |
845 | }; | |
846 | ||
1e3baf05 DM |
847 | my $idedesc = { |
848 | optional => 1, | |
0541eeb8 | 849 | type => 'string', format => $ide_fmt, |
3c770faa | 850 | description => "Use volume as IDE hard disk or CD-ROM (n is 0 to " .($MAX_IDE_DISKS -1) . ").", |
1e3baf05 DM |
851 | }; |
852 | PVE::JSONSchema::register_standard_option("pve-qm-ide", $idedesc); | |
853 | ||
0541eeb8 WB |
854 | my $scsi_fmt = { |
855 | %drivedesc_base, | |
856 | %iothread_fmt, | |
857 | %queues_fmt, | |
858 | }; | |
1e3baf05 DM |
859 | my $scsidesc = { |
860 | optional => 1, | |
0541eeb8 | 861 | type => 'string', format => $scsi_fmt, |
3c770faa | 862 | description => "Use volume as SCSI hard disk or CD-ROM (n is 0 to " . ($MAX_SCSI_DISKS - 1) . ").", |
1e3baf05 DM |
863 | }; |
864 | PVE::JSONSchema::register_standard_option("pve-qm-scsi", $scsidesc); | |
865 | ||
0541eeb8 WB |
866 | my $sata_fmt = { |
867 | %drivedesc_base, | |
868 | %rerror_fmt, | |
869 | }; | |
cdb0931f DA |
870 | my $satadesc = { |
871 | optional => 1, | |
0541eeb8 | 872 | type => 'string', format => $sata_fmt, |
3c770faa | 873 | description => "Use volume as SATA hard disk or CD-ROM (n is 0 to " . ($MAX_SATA_DISKS - 1). ").", |
cdb0931f DA |
874 | }; |
875 | PVE::JSONSchema::register_standard_option("pve-qm-sata", $satadesc); | |
876 | ||
0541eeb8 WB |
877 | my $virtio_fmt = { |
878 | %drivedesc_base, | |
879 | %iothread_fmt, | |
880 | %rerror_fmt, | |
881 | }; | |
1e3baf05 DM |
882 | my $virtiodesc = { |
883 | optional => 1, | |
0541eeb8 | 884 | type => 'string', format => $virtio_fmt, |
3c770faa | 885 | description => "Use volume as VIRTIO hard disk (n is 0 to " . ($MAX_VIRTIO_DISKS - 1) . ").", |
1e3baf05 DM |
886 | }; |
887 | PVE::JSONSchema::register_standard_option("pve-qm-virtio", $virtiodesc); | |
888 | ||
0541eeb8 WB |
889 | my $alldrive_fmt = { |
890 | %drivedesc_base, | |
891 | %rerror_fmt, | |
892 | %iothread_fmt, | |
893 | %model_fmt, | |
894 | %queues_fmt, | |
895 | }; | |
896 | ||
ff6ffe20 | 897 | my $usb_fmt = { |
a6b9aee4 DC |
898 | host => { |
899 | default_key => 1, | |
900 | type => 'string', format => 'pve-qm-usb-device', | |
901 | format_description => 'HOSTUSBDEVICE|spice', | |
52261945 DM |
902 | description => <<EODESCR, |
903 | The Host USB device or port or the value 'spice'. HOSTUSBDEVICE syntax is: | |
904 | ||
905 | 'bus-port(.port)*' (decimal numbers) or | |
906 | 'vendor_id:product_id' (hexadeciaml numbers) or | |
907 | 'spice' | |
908 | ||
909 | You can use the 'lsusb -t' command to list existing usb devices. | |
910 | ||
911 | NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care. | |
912 | ||
913 | The value 'spice' can be used to add a usb redirection devices for spice. | |
914 | EODESCR | |
a6b9aee4 DC |
915 | }, |
916 | usb3 => { | |
917 | optional => 1, | |
918 | type => 'boolean', | |
52261945 DM |
919 | description => "Specifies whether if given host option is a USB3 device or port (this does currently not work reliably with spice redirection and is then ignored).", |
920 | default => 0, | |
a6b9aee4 DC |
921 | }, |
922 | }; | |
923 | ||
1e3baf05 DM |
924 | my $usbdesc = { |
925 | optional => 1, | |
ff6ffe20 | 926 | type => 'string', format => $usb_fmt, |
52261945 | 927 | description => "Configure an USB device (n is 0 to 4).", |
1e3baf05 DM |
928 | }; |
929 | PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc); | |
930 | ||
1f4f447b WB |
931 | # NOTE: the match-groups of this regex are used in parse_hostpci |
932 | my $PCIRE = qr/([a-f0-9]{2}:[a-f0-9]{2})(?:\.([a-f0-9]))?/; | |
933 | my $hostpci_fmt = { | |
934 | host => { | |
935 | default_key => 1, | |
936 | type => 'string', | |
937 | pattern => qr/$PCIRE(;$PCIRE)*/, | |
938 | format_description => 'HOSTPCIID[;HOSTPCIID2...]', | |
52261945 DM |
939 | description => <<EODESCR, |
940 | Host PCI device pass through. The PCI ID of a host's PCI device or a list | |
941 | of PCI virtual functions of the host. HOSTPCIID syntax is: | |
942 | ||
943 | 'bus:dev.func' (hexadecimal numbers) | |
944 | ||
945 | You can us the 'lspci' command to list existing PCI devices. | |
946 | ||
947 | NOTE: This option allows direct access to host hardware. So it is no longer | |
948 | possible to migrate such machines - use with special care. | |
949 | ||
950 | CAUTION: Experimental! User reported problems with this option. | |
951 | ||
952 | EODESCR | |
1f4f447b WB |
953 | }, |
954 | rombar => { | |
955 | type => 'boolean', | |
52261945 | 956 | description => "Specify whether or not the device's ROM will be visible in the guest's memory map.", |
1f4f447b WB |
957 | optional => 1, |
958 | default => 1, | |
959 | }, | |
960 | pcie => { | |
961 | type => 'boolean', | |
52261945 | 962 | description => "Choose the PCI-express bus (needs the 'q35' machine model).", |
1f4f447b WB |
963 | optional => 1, |
964 | default => 0, | |
965 | }, | |
966 | 'x-vga' => { | |
967 | type => 'boolean', | |
52261945 | 968 | description => "Enable vfio-vga device support.", |
1f4f447b WB |
969 | optional => 1, |
970 | default => 0, | |
971 | }, | |
972 | }; | |
973 | PVE::JSONSchema::register_format('pve-qm-hostpci', $hostpci_fmt); | |
974 | ||
040b06b7 DA |
975 | my $hostpcidesc = { |
976 | optional => 1, | |
977 | type => 'string', format => 'pve-qm-hostpci', | |
52261945 | 978 | description => "Map host PCI devices into guest.", |
040b06b7 DA |
979 | }; |
980 | PVE::JSONSchema::register_standard_option("pve-qm-hostpci", $hostpcidesc); | |
981 | ||
bae179aa DA |
982 | my $serialdesc = { |
983 | optional => 1, | |
ca0cef26 | 984 | type => 'string', |
1b0b51ed | 985 | pattern => '(/dev/.+|socket)', |
52261945 DM |
986 | description => "Create a serial device inside the VM (n is 0 to 3)", |
987 | verbose_description => <<EODESCR, | |
988 | ||
989 | Create a serial device inside the VM (n is 0 to 3), and pass through a | |
990 | host serial device (i.e. /dev/ttyS0), or create a unix socket on the | |
991 | host side (use 'qm terminal' to open a terminal connection). | |
bae179aa | 992 | |
8a61e0fd | 993 | NOTE: If you pass through a host serial device, it is no longer possible to migrate such machines - use with special care. |
bae179aa | 994 | |
52261945 | 995 | CAUTION: Experimental! User reported problems with this option. |
bae179aa DA |
996 | EODESCR |
997 | }; | |
bae179aa | 998 | |
1989a89c DA |
999 | my $paralleldesc= { |
1000 | optional => 1, | |
ca0cef26 | 1001 | type => 'string', |
9ecc8431 | 1002 | pattern => '/dev/parport\d+|/dev/usb/lp\d+', |
52261945 DM |
1003 | description => "Map host parallel devices (n is 0 to 2).", |
1004 | verbose_description => <<EODESCR, | |
19672434 | 1005 | Map host parallel devices (n is 0 to 2). |
1989a89c | 1006 | |
8a61e0fd | 1007 | NOTE: This option allows direct access to host hardware. So it is no longer possible to migrate such machines - use with special care. |
1989a89c | 1008 | |
52261945 | 1009 | CAUTION: Experimental! User reported problems with this option. |
1989a89c DA |
1010 | EODESCR |
1011 | }; | |
1989a89c DA |
1012 | |
1013 | for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) { | |
1014 | $confdesc->{"parallel$i"} = $paralleldesc; | |
1015 | } | |
1016 | ||
bae179aa DA |
1017 | for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) { |
1018 | $confdesc->{"serial$i"} = $serialdesc; | |
1019 | } | |
1020 | ||
040b06b7 DA |
1021 | for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) { |
1022 | $confdesc->{"hostpci$i"} = $hostpcidesc; | |
1023 | } | |
1e3baf05 DM |
1024 | |
1025 | for (my $i = 0; $i < $MAX_IDE_DISKS; $i++) { | |
1026 | $drivename_hash->{"ide$i"} = 1; | |
1027 | $confdesc->{"ide$i"} = $idedesc; | |
1028 | } | |
1029 | ||
cdb0931f DA |
1030 | for (my $i = 0; $i < $MAX_SATA_DISKS; $i++) { |
1031 | $drivename_hash->{"sata$i"} = 1; | |
1032 | $confdesc->{"sata$i"} = $satadesc; | |
1033 | } | |
1034 | ||
1e3baf05 DM |
1035 | for (my $i = 0; $i < $MAX_SCSI_DISKS; $i++) { |
1036 | $drivename_hash->{"scsi$i"} = 1; | |
1037 | $confdesc->{"scsi$i"} = $scsidesc ; | |
1038 | } | |
1039 | ||
1040 | for (my $i = 0; $i < $MAX_VIRTIO_DISKS; $i++) { | |
1041 | $drivename_hash->{"virtio$i"} = 1; | |
1042 | $confdesc->{"virtio$i"} = $virtiodesc; | |
1043 | } | |
1044 | ||
1045 | for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) { | |
1046 | $confdesc->{"usb$i"} = $usbdesc; | |
1047 | } | |
1048 | ||
1049 | my $unuseddesc = { | |
1050 | optional => 1, | |
1051 | type => 'string', format => 'pve-volume-id', | |
52261945 | 1052 | description => "Reference to unused volumes. This is used internally, and should not be modified manually.", |
1e3baf05 DM |
1053 | }; |
1054 | ||
1055 | for (my $i = 0; $i < $MAX_UNUSED_DISKS; $i++) { | |
1056 | $confdesc->{"unused$i"} = $unuseddesc; | |
1057 | } | |
1058 | ||
1059 | my $kvm_api_version = 0; | |
1060 | ||
1061 | sub kvm_version { | |
1062 | ||
1063 | return $kvm_api_version if $kvm_api_version; | |
1064 | ||
6b64503e | 1065 | my $fh = IO::File->new("</dev/kvm") || |
1e3baf05 DM |
1066 | return 0; |
1067 | ||
6b64503e | 1068 | if (my $v = $fh->ioctl(KVM_GET_API_VERSION(), 0)) { |
1e3baf05 DM |
1069 | $kvm_api_version = $v; |
1070 | } | |
1071 | ||
1072 | $fh->close(); | |
1073 | ||
1074 | return $kvm_api_version; | |
1075 | } | |
1076 | ||
1077 | my $kvm_user_version; | |
1078 | ||
1079 | sub kvm_user_version { | |
1080 | ||
1081 | return $kvm_user_version if $kvm_user_version; | |
1082 | ||
1083 | $kvm_user_version = 'unknown'; | |
1084 | ||
09b11429 TL |
1085 | my $code = sub { |
1086 | my $line = shift; | |
1087 | if ($line =~ m/^QEMU( PC)? emulator version (\d+\.\d+(\.\d+)?)(\.\d+)?[,\s]/) { | |
1088 | $kvm_user_version = $2; | |
1089 | } | |
1090 | }; | |
19672434 | 1091 | |
09b11429 TL |
1092 | eval { run_command("kvm -version", outfunc => $code); }; |
1093 | warn $@ if $@; | |
1e3baf05 DM |
1094 | |
1095 | return $kvm_user_version; | |
1096 | ||
1097 | } | |
1098 | ||
1099 | my $kernel_has_vhost_net = -c '/dev/vhost-net'; | |
1100 | ||
74479ee9 | 1101 | sub valid_drive_names { |
1e3baf05 | 1102 | # order is important - used to autoselect boot disk |
19672434 | 1103 | return ((map { "ide$_" } (0 .. ($MAX_IDE_DISKS - 1))), |
1e3baf05 | 1104 | (map { "scsi$_" } (0 .. ($MAX_SCSI_DISKS - 1))), |
cdb0931f DA |
1105 | (map { "virtio$_" } (0 .. ($MAX_VIRTIO_DISKS - 1))), |
1106 | (map { "sata$_" } (0 .. ($MAX_SATA_DISKS - 1)))); | |
1e3baf05 DM |
1107 | } |
1108 | ||
74479ee9 | 1109 | sub is_valid_drivename { |
1e3baf05 DM |
1110 | my $dev = shift; |
1111 | ||
6b64503e | 1112 | return defined($drivename_hash->{$dev}); |
1e3baf05 DM |
1113 | } |
1114 | ||
1115 | sub option_exists { | |
1116 | my $key = shift; | |
1117 | return defined($confdesc->{$key}); | |
19672434 | 1118 | } |
1e3baf05 DM |
1119 | |
1120 | sub nic_models { | |
1121 | return $nic_model_list; | |
1122 | } | |
1123 | ||
1124 | sub os_list_description { | |
1125 | ||
1126 | return { | |
1127 | other => 'Other', | |
1128 | wxp => 'Windows XP', | |
1129 | w2k => 'Windows 2000', | |
1130 | w2k3 =>, 'Windows 2003', | |
1131 | w2k8 => 'Windows 2008', | |
1132 | wvista => 'Windows Vista', | |
1133 | win7 => 'Windows 7', | |
a70ebde3 | 1134 | win8 => 'Windows 8/2012', |
1e3baf05 DM |
1135 | l24 => 'Linux 2.4', |
1136 | l26 => 'Linux 2.6', | |
19672434 | 1137 | }; |
1e3baf05 DM |
1138 | } |
1139 | ||
1e3baf05 DM |
1140 | my $cdrom_path; |
1141 | ||
1142 | sub get_cdrom_path { | |
1143 | ||
1144 | return $cdrom_path if $cdrom_path; | |
1145 | ||
1146 | return $cdrom_path = "/dev/cdrom" if -l "/dev/cdrom"; | |
1147 | return $cdrom_path = "/dev/cdrom1" if -l "/dev/cdrom1"; | |
1148 | return $cdrom_path = "/dev/cdrom2" if -l "/dev/cdrom2"; | |
1149 | } | |
1150 | ||
1151 | sub get_iso_path { | |
1152 | my ($storecfg, $vmid, $cdrom) = @_; | |
1153 | ||
1154 | if ($cdrom eq 'cdrom') { | |
1155 | return get_cdrom_path(); | |
1156 | } elsif ($cdrom eq 'none') { | |
1157 | return ''; | |
1158 | } elsif ($cdrom =~ m|^/|) { | |
1159 | return $cdrom; | |
1160 | } else { | |
6b64503e | 1161 | return PVE::Storage::path($storecfg, $cdrom); |
1e3baf05 DM |
1162 | } |
1163 | } | |
1164 | ||
1165 | # try to convert old style file names to volume IDs | |
1166 | sub filename_to_volume_id { | |
1167 | my ($vmid, $file, $media) = @_; | |
1168 | ||
1169 | if (!($file eq 'none' || $file eq 'cdrom' || | |
1170 | $file =~ m|^/dev/.+| || $file =~ m/^([^:]+):(.+)$/)) { | |
19672434 | 1171 | |
1e3baf05 | 1172 | return undef if $file =~ m|/|; |
19672434 | 1173 | |
1e3baf05 DM |
1174 | if ($media && $media eq 'cdrom') { |
1175 | $file = "local:iso/$file"; | |
1176 | } else { | |
1177 | $file = "local:$vmid/$file"; | |
1178 | } | |
1179 | } | |
1180 | ||
1181 | return $file; | |
1182 | } | |
1183 | ||
1184 | sub verify_media_type { | |
1185 | my ($opt, $vtype, $media) = @_; | |
1186 | ||
1187 | return if !$media; | |
1188 | ||
1189 | my $etype; | |
1190 | if ($media eq 'disk') { | |
a125592c | 1191 | $etype = 'images'; |
1e3baf05 DM |
1192 | } elsif ($media eq 'cdrom') { |
1193 | $etype = 'iso'; | |
1194 | } else { | |
1195 | die "internal error"; | |
1196 | } | |
1197 | ||
1198 | return if ($vtype eq $etype); | |
19672434 | 1199 | |
1e3baf05 DM |
1200 | raise_param_exc({ $opt => "unexpected media type ($vtype != $etype)" }); |
1201 | } | |
1202 | ||
1203 | sub cleanup_drive_path { | |
1204 | my ($opt, $storecfg, $drive) = @_; | |
1205 | ||
1206 | # try to convert filesystem paths to volume IDs | |
1207 | ||
1208 | if (($drive->{file} !~ m/^(cdrom|none)$/) && | |
1209 | ($drive->{file} !~ m|^/dev/.+|) && | |
1210 | ($drive->{file} !~ m/^([^:]+):(.+)$/) && | |
19672434 | 1211 | ($drive->{file} !~ m/^\d+$/)) { |
1e3baf05 DM |
1212 | my ($vtype, $volid) = PVE::Storage::path_to_volume_id($storecfg, $drive->{file}); |
1213 | raise_param_exc({ $opt => "unable to associate path '$drive->{file}' to any storage"}) if !$vtype; | |
1214 | $drive->{media} = 'cdrom' if !$drive->{media} && $vtype eq 'iso'; | |
1215 | verify_media_type($opt, $vtype, $drive->{media}); | |
1216 | $drive->{file} = $volid; | |
1217 | } | |
1218 | ||
1219 | $drive->{media} = 'cdrom' if !$drive->{media} && $drive->{file} =~ m/^(cdrom|none)$/; | |
1220 | } | |
1221 | ||
b3c2bdd1 DM |
1222 | sub parse_hotplug_features { |
1223 | my ($data) = @_; | |
1224 | ||
1225 | my $res = {}; | |
1226 | ||
1227 | return $res if $data eq '0'; | |
a1b7d579 | 1228 | |
b3c2bdd1 DM |
1229 | $data = $confdesc->{hotplug}->{default} if $data eq '1'; |
1230 | ||
45827685 | 1231 | foreach my $feature (PVE::Tools::split_list($data)) { |
b3c2bdd1 DM |
1232 | if ($feature =~ m/^(network|disk|cpu|memory|usb)$/) { |
1233 | $res->{$1} = 1; | |
1234 | } else { | |
1235 | warn "ignoring unknown hotplug feature '$feature'\n"; | |
1236 | } | |
1237 | } | |
1238 | return $res; | |
1239 | } | |
1240 | ||
1241 | PVE::JSONSchema::register_format('pve-hotplug-features', \&pve_verify_hotplug_features); | |
1242 | sub pve_verify_hotplug_features { | |
1243 | my ($value, $noerr) = @_; | |
1244 | ||
1245 | return $value if parse_hotplug_features($value); | |
1246 | ||
1247 | return undef if $noerr; | |
1248 | ||
1249 | die "unable to parse hotplug option\n"; | |
1250 | } | |
1251 | ||
1e3baf05 DM |
1252 | # ideX = [volume=]volume-id[,media=d][,cyls=c,heads=h,secs=s[,trans=t]] |
1253 | # [,snapshot=on|off][,cache=on|off][,format=f][,backup=yes|no] | |
036e0e2b | 1254 | # [,rerror=ignore|report|stop][,werror=enospc|ignore|report|stop] |
6e47c3b4 WB |
1255 | # [,aio=native|threads][,discard=ignore|on][,detect_zeroes=on|off] |
1256 | # [,iothread=on][,serial=serial][,model=model] | |
1e3baf05 DM |
1257 | |
1258 | sub parse_drive { | |
1259 | my ($key, $data) = @_; | |
1260 | ||
0541eeb8 | 1261 | my ($interface, $index); |
19672434 | 1262 | |
0541eeb8 WB |
1263 | if ($key =~ m/^([^\d]+)(\d+)$/) { |
1264 | $interface = $1; | |
1265 | $index = $2; | |
1e3baf05 DM |
1266 | } else { |
1267 | return undef; | |
1268 | } | |
1269 | ||
0541eeb8 WB |
1270 | my $desc = $key =~ /^unused\d+$/ ? $alldrive_fmt |
1271 | : $confdesc->{$key}->{format}; | |
1272 | if (!$desc) { | |
1273 | warn "invalid drive key: $key\n"; | |
1274 | return undef; | |
1275 | } | |
1276 | my $res = eval { PVE::JSONSchema::parse_property_string($desc, $data) }; | |
1277 | return undef if !$res; | |
1278 | $res->{interface} = $interface; | |
1279 | $res->{index} = $index; | |
1280 | ||
1281 | my $error = 0; | |
1282 | foreach my $opt (qw(bps bps_rd bps_wr)) { | |
1283 | if (my $bps = defined(delete $res->{$opt})) { | |
1284 | if (defined($res->{"m$opt"})) { | |
1285 | warn "both $opt and m$opt specified\n"; | |
1286 | ++$error; | |
1287 | next; | |
1e3baf05 | 1288 | } |
0541eeb8 | 1289 | $res->{"m$opt"} = sprintf("%.3f", $bps / (1024*1024.0)); |
1e3baf05 DM |
1290 | } |
1291 | } | |
0541eeb8 | 1292 | return undef if $error; |
be190583 | 1293 | |
9bf371a6 DM |
1294 | return undef if $res->{mbps_rd} && $res->{mbps}; |
1295 | return undef if $res->{mbps_wr} && $res->{mbps}; | |
affd2f88 AD |
1296 | return undef if $res->{iops_rd} && $res->{iops}; |
1297 | return undef if $res->{iops_wr} && $res->{iops}; | |
74edd76b | 1298 | |
1e3baf05 DM |
1299 | if ($res->{media} && ($res->{media} eq 'cdrom')) { |
1300 | return undef if $res->{snapshot} || $res->{trans} || $res->{format}; | |
19672434 | 1301 | return undef if $res->{heads} || $res->{secs} || $res->{cyls}; |
1e3baf05 DM |
1302 | return undef if $res->{interface} eq 'virtio'; |
1303 | } | |
1304 | ||
0541eeb8 WB |
1305 | if (my $size = $res->{size}) { |
1306 | return undef if !defined($res->{size} = PVE::JSONSchema::parse_size($size)); | |
1e3baf05 DM |
1307 | } |
1308 | ||
1309 | return $res; | |
1310 | } | |
1311 | ||
1e3baf05 DM |
1312 | sub print_drive { |
1313 | my ($vmid, $drive) = @_; | |
0541eeb8 WB |
1314 | my $data = { %$drive }; |
1315 | delete $data->{$_} for qw(index interface); | |
1316 | return PVE::JSONSchema::print_property_string($data, $alldrive_fmt); | |
1e3baf05 DM |
1317 | } |
1318 | ||
28ef82d3 DM |
1319 | sub scsi_inquiry { |
1320 | my($fh, $noerr) = @_; | |
1321 | ||
1322 | my $SG_IO = 0x2285; | |
1323 | my $SG_GET_VERSION_NUM = 0x2282; | |
1324 | ||
1325 | my $versionbuf = "\x00" x 8; | |
1326 | my $ret = ioctl($fh, $SG_GET_VERSION_NUM, $versionbuf); | |
1327 | if (!$ret) { | |
1328 | die "scsi ioctl SG_GET_VERSION_NUM failoed - $!\n" if !$noerr; | |
1329 | return undef; | |
1330 | } | |
97d62eb7 | 1331 | my $version = unpack("I", $versionbuf); |
28ef82d3 DM |
1332 | if ($version < 30000) { |
1333 | die "scsi generic interface too old\n" if !$noerr; | |
1334 | return undef; | |
1335 | } | |
97d62eb7 | 1336 | |
28ef82d3 DM |
1337 | my $buf = "\x00" x 36; |
1338 | my $sensebuf = "\x00" x 8; | |
f334aa3e | 1339 | my $cmd = pack("C x3 C x1", 0x12, 36); |
97d62eb7 | 1340 | |
28ef82d3 DM |
1341 | # see /usr/include/scsi/sg.h |
1342 | 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"; | |
1343 | ||
97d62eb7 DM |
1344 | my $packet = pack($sg_io_hdr_t, ord('S'), -3, length($cmd), |
1345 | length($sensebuf), 0, length($buf), $buf, | |
28ef82d3 DM |
1346 | $cmd, $sensebuf, 6000); |
1347 | ||
1348 | $ret = ioctl($fh, $SG_IO, $packet); | |
1349 | if (!$ret) { | |
1350 | die "scsi ioctl SG_IO failed - $!\n" if !$noerr; | |
1351 | return undef; | |
1352 | } | |
97d62eb7 | 1353 | |
28ef82d3 DM |
1354 | my @res = unpack($sg_io_hdr_t, $packet); |
1355 | if ($res[17] || $res[18]) { | |
1356 | die "scsi ioctl SG_IO status error - $!\n" if !$noerr; | |
1357 | return undef; | |
1358 | } | |
1359 | ||
1360 | my $res = {}; | |
09984754 | 1361 | (my $byte0, my $byte1, $res->{vendor}, |
28ef82d3 DM |
1362 | $res->{product}, $res->{revision}) = unpack("C C x6 A8 A16 A4", $buf); |
1363 | ||
09984754 DM |
1364 | $res->{removable} = $byte1 & 128 ? 1 : 0; |
1365 | $res->{type} = $byte0 & 31; | |
1366 | ||
28ef82d3 DM |
1367 | return $res; |
1368 | } | |
1369 | ||
1370 | sub path_is_scsi { | |
1371 | my ($path) = @_; | |
1372 | ||
1373 | my $fh = IO::File->new("+<$path") || return undef; | |
1374 | my $res = scsi_inquiry($fh, 1); | |
1375 | close($fh); | |
1376 | ||
1377 | return $res; | |
1378 | } | |
1379 | ||
db656e5f DM |
1380 | sub machine_type_is_q35 { |
1381 | my ($conf) = @_; | |
b467f79a | 1382 | |
db656e5f DM |
1383 | return $conf->{machine} && ($conf->{machine} =~ m/q35/) ? 1 : 0; |
1384 | } | |
1385 | ||
1386 | sub print_tabletdevice_full { | |
1387 | my ($conf) = @_; | |
b467f79a | 1388 | |
db656e5f DM |
1389 | my $q35 = machine_type_is_q35($conf); |
1390 | ||
1391 | # we use uhci for old VMs because tablet driver was buggy in older qemu | |
1392 | my $usbbus = $q35 ? "ehci" : "uhci"; | |
b467f79a | 1393 | |
db656e5f DM |
1394 | return "usb-tablet,id=tablet,bus=$usbbus.0,port=1"; |
1395 | } | |
1396 | ||
ca916ecc | 1397 | sub print_drivedevice_full { |
5bdcf937 | 1398 | my ($storecfg, $conf, $vmid, $drive, $bridges) = @_; |
ca916ecc DA |
1399 | |
1400 | my $device = ''; | |
1401 | my $maxdev = 0; | |
19672434 | 1402 | |
ca916ecc | 1403 | if ($drive->{interface} eq 'virtio') { |
5bdcf937 | 1404 | my $pciaddr = print_pci_addr("$drive->{interface}$drive->{index}", $bridges); |
2ed36a41 | 1405 | $device = "virtio-blk-pci,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}$pciaddr"; |
51f492cd | 1406 | $device .= ",iothread=iothread-$drive->{interface}$drive->{index}" if $drive->{iothread}; |
2ed36a41 | 1407 | } elsif ($drive->{interface} eq 'scsi') { |
6731a4cf | 1408 | |
ee034f5c | 1409 | my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive); |
2ed36a41 DM |
1410 | my $unit = $drive->{index} % $maxdev; |
1411 | my $devicetype = 'hd'; | |
69bcf246 WB |
1412 | my $path = ''; |
1413 | if (drive_is_cdrom($drive)) { | |
1414 | $devicetype = 'cd'; | |
29b19529 | 1415 | } else { |
69bcf246 WB |
1416 | if ($drive->{file} =~ m|^/|) { |
1417 | $path = $drive->{file}; | |
1418 | if (my $info = path_is_scsi($path)) { | |
1419 | if ($info->{type} == 0) { | |
1420 | $devicetype = 'block'; | |
1421 | } elsif ($info->{type} == 1) { # tape | |
1422 | $devicetype = 'generic'; | |
1423 | } | |
1424 | } | |
1425 | } else { | |
1426 | $path = PVE::Storage::path($storecfg, $drive->{file}); | |
1427 | } | |
1428 | ||
1429 | if($path =~ m/^iscsi\:\/\//){ | |
1430 | $devicetype = 'generic'; | |
1431 | } | |
1432 | } | |
1433 | ||
1434 | if (!$conf->{scsihw} || ($conf->{scsihw} =~ m/^lsi/)){ | |
1435 | $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,scsi-id=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}"; | |
1436 | } else { | |
1437 | $device = "scsi-$devicetype,bus=$controller_prefix$controller.0,channel=0,scsi-id=0,lun=$drive->{index},drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}"; | |
1438 | } | |
cdd20088 | 1439 | |
2ed36a41 DM |
1440 | } elsif ($drive->{interface} eq 'ide'){ |
1441 | $maxdev = 2; | |
1442 | my $controller = int($drive->{index} / $maxdev); | |
1443 | my $unit = $drive->{index} % $maxdev; | |
1444 | my $devicetype = ($drive->{media} && $drive->{media} eq 'cdrom') ? "cd" : "hd"; | |
1445 | ||
7ebe888a | 1446 | $device = "ide-$devicetype,bus=ide.$controller,unit=$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}"; |
0f2812c2 | 1447 | if ($devicetype eq 'hd' && (my $model = $drive->{model})) { |
46630a5f | 1448 | $model = URI::Escape::uri_unescape($model); |
0f2812c2 WB |
1449 | $device .= ",model=$model"; |
1450 | } | |
cdb0931f DA |
1451 | } elsif ($drive->{interface} eq 'sata'){ |
1452 | my $controller = int($drive->{index} / $MAX_SATA_DISKS); | |
1453 | my $unit = $drive->{index} % $MAX_SATA_DISKS; | |
1454 | $device = "ide-drive,bus=ahci$controller.$unit,drive=drive-$drive->{interface}$drive->{index},id=$drive->{interface}$drive->{index}"; | |
2ed36a41 DM |
1455 | } elsif ($drive->{interface} eq 'usb') { |
1456 | die "implement me"; | |
1457 | # -device ide-drive,bus=ide.1,unit=0,drive=drive-ide0-1-0,id=ide0-1-0 | |
1458 | } else { | |
1459 | die "unsupported interface type"; | |
ca916ecc DA |
1460 | } |
1461 | ||
3b408e82 DM |
1462 | $device .= ",bootindex=$drive->{bootindex}" if $drive->{bootindex}; |
1463 | ||
ca916ecc DA |
1464 | return $device; |
1465 | } | |
1466 | ||
15b21acc | 1467 | sub get_initiator_name { |
46f58b5f | 1468 | my $initiator; |
15b21acc | 1469 | |
46f58b5f DM |
1470 | my $fh = IO::File->new('/etc/iscsi/initiatorname.iscsi') || return undef; |
1471 | while (defined(my $line = <$fh>)) { | |
1472 | next if $line !~ m/^\s*InitiatorName\s*=\s*([\.\-:\w]+)/; | |
15b21acc MR |
1473 | $initiator = $1; |
1474 | last; | |
1475 | } | |
46f58b5f DM |
1476 | $fh->close(); |
1477 | ||
15b21acc MR |
1478 | return $initiator; |
1479 | } | |
1480 | ||
1e3baf05 DM |
1481 | sub print_drive_full { |
1482 | my ($storecfg, $vmid, $drive) = @_; | |
1483 | ||
d81f0f09 DM |
1484 | my $path; |
1485 | my $volid = $drive->{file}; | |
1486 | my $format; | |
1487 | ||
1488 | if (drive_is_cdrom($drive)) { | |
1489 | $path = get_iso_path($storecfg, $vmid, $volid); | |
1490 | } else { | |
1491 | my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1); | |
1492 | if ($storeid) { | |
1493 | $path = PVE::Storage::path($storecfg, $volid); | |
1494 | my $scfg = PVE::Storage::storage_config($storecfg, $storeid); | |
1495 | $format = qemu_img_format($scfg, $volname); | |
1496 | } else { | |
1497 | $path = $volid; | |
5b61bff2 | 1498 | $format = "raw"; |
d81f0f09 DM |
1499 | } |
1500 | } | |
1501 | ||
1e3baf05 | 1502 | my $opts = ''; |
46630a5f | 1503 | my @qemu_drive_options = qw(heads secs cyls trans media format cache snapshot rerror werror aio discard iops iops_rd iops_wr iops_max iops_rd_max iops_wr_max); |
1e3baf05 DM |
1504 | foreach my $o (@qemu_drive_options) { |
1505 | $opts .= ",$o=$drive->{$o}" if $drive->{$o}; | |
19672434 | 1506 | } |
46630a5f WB |
1507 | if (my $serial = $drive->{serial}) { |
1508 | $serial = URI::Escape::uri_unescape($serial); | |
1509 | $opts .= ",serial=$serial"; | |
1510 | } | |
1e3baf05 | 1511 | |
d81f0f09 DM |
1512 | $opts .= ",format=$format" if $format && !$drive->{format}; |
1513 | ||
9bf371a6 DM |
1514 | foreach my $o (qw(bps bps_rd bps_wr)) { |
1515 | my $v = $drive->{"m$o"}; | |
1516 | $opts .= ",$o=" . int($v*1024*1024) if $v; | |
1517 | } | |
1518 | ||
b2ee900e WB |
1519 | my $cache_direct = 0; |
1520 | ||
1521 | if (my $cache = $drive->{cache}) { | |
1522 | $cache_direct = $cache =~ /^(?:off|none|directsync)$/; | |
1523 | } elsif (!drive_is_cdrom($drive)) { | |
1524 | $opts .= ",cache=none"; | |
1525 | $cache_direct = 1; | |
1526 | } | |
1527 | ||
1528 | # aio native works only with O_DIRECT | |
1529 | if (!$drive->{aio}) { | |
1530 | if($cache_direct) { | |
1531 | $opts .= ",aio=native"; | |
1532 | } else { | |
1533 | $opts .= ",aio=threads"; | |
1534 | } | |
1535 | } | |
11490cf2 | 1536 | |
6e47c3b4 WB |
1537 | if (!drive_is_cdrom($drive)) { |
1538 | my $detectzeroes; | |
7d4e30f3 | 1539 | if (defined($drive->{detect_zeroes}) && !$drive->{detect_zeroes}) { |
6e47c3b4 WB |
1540 | $detectzeroes = 'off'; |
1541 | } elsif ($drive->{discard}) { | |
1542 | $detectzeroes = $drive->{discard} eq 'on' ? 'unmap' : 'on'; | |
1543 | } else { | |
1544 | # This used to be our default with discard not being specified: | |
1545 | $detectzeroes = 'on'; | |
1546 | } | |
1547 | $opts .= ",detect-zeroes=$detectzeroes" if $detectzeroes; | |
1548 | } | |
f1e05305 | 1549 | |
1e3baf05 DM |
1550 | my $pathinfo = $path ? "file=$path," : ''; |
1551 | ||
3ebfcc86 | 1552 | return "${pathinfo}if=none,id=drive-$drive->{interface}$drive->{index}$opts"; |
1e3baf05 DM |
1553 | } |
1554 | ||
cc4d6182 | 1555 | sub print_netdevice_full { |
ba9e1000 | 1556 | my ($vmid, $conf, $net, $netid, $bridges, $use_old_bios_files) = @_; |
cc4d6182 DA |
1557 | |
1558 | my $bootorder = $conf->{boot} || $confdesc->{boot}->{default}; | |
1559 | ||
1560 | my $device = $net->{model}; | |
1561 | if ($net->{model} eq 'virtio') { | |
1562 | $device = 'virtio-net-pci'; | |
1563 | }; | |
1564 | ||
5bdcf937 | 1565 | my $pciaddr = print_pci_addr("$netid", $bridges); |
5e2068d2 | 1566 | my $tmpstr = "$device,mac=$net->{macaddr},netdev=$netid$pciaddr,id=$netid"; |
a9410357 AD |
1567 | if ($net->{queues} && $net->{queues} > 1 && $net->{model} eq 'virtio'){ |
1568 | #Consider we have N queues, the number of vectors needed is 2*N + 2 (plus one config interrupt and control vq) | |
1569 | my $vectors = $net->{queues} * 2 + 2; | |
1570 | $tmpstr .= ",vectors=$vectors,mq=on"; | |
1571 | } | |
cc4d6182 | 1572 | $tmpstr .= ",bootindex=$net->{bootindex}" if $net->{bootindex} ; |
ba9e1000 DM |
1573 | |
1574 | if ($use_old_bios_files) { | |
1575 | my $romfile; | |
1576 | if ($device eq 'virtio-net-pci') { | |
1577 | $romfile = 'pxe-virtio.rom'; | |
1578 | } elsif ($device eq 'e1000') { | |
1579 | $romfile = 'pxe-e1000.rom'; | |
1580 | } elsif ($device eq 'ne2k') { | |
1581 | $romfile = 'pxe-ne2k_pci.rom'; | |
1582 | } elsif ($device eq 'pcnet') { | |
1583 | $romfile = 'pxe-pcnet.rom'; | |
1584 | } elsif ($device eq 'rtl8139') { | |
1585 | $romfile = 'pxe-rtl8139.rom'; | |
1586 | } | |
1587 | $tmpstr .= ",romfile=$romfile" if $romfile; | |
1588 | } | |
1589 | ||
cc4d6182 DA |
1590 | return $tmpstr; |
1591 | } | |
1592 | ||
1593 | sub print_netdev_full { | |
208ba94e | 1594 | my ($vmid, $conf, $net, $netid, $hotplug) = @_; |
cc4d6182 DA |
1595 | |
1596 | my $i = ''; | |
1597 | if ($netid =~ m/^net(\d+)$/) { | |
1598 | $i = int($1); | |
1599 | } | |
1600 | ||
1601 | die "got strange net id '$i'\n" if $i >= ${MAX_NETS}; | |
1602 | ||
1603 | my $ifname = "tap${vmid}i$i"; | |
1604 | ||
1605 | # kvm uses TUNSETIFF ioctl, and that limits ifname length | |
1606 | die "interface name '$ifname' is too long (max 15 character)\n" | |
1607 | if length($ifname) >= 16; | |
1608 | ||
1609 | my $vhostparam = ''; | |
1610 | $vhostparam = ',vhost=on' if $kernel_has_vhost_net && $net->{model} eq 'virtio'; | |
1611 | ||
1612 | my $vmname = $conf->{name} || "vm$vmid"; | |
1613 | ||
a9410357 | 1614 | my $netdev = ""; |
208ba94e | 1615 | my $script = $hotplug ? "pve-bridge-hotplug" : "pve-bridge"; |
a9410357 | 1616 | |
cc4d6182 | 1617 | if ($net->{bridge}) { |
208ba94e | 1618 | $netdev = "type=tap,id=$netid,ifname=${ifname},script=/var/lib/qemu-server/$script,downscript=/var/lib/qemu-server/pve-bridgedown$vhostparam"; |
cc4d6182 | 1619 | } else { |
a9410357 | 1620 | $netdev = "type=user,id=$netid,hostname=$vmname"; |
cc4d6182 | 1621 | } |
a9410357 AD |
1622 | |
1623 | $netdev .= ",queues=$net->{queues}" if ($net->{queues} && $net->{model} eq 'virtio'); | |
1624 | ||
1625 | return $netdev; | |
cc4d6182 | 1626 | } |
1e3baf05 DM |
1627 | |
1628 | sub drive_is_cdrom { | |
1629 | my ($drive) = @_; | |
1630 | ||
1631 | return $drive && $drive->{media} && ($drive->{media} eq 'cdrom'); | |
1632 | ||
1633 | } | |
1634 | ||
ffc0d8c7 WB |
1635 | sub parse_number_sets { |
1636 | my ($set) = @_; | |
1637 | my $res = []; | |
1638 | foreach my $part (split(/;/, $set)) { | |
1639 | if ($part =~ /^\s*(\d+)(?:-(\d+))?\s*$/) { | |
1640 | die "invalid range: $part ($2 < $1)\n" if defined($2) && $2 < $1; | |
1641 | push @$res, [ $1, $2 ]; | |
2ed5d572 | 1642 | } else { |
ffc0d8c7 | 1643 | die "invalid range: $part\n"; |
2ed5d572 AD |
1644 | } |
1645 | } | |
ffc0d8c7 WB |
1646 | return $res; |
1647 | } | |
2ed5d572 | 1648 | |
ffc0d8c7 WB |
1649 | sub parse_numa { |
1650 | my ($data) = @_; | |
1651 | ||
1652 | my $res = PVE::JSONSchema::parse_property_string($numa_fmt, $data); | |
1653 | $res->{cpus} = parse_number_sets($res->{cpus}) if defined($res->{cpus}); | |
1654 | $res->{hostnodes} = parse_number_sets($res->{hostnodes}) if defined($res->{hostnodes}); | |
2ed5d572 AD |
1655 | return $res; |
1656 | } | |
1657 | ||
040b06b7 DA |
1658 | sub parse_hostpci { |
1659 | my ($value) = @_; | |
1660 | ||
1661 | return undef if !$value; | |
1662 | ||
1f4f447b | 1663 | my $res = PVE::JSONSchema::parse_property_string($hostpci_fmt, $value); |
0cea6a01 | 1664 | |
1f4f447b WB |
1665 | my @idlist = split(/;/, $res->{host}); |
1666 | delete $res->{host}; | |
1667 | foreach my $id (@idlist) { | |
1668 | if ($id =~ /^$PCIRE$/) { | |
1669 | push @{$res->{pciid}}, { id => $1, function => ($2//'0') }; | |
0cea6a01 | 1670 | } else { |
1f4f447b WB |
1671 | # should have been caught by parse_property_string already |
1672 | die "failed to parse PCI id: $id\n"; | |
0cea6a01 | 1673 | } |
040b06b7 | 1674 | } |
040b06b7 DA |
1675 | return $res; |
1676 | } | |
1677 | ||
1e3baf05 DM |
1678 | # netX: e1000=XX:XX:XX:XX:XX:XX,bridge=vmbr0,rate=<mbps> |
1679 | sub parse_net { | |
1680 | my ($data) = @_; | |
1681 | ||
cd9c34d1 WB |
1682 | my $res = eval { PVE::JSONSchema::parse_property_string($net_fmt, $data) }; |
1683 | if ($@) { | |
1684 | warn $@; | |
1685 | return undef; | |
1e3baf05 | 1686 | } |
cd9c34d1 | 1687 | $res->{macaddr} = PVE::Tools::random_ether_addr() if !defined($res->{macaddr}); |
1e3baf05 DM |
1688 | return $res; |
1689 | } | |
1690 | ||
1691 | sub print_net { | |
1692 | my $net = shift; | |
1693 | ||
cd9c34d1 | 1694 | return PVE::JSONSchema::print_property_string($net, $net_fmt); |
1e3baf05 DM |
1695 | } |
1696 | ||
1697 | sub add_random_macs { | |
1698 | my ($settings) = @_; | |
1699 | ||
1700 | foreach my $opt (keys %$settings) { | |
1701 | next if $opt !~ m/^net(\d+)$/; | |
1702 | my $net = parse_net($settings->{$opt}); | |
1703 | next if !$net; | |
1704 | $settings->{$opt} = print_net($net); | |
1705 | } | |
1706 | } | |
1707 | ||
055d554d DM |
1708 | sub vm_is_volid_owner { |
1709 | my ($storecfg, $vmid, $volid) = @_; | |
1710 | ||
1711 | if ($volid !~ m|^/|) { | |
1712 | my ($path, $owner); | |
1713 | eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); }; | |
1714 | if ($owner && ($owner == $vmid)) { | |
1715 | return 1; | |
1716 | } | |
1717 | } | |
1718 | ||
1719 | return undef; | |
1720 | } | |
1721 | ||
3dc38fbb WB |
1722 | sub split_flagged_list { |
1723 | my $text = shift || ''; | |
1724 | $text =~ s/[,;]/ /g; | |
1725 | $text =~ s/^\s+//; | |
1726 | return { map { /^(!?)(.*)$/ && ($2, $1) } ($text =~ /\S+/g) }; | |
1727 | } | |
1728 | ||
1729 | sub join_flagged_list { | |
1730 | my ($how, $lst) = @_; | |
1731 | join $how, map { $lst->{$_} . $_ } keys %$lst; | |
1732 | } | |
1733 | ||
055d554d | 1734 | sub vmconfig_delete_pending_option { |
3dc38fbb | 1735 | my ($conf, $key, $force) = @_; |
055d554d DM |
1736 | |
1737 | delete $conf->{pending}->{$key}; | |
3dc38fbb WB |
1738 | my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete}); |
1739 | $pending_delete_hash->{$key} = $force ? '!' : ''; | |
1740 | $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash); | |
055d554d DM |
1741 | } |
1742 | ||
1743 | sub vmconfig_undelete_pending_option { | |
1744 | my ($conf, $key) = @_; | |
1745 | ||
3dc38fbb | 1746 | my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete}); |
055d554d DM |
1747 | delete $pending_delete_hash->{$key}; |
1748 | ||
3dc38fbb WB |
1749 | if (%$pending_delete_hash) { |
1750 | $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash); | |
055d554d DM |
1751 | } else { |
1752 | delete $conf->{pending}->{delete}; | |
1753 | } | |
1754 | } | |
1755 | ||
1756 | sub vmconfig_register_unused_drive { | |
1757 | my ($storecfg, $vmid, $conf, $drive) = @_; | |
1758 | ||
1759 | if (!drive_is_cdrom($drive)) { | |
1760 | my $volid = $drive->{file}; | |
1761 | if (vm_is_volid_owner($storecfg, $vmid, $volid)) { | |
8793d495 | 1762 | PVE::QemuConfig->add_unused_volume($conf, $volid, $vmid); |
055d554d DM |
1763 | } |
1764 | } | |
1765 | } | |
1766 | ||
c750e90a DM |
1767 | sub vmconfig_cleanup_pending { |
1768 | my ($conf) = @_; | |
1769 | ||
1770 | # remove pending changes when nothing changed | |
1771 | my $changes; | |
1772 | foreach my $opt (keys %{$conf->{pending}}) { | |
1773 | if (defined($conf->{$opt}) && ($conf->{pending}->{$opt} eq $conf->{$opt})) { | |
1774 | $changes = 1; | |
1775 | delete $conf->{pending}->{$opt}; | |
1776 | } | |
1777 | } | |
1778 | ||
3dc38fbb | 1779 | my $current_delete_hash = split_flagged_list($conf->{pending}->{delete}); |
c750e90a | 1780 | my $pending_delete_hash = {}; |
3dc38fbb | 1781 | while (my ($opt, $force) = each %$current_delete_hash) { |
c750e90a | 1782 | if (defined($conf->{$opt})) { |
3dc38fbb | 1783 | $pending_delete_hash->{$opt} = $force; |
c750e90a DM |
1784 | } else { |
1785 | $changes = 1; | |
1786 | } | |
1787 | } | |
1788 | ||
3dc38fbb WB |
1789 | if (%$pending_delete_hash) { |
1790 | $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash); | |
c750e90a DM |
1791 | } else { |
1792 | delete $conf->{pending}->{delete}; | |
1793 | } | |
1794 | ||
1795 | return $changes; | |
1796 | } | |
1797 | ||
bd27e851 | 1798 | # smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str] |
ff6ffe20 | 1799 | my $smbios1_fmt = { |
bd27e851 WB |
1800 | uuid => { |
1801 | type => 'string', | |
1802 | pattern => '[a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}', | |
1803 | format_description => 'UUID', | |
52261945 | 1804 | description => "Set SMBIOS1 UUID.", |
bd27e851 WB |
1805 | optional => 1, |
1806 | }, | |
1807 | version => { | |
1808 | type => 'string', | |
1809 | pattern => '\S+', | |
7f694a71 | 1810 | format_description => 'string', |
52261945 | 1811 | description => "Set SMBIOS1 version.", |
bd27e851 WB |
1812 | optional => 1, |
1813 | }, | |
1814 | serial => { | |
1815 | type => 'string', | |
1816 | pattern => '\S+', | |
7f694a71 | 1817 | format_description => 'string', |
52261945 | 1818 | description => "Set SMBIOS1 serial number.", |
bd27e851 WB |
1819 | optional => 1, |
1820 | }, | |
1821 | manufacturer => { | |
1822 | type => 'string', | |
1823 | pattern => '\S+', | |
7f694a71 | 1824 | format_description => 'string', |
52261945 | 1825 | description => "Set SMBIOS1 manufacturer.", |
bd27e851 WB |
1826 | optional => 1, |
1827 | }, | |
1828 | product => { | |
1829 | type => 'string', | |
1830 | pattern => '\S+', | |
7f694a71 | 1831 | format_description => 'string', |
52261945 | 1832 | description => "Set SMBIOS1 product ID.", |
bd27e851 WB |
1833 | optional => 1, |
1834 | }, | |
1835 | sku => { | |
1836 | type => 'string', | |
1837 | pattern => '\S+', | |
7f694a71 | 1838 | format_description => 'string', |
52261945 | 1839 | description => "Set SMBIOS1 SKU string.", |
bd27e851 WB |
1840 | optional => 1, |
1841 | }, | |
1842 | family => { | |
1843 | type => 'string', | |
1844 | pattern => '\S+', | |
7f694a71 | 1845 | format_description => 'string', |
52261945 | 1846 | description => "Set SMBIOS1 family string.", |
bd27e851 WB |
1847 | optional => 1, |
1848 | }, | |
2796e7d5 DM |
1849 | }; |
1850 | ||
2796e7d5 DM |
1851 | sub parse_smbios1 { |
1852 | my ($data) = @_; | |
1853 | ||
ff6ffe20 | 1854 | my $res = eval { PVE::JSONSchema::parse_property_string($smbios1_fmt, $data) }; |
bd27e851 | 1855 | warn $@ if $@; |
2796e7d5 DM |
1856 | return $res; |
1857 | } | |
1858 | ||
cd11416f DM |
1859 | sub print_smbios1 { |
1860 | my ($smbios1) = @_; | |
ff6ffe20 | 1861 | return PVE::JSONSchema::print_property_string($smbios1, $smbios1_fmt); |
cd11416f DM |
1862 | } |
1863 | ||
ff6ffe20 | 1864 | PVE::JSONSchema::register_format('pve-qm-smbios1', $smbios1_fmt); |
2796e7d5 | 1865 | |
1e3baf05 DM |
1866 | PVE::JSONSchema::register_format('pve-qm-bootdisk', \&verify_bootdisk); |
1867 | sub verify_bootdisk { | |
1868 | my ($value, $noerr) = @_; | |
1869 | ||
74479ee9 | 1870 | return $value if is_valid_drivename($value); |
1e3baf05 DM |
1871 | |
1872 | return undef if $noerr; | |
1873 | ||
1874 | die "invalid boot disk '$value'\n"; | |
1875 | } | |
1876 | ||
0ea9541d DM |
1877 | sub parse_watchdog { |
1878 | my ($value) = @_; | |
1879 | ||
1880 | return undef if !$value; | |
1881 | ||
ec3582b5 WB |
1882 | my $res = eval { PVE::JSONSchema::parse_property_string($watchdog_fmt, $value) }; |
1883 | warn $@ if $@; | |
0ea9541d DM |
1884 | return $res; |
1885 | } | |
1886 | ||
1e3baf05 DM |
1887 | sub parse_usb_device { |
1888 | my ($value) = @_; | |
1889 | ||
1890 | return undef if !$value; | |
1891 | ||
1e3baf05 | 1892 | my $res = {}; |
a6b9aee4 DC |
1893 | if ($value =~ m/^(0x)?([0-9A-Fa-f]{4}):(0x)?([0-9A-Fa-f]{4})$/) { |
1894 | $res->{vendorid} = $2; | |
1895 | $res->{productid} = $4; | |
1896 | } elsif ($value =~ m/^(\d+)\-(\d+(\.\d+)*)$/) { | |
1897 | $res->{hostbus} = $1; | |
1898 | $res->{hostport} = $2; | |
1899 | } elsif ($value =~ m/^spice$/i) { | |
1900 | $res->{spice} = 1; | |
1901 | } else { | |
1902 | return undef; | |
1e3baf05 | 1903 | } |
1e3baf05 DM |
1904 | |
1905 | return $res; | |
1906 | } | |
19672434 | 1907 | |
1e3baf05 DM |
1908 | PVE::JSONSchema::register_format('pve-qm-usb-device', \&verify_usb_device); |
1909 | sub verify_usb_device { | |
1910 | my ($value, $noerr) = @_; | |
1911 | ||
1912 | return $value if parse_usb_device($value); | |
1913 | ||
1914 | return undef if $noerr; | |
19672434 | 1915 | |
1e3baf05 DM |
1916 | die "unable to parse usb device\n"; |
1917 | } | |
1918 | ||
1e3baf05 DM |
1919 | # add JSON properties for create and set function |
1920 | sub json_config_properties { | |
1921 | my $prop = shift; | |
1922 | ||
1923 | foreach my $opt (keys %$confdesc) { | |
18bfb361 | 1924 | next if $opt eq 'parent' || $opt eq 'snaptime' || $opt eq 'vmstate'; |
1e3baf05 DM |
1925 | $prop->{$opt} = $confdesc->{$opt}; |
1926 | } | |
1927 | ||
1928 | return $prop; | |
1929 | } | |
1930 | ||
1931 | sub check_type { | |
1932 | my ($key, $value) = @_; | |
1933 | ||
1934 | die "unknown setting '$key'\n" if !$confdesc->{$key}; | |
1935 | ||
1936 | my $type = $confdesc->{$key}->{type}; | |
1937 | ||
6b64503e | 1938 | if (!defined($value)) { |
1e3baf05 DM |
1939 | die "got undefined value\n"; |
1940 | } | |
1941 | ||
1942 | if ($value =~ m/[\n\r]/) { | |
1943 | die "property contains a line feed\n"; | |
1944 | } | |
1945 | ||
1946 | if ($type eq 'boolean') { | |
19672434 DM |
1947 | return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i); |
1948 | return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i); | |
1949 | die "type check ('boolean') failed - got '$value'\n"; | |
1e3baf05 DM |
1950 | } elsif ($type eq 'integer') { |
1951 | return int($1) if $value =~ m/^(\d+)$/; | |
1952 | die "type check ('integer') failed - got '$value'\n"; | |
04432191 AD |
1953 | } elsif ($type eq 'number') { |
1954 | return $value if $value =~ m/^(\d+)(\.\d+)?$/; | |
1955 | die "type check ('number') failed - got '$value'\n"; | |
1e3baf05 DM |
1956 | } elsif ($type eq 'string') { |
1957 | if (my $fmt = $confdesc->{$key}->{format}) { | |
1958 | if ($fmt eq 'pve-qm-drive') { | |
1959 | # special case - we need to pass $key to parse_drive() | |
6b64503e | 1960 | my $drive = parse_drive($key, $value); |
1e3baf05 DM |
1961 | return $value if $drive; |
1962 | die "unable to parse drive options\n"; | |
1963 | } | |
1964 | PVE::JSONSchema::check_format($fmt, $value); | |
19672434 DM |
1965 | return $value; |
1966 | } | |
1e3baf05 | 1967 | $value =~ s/^\"(.*)\"$/$1/; |
19672434 | 1968 | return $value; |
1e3baf05 DM |
1969 | } else { |
1970 | die "internal error" | |
1971 | } | |
1972 | } | |
1973 | ||
040b06b7 DA |
1974 | sub check_iommu_support{ |
1975 | #fixme : need to check IOMMU support | |
1976 | #http://www.linux-kvm.org/page/How_to_assign_devices_with_VT-d_in_KVM | |
1977 | ||
1978 | my $iommu=1; | |
1979 | return $iommu; | |
1980 | ||
1981 | } | |
1982 | ||
1e3baf05 DM |
1983 | sub touch_config { |
1984 | my ($vmid) = @_; | |
1985 | ||
ffda963f | 1986 | my $conf = PVE::QemuConfig->config_file($vmid); |
1e3baf05 DM |
1987 | utime undef, undef, $conf; |
1988 | } | |
1989 | ||
1e3baf05 | 1990 | sub destroy_vm { |
15cc8784 | 1991 | my ($storecfg, $vmid, $keep_empty_config, $skiplock) = @_; |
1e3baf05 | 1992 | |
ffda963f | 1993 | my $conffile = PVE::QemuConfig->config_file($vmid); |
1e3baf05 | 1994 | |
ffda963f | 1995 | my $conf = PVE::QemuConfig->load_config($vmid); |
1e3baf05 | 1996 | |
ffda963f | 1997 | PVE::QemuConfig->check_lock($conf) if !$skiplock; |
1e3baf05 | 1998 | |
19672434 | 1999 | # only remove disks owned by this VM |
1e3baf05 DM |
2000 | foreach_drive($conf, sub { |
2001 | my ($ds, $drive) = @_; | |
2002 | ||
6b64503e | 2003 | return if drive_is_cdrom($drive); |
1e3baf05 DM |
2004 | |
2005 | my $volid = $drive->{file}; | |
ed221350 | 2006 | |
ff1a2432 | 2007 | return if !$volid || $volid =~ m|^/|; |
1e3baf05 | 2008 | |
6b64503e | 2009 | my ($path, $owner) = PVE::Storage::path($storecfg, $volid); |
ff1a2432 | 2010 | return if !$path || !$owner || ($owner != $vmid); |
1e3baf05 | 2011 | |
6b64503e | 2012 | PVE::Storage::vdisk_free($storecfg, $volid); |
1e3baf05 | 2013 | }); |
19672434 | 2014 | |
a6af7b3e | 2015 | if ($keep_empty_config) { |
9c502e26 | 2016 | PVE::Tools::file_set_contents($conffile, "memory: 128\n"); |
a6af7b3e DM |
2017 | } else { |
2018 | unlink $conffile; | |
2019 | } | |
1e3baf05 DM |
2020 | |
2021 | # also remove unused disk | |
2022 | eval { | |
6b64503e | 2023 | my $dl = PVE::Storage::vdisk_list($storecfg, undef, $vmid); |
1e3baf05 DM |
2024 | |
2025 | eval { | |
6b64503e | 2026 | PVE::Storage::foreach_volid($dl, sub { |
1e3baf05 | 2027 | my ($volid, $sid, $volname, $d) = @_; |
6b64503e | 2028 | PVE::Storage::vdisk_free($storecfg, $volid); |
1e3baf05 DM |
2029 | }); |
2030 | }; | |
2031 | warn $@ if $@; | |
2032 | ||
2033 | }; | |
2034 | warn $@ if $@; | |
2035 | } | |
2036 | ||
1e3baf05 DM |
2037 | sub parse_vm_config { |
2038 | my ($filename, $raw) = @_; | |
2039 | ||
2040 | return undef if !defined($raw); | |
2041 | ||
554ac7e7 | 2042 | my $res = { |
fc1ddcdc | 2043 | digest => Digest::SHA::sha1_hex($raw), |
0d18dcfc | 2044 | snapshots => {}, |
0d732d16 | 2045 | pending => {}, |
554ac7e7 | 2046 | }; |
1e3baf05 | 2047 | |
19672434 | 2048 | $filename =~ m|/qemu-server/(\d+)\.conf$| |
1e3baf05 DM |
2049 | || die "got strange filename '$filename'"; |
2050 | ||
2051 | my $vmid = $1; | |
2052 | ||
0d18dcfc | 2053 | my $conf = $res; |
b0ec896e | 2054 | my $descr; |
e297c490 | 2055 | my $section = ''; |
0581fe4f | 2056 | |
0d18dcfc DM |
2057 | my @lines = split(/\n/, $raw); |
2058 | foreach my $line (@lines) { | |
1e3baf05 | 2059 | next if $line =~ m/^\s*$/; |
be190583 | 2060 | |
eab09f4e | 2061 | if ($line =~ m/^\[PENDING\]\s*$/i) { |
e297c490 | 2062 | $section = 'pending'; |
b0ec896e DM |
2063 | if (defined($descr)) { |
2064 | $descr =~ s/\s+$//; | |
2065 | $conf->{description} = $descr; | |
2066 | } | |
2067 | $descr = undef; | |
e297c490 | 2068 | $conf = $res->{$section} = {}; |
eab09f4e AD |
2069 | next; |
2070 | ||
0d732d16 | 2071 | } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { |
e297c490 | 2072 | $section = $1; |
b0ec896e DM |
2073 | if (defined($descr)) { |
2074 | $descr =~ s/\s+$//; | |
2075 | $conf->{description} = $descr; | |
2076 | } | |
2077 | $descr = undef; | |
e297c490 | 2078 | $conf = $res->{snapshots}->{$section} = {}; |
0d18dcfc DM |
2079 | next; |
2080 | } | |
1e3baf05 | 2081 | |
0581fe4f | 2082 | if ($line =~ m/^\#(.*)\s*$/) { |
b0ec896e | 2083 | $descr = '' if !defined($descr); |
0581fe4f DM |
2084 | $descr .= PVE::Tools::decode_text($1) . "\n"; |
2085 | next; | |
2086 | } | |
2087 | ||
1e3baf05 | 2088 | if ($line =~ m/^(description):\s*(.*\S)\s*$/) { |
b0ec896e | 2089 | $descr = '' if !defined($descr); |
0581fe4f | 2090 | $descr .= PVE::Tools::decode_text($2); |
0d18dcfc DM |
2091 | } elsif ($line =~ m/snapstate:\s*(prepare|delete)\s*$/) { |
2092 | $conf->{snapstate} = $1; | |
1e3baf05 DM |
2093 | } elsif ($line =~ m/^(args):\s*(.*\S)\s*$/) { |
2094 | my $key = $1; | |
2095 | my $value = $2; | |
0d18dcfc | 2096 | $conf->{$key} = $value; |
ef824322 | 2097 | } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) { |
e297c490 | 2098 | my $value = $1; |
ef824322 DM |
2099 | if ($section eq 'pending') { |
2100 | $conf->{delete} = $value; # we parse this later | |
2101 | } else { | |
2102 | warn "vm $vmid - propertry 'delete' is only allowed in [PENDING]\n"; | |
eab09f4e | 2103 | } |
1e3baf05 DM |
2104 | } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(\S+)\s*$/) { |
2105 | my $key = $1; | |
2106 | my $value = $2; | |
2107 | eval { $value = check_type($key, $value); }; | |
2108 | if ($@) { | |
2109 | warn "vm $vmid - unable to parse value of '$key' - $@"; | |
2110 | } else { | |
2111 | my $fmt = $confdesc->{$key}->{format}; | |
2112 | if ($fmt && $fmt eq 'pve-qm-drive') { | |
2113 | my $v = parse_drive($key, $value); | |
2114 | if (my $volid = filename_to_volume_id($vmid, $v->{file}, $v->{media})) { | |
2115 | $v->{file} = $volid; | |
6b64503e | 2116 | $value = print_drive($vmid, $v); |
1e3baf05 DM |
2117 | } else { |
2118 | warn "vm $vmid - unable to parse value of '$key'\n"; | |
2119 | next; | |
2120 | } | |
2121 | } | |
2122 | ||
2123 | if ($key eq 'cdrom') { | |
0d18dcfc | 2124 | $conf->{ide2} = $value; |
1e3baf05 | 2125 | } else { |
0d18dcfc | 2126 | $conf->{$key} = $value; |
1e3baf05 DM |
2127 | } |
2128 | } | |
2129 | } | |
2130 | } | |
2131 | ||
b0ec896e DM |
2132 | if (defined($descr)) { |
2133 | $descr =~ s/\s+$//; | |
2134 | $conf->{description} = $descr; | |
2135 | } | |
0d18dcfc | 2136 | delete $res->{snapstate}; # just to be sure |
1e3baf05 DM |
2137 | |
2138 | return $res; | |
2139 | } | |
2140 | ||
1858638f DM |
2141 | sub write_vm_config { |
2142 | my ($filename, $conf) = @_; | |
1e3baf05 | 2143 | |
0d18dcfc DM |
2144 | delete $conf->{snapstate}; # just to be sure |
2145 | ||
1858638f DM |
2146 | if ($conf->{cdrom}) { |
2147 | die "option ide2 conflicts with cdrom\n" if $conf->{ide2}; | |
2148 | $conf->{ide2} = $conf->{cdrom}; | |
2149 | delete $conf->{cdrom}; | |
2150 | } | |
1e3baf05 DM |
2151 | |
2152 | # we do not use 'smp' any longer | |
1858638f DM |
2153 | if ($conf->{sockets}) { |
2154 | delete $conf->{smp}; | |
2155 | } elsif ($conf->{smp}) { | |
2156 | $conf->{sockets} = $conf->{smp}; | |
2157 | delete $conf->{cores}; | |
2158 | delete $conf->{smp}; | |
1e3baf05 DM |
2159 | } |
2160 | ||
ee2f90b1 | 2161 | my $used_volids = {}; |
0d18dcfc | 2162 | |
ee2f90b1 | 2163 | my $cleanup_config = sub { |
ef824322 | 2164 | my ($cref, $pending, $snapname) = @_; |
1858638f | 2165 | |
ee2f90b1 DM |
2166 | foreach my $key (keys %$cref) { |
2167 | next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' || | |
ef824322 | 2168 | $key eq 'snapstate' || $key eq 'pending'; |
ee2f90b1 | 2169 | my $value = $cref->{$key}; |
ef824322 DM |
2170 | if ($key eq 'delete') { |
2171 | die "propertry 'delete' is only allowed in [PENDING]\n" | |
2172 | if !$pending; | |
2173 | # fixme: check syntax? | |
2174 | next; | |
2175 | } | |
ee2f90b1 DM |
2176 | eval { $value = check_type($key, $value); }; |
2177 | die "unable to parse value of '$key' - $@" if $@; | |
1858638f | 2178 | |
ee2f90b1 DM |
2179 | $cref->{$key} = $value; |
2180 | ||
74479ee9 | 2181 | if (!$snapname && is_valid_drivename($key)) { |
ed221350 | 2182 | my $drive = parse_drive($key, $value); |
ee2f90b1 DM |
2183 | $used_volids->{$drive->{file}} = 1 if $drive && $drive->{file}; |
2184 | } | |
1e3baf05 | 2185 | } |
ee2f90b1 DM |
2186 | }; |
2187 | ||
2188 | &$cleanup_config($conf); | |
ef824322 DM |
2189 | |
2190 | &$cleanup_config($conf->{pending}, 1); | |
2191 | ||
ee2f90b1 | 2192 | foreach my $snapname (keys %{$conf->{snapshots}}) { |
ef824322 DM |
2193 | die "internal error" if $snapname eq 'pending'; |
2194 | &$cleanup_config($conf->{snapshots}->{$snapname}, undef, $snapname); | |
1e3baf05 DM |
2195 | } |
2196 | ||
1858638f DM |
2197 | # remove 'unusedX' settings if we re-add a volume |
2198 | foreach my $key (keys %$conf) { | |
2199 | my $value = $conf->{$key}; | |
ee2f90b1 | 2200 | if ($key =~ m/^unused/ && $used_volids->{$value}) { |
1858638f | 2201 | delete $conf->{$key}; |
1e3baf05 | 2202 | } |
1858638f | 2203 | } |
be190583 | 2204 | |
0d18dcfc | 2205 | my $generate_raw_config = sub { |
b0ec896e | 2206 | my ($conf, $pending) = @_; |
0581fe4f | 2207 | |
0d18dcfc DM |
2208 | my $raw = ''; |
2209 | ||
2210 | # add description as comment to top of file | |
b0ec896e DM |
2211 | if (defined(my $descr = $conf->{description})) { |
2212 | if ($descr) { | |
2213 | foreach my $cl (split(/\n/, $descr)) { | |
2214 | $raw .= '#' . PVE::Tools::encode_text($cl) . "\n"; | |
2215 | } | |
2216 | } else { | |
2217 | $raw .= "#\n" if $pending; | |
2218 | } | |
0d18dcfc DM |
2219 | } |
2220 | ||
2221 | foreach my $key (sort keys %$conf) { | |
ef824322 | 2222 | next if $key eq 'digest' || $key eq 'description' || $key eq 'pending' || $key eq 'snapshots'; |
0d18dcfc DM |
2223 | $raw .= "$key: $conf->{$key}\n"; |
2224 | } | |
2225 | return $raw; | |
2226 | }; | |
0581fe4f | 2227 | |
0d18dcfc | 2228 | my $raw = &$generate_raw_config($conf); |
ef824322 DM |
2229 | |
2230 | if (scalar(keys %{$conf->{pending}})){ | |
2231 | $raw .= "\n[PENDING]\n"; | |
b0ec896e | 2232 | $raw .= &$generate_raw_config($conf->{pending}, 1); |
ef824322 DM |
2233 | } |
2234 | ||
0d18dcfc DM |
2235 | foreach my $snapname (sort keys %{$conf->{snapshots}}) { |
2236 | $raw .= "\n[$snapname]\n"; | |
2237 | $raw .= &$generate_raw_config($conf->{snapshots}->{$snapname}); | |
1858638f | 2238 | } |
1e3baf05 | 2239 | |
1858638f DM |
2240 | return $raw; |
2241 | } | |
1e3baf05 | 2242 | |
19672434 | 2243 | sub load_defaults { |
1e3baf05 DM |
2244 | |
2245 | my $res = {}; | |
2246 | ||
2247 | # we use static defaults from our JSON schema configuration | |
2248 | foreach my $key (keys %$confdesc) { | |
2249 | if (defined(my $default = $confdesc->{$key}->{default})) { | |
2250 | $res->{$key} = $default; | |
2251 | } | |
2252 | } | |
19672434 | 2253 | |
1e3baf05 DM |
2254 | my $conf = PVE::Cluster::cfs_read_file('datacenter.cfg'); |
2255 | $res->{keyboard} = $conf->{keyboard} if $conf->{keyboard}; | |
2256 | ||
2257 | return $res; | |
2258 | } | |
2259 | ||
2260 | sub config_list { | |
2261 | my $vmlist = PVE::Cluster::get_vmlist(); | |
2262 | my $res = {}; | |
2263 | return $res if !$vmlist || !$vmlist->{ids}; | |
2264 | my $ids = $vmlist->{ids}; | |
2265 | ||
1e3baf05 DM |
2266 | foreach my $vmid (keys %$ids) { |
2267 | my $d = $ids->{$vmid}; | |
2268 | next if !$d->{node} || $d->{node} ne $nodename; | |
5ee957cc | 2269 | next if !$d->{type} || $d->{type} ne 'qemu'; |
1e3baf05 DM |
2270 | $res->{$vmid}->{exists} = 1; |
2271 | } | |
2272 | return $res; | |
2273 | } | |
2274 | ||
64e13401 DM |
2275 | # test if VM uses local resources (to prevent migration) |
2276 | sub check_local_resources { | |
2277 | my ($conf, $noerr) = @_; | |
2278 | ||
2279 | my $loc_res = 0; | |
19672434 | 2280 | |
e0ab7331 DM |
2281 | $loc_res = 1 if $conf->{hostusb}; # old syntax |
2282 | $loc_res = 1 if $conf->{hostpci}; # old syntax | |
64e13401 | 2283 | |
0d29ab3b | 2284 | foreach my $k (keys %$conf) { |
49ca581d | 2285 | next if $k =~ m/^usb/ && ($conf->{$k} eq 'spice'); |
d44712fc EK |
2286 | # sockets are safe: they will recreated be on the target side post-migrate |
2287 | next if $k =~ m/^serial/ && ($conf->{$k} eq 'socket'); | |
2fe1a152 | 2288 | $loc_res = 1 if $k =~ m/^(usb|hostpci|serial|parallel)\d+$/; |
64e13401 DM |
2289 | } |
2290 | ||
2291 | die "VM uses local resources\n" if $loc_res && !$noerr; | |
2292 | ||
2293 | return $loc_res; | |
2294 | } | |
2295 | ||
719893a9 | 2296 | # check if used storages are available on all nodes (use by migrate) |
47152e2e DM |
2297 | sub check_storage_availability { |
2298 | my ($storecfg, $conf, $node) = @_; | |
2299 | ||
2300 | foreach_drive($conf, sub { | |
2301 | my ($ds, $drive) = @_; | |
2302 | ||
2303 | my $volid = $drive->{file}; | |
2304 | return if !$volid; | |
2305 | ||
2306 | my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1); | |
2307 | return if !$sid; | |
2308 | ||
2309 | # check if storage is available on both nodes | |
2310 | my $scfg = PVE::Storage::storage_check_node($storecfg, $sid); | |
2311 | PVE::Storage::storage_check_node($storecfg, $sid, $node); | |
2312 | }); | |
2313 | } | |
2314 | ||
719893a9 DM |
2315 | # list nodes where all VM images are available (used by has_feature API) |
2316 | sub shared_nodes { | |
2317 | my ($conf, $storecfg) = @_; | |
2318 | ||
2319 | my $nodelist = PVE::Cluster::get_nodelist(); | |
2320 | my $nodehash = { map { $_ => 1 } @$nodelist }; | |
2321 | my $nodename = PVE::INotify::nodename(); | |
be190583 | 2322 | |
719893a9 DM |
2323 | foreach_drive($conf, sub { |
2324 | my ($ds, $drive) = @_; | |
2325 | ||
2326 | my $volid = $drive->{file}; | |
2327 | return if !$volid; | |
2328 | ||
2329 | my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, 1); | |
2330 | if ($storeid) { | |
2331 | my $scfg = PVE::Storage::storage_config($storecfg, $storeid); | |
2332 | if ($scfg->{disable}) { | |
2333 | $nodehash = {}; | |
2334 | } elsif (my $avail = $scfg->{nodes}) { | |
2335 | foreach my $node (keys %$nodehash) { | |
2336 | delete $nodehash->{$node} if !$avail->{$node}; | |
2337 | } | |
2338 | } elsif (!$scfg->{shared}) { | |
2339 | foreach my $node (keys %$nodehash) { | |
2340 | delete $nodehash->{$node} if $node ne $nodename | |
2341 | } | |
2342 | } | |
2343 | } | |
2344 | }); | |
2345 | ||
2346 | return $nodehash | |
2347 | } | |
2348 | ||
1e3baf05 DM |
2349 | sub check_cmdline { |
2350 | my ($pidfile, $pid) = @_; | |
2351 | ||
6b64503e DM |
2352 | my $fh = IO::File->new("/proc/$pid/cmdline", "r"); |
2353 | if (defined($fh)) { | |
1e3baf05 DM |
2354 | my $line = <$fh>; |
2355 | $fh->close; | |
2356 | return undef if !$line; | |
6b64503e | 2357 | my @param = split(/\0/, $line); |
1e3baf05 DM |
2358 | |
2359 | my $cmd = $param[0]; | |
06094efd | 2360 | return if !$cmd || ($cmd !~ m|kvm$| && $cmd !~ m|qemu-system-x86_64$|); |
1e3baf05 DM |
2361 | |
2362 | for (my $i = 0; $i < scalar (@param); $i++) { | |
2363 | my $p = $param[$i]; | |
2364 | next if !$p; | |
2365 | if (($p eq '-pidfile') || ($p eq '--pidfile')) { | |
2366 | my $p = $param[$i+1]; | |
2367 | return 1 if $p && ($p eq $pidfile); | |
2368 | return undef; | |
2369 | } | |
2370 | } | |
2371 | } | |
2372 | return undef; | |
2373 | } | |
2374 | ||
2375 | sub check_running { | |
7e8dcf2c | 2376 | my ($vmid, $nocheck, $node) = @_; |
1e3baf05 | 2377 | |
ffda963f | 2378 | my $filename = PVE::QemuConfig->config_file($vmid, $node); |
1e3baf05 DM |
2379 | |
2380 | die "unable to find configuration file for VM $vmid - no such machine\n" | |
e6c3b671 | 2381 | if !$nocheck && ! -f $filename; |
1e3baf05 | 2382 | |
e6c3b671 | 2383 | my $pidfile = pidfile_name($vmid); |
1e3baf05 | 2384 | |
e6c3b671 DM |
2385 | if (my $fd = IO::File->new("<$pidfile")) { |
2386 | my $st = stat($fd); | |
1e3baf05 | 2387 | my $line = <$fd>; |
6b64503e | 2388 | close($fd); |
1e3baf05 DM |
2389 | |
2390 | my $mtime = $st->mtime; | |
2391 | if ($mtime > time()) { | |
2392 | warn "file '$filename' modified in future\n"; | |
2393 | } | |
2394 | ||
2395 | if ($line =~ m/^(\d+)$/) { | |
2396 | my $pid = $1; | |
e6c3b671 DM |
2397 | if (check_cmdline($pidfile, $pid)) { |
2398 | if (my $pinfo = PVE::ProcFSTools::check_process_running($pid)) { | |
2399 | return $pid; | |
2400 | } | |
2401 | } | |
1e3baf05 DM |
2402 | } |
2403 | } | |
2404 | ||
2405 | return undef; | |
2406 | } | |
2407 | ||
2408 | sub vzlist { | |
19672434 | 2409 | |
1e3baf05 DM |
2410 | my $vzlist = config_list(); |
2411 | ||
6b64503e | 2412 | my $fd = IO::Dir->new($var_run_tmpdir) || return $vzlist; |
1e3baf05 | 2413 | |
19672434 | 2414 | while (defined(my $de = $fd->read)) { |
1e3baf05 DM |
2415 | next if $de !~ m/^(\d+)\.pid$/; |
2416 | my $vmid = $1; | |
6b64503e DM |
2417 | next if !defined($vzlist->{$vmid}); |
2418 | if (my $pid = check_running($vmid)) { | |
1e3baf05 DM |
2419 | $vzlist->{$vmid}->{pid} = $pid; |
2420 | } | |
2421 | } | |
2422 | ||
2423 | return $vzlist; | |
2424 | } | |
2425 | ||
1e3baf05 DM |
2426 | sub disksize { |
2427 | my ($storecfg, $conf) = @_; | |
2428 | ||
2429 | my $bootdisk = $conf->{bootdisk}; | |
2430 | return undef if !$bootdisk; | |
74479ee9 | 2431 | return undef if !is_valid_drivename($bootdisk); |
1e3baf05 DM |
2432 | |
2433 | return undef if !$conf->{$bootdisk}; | |
2434 | ||
2435 | my $drive = parse_drive($bootdisk, $conf->{$bootdisk}); | |
2436 | return undef if !defined($drive); | |
2437 | ||
2438 | return undef if drive_is_cdrom($drive); | |
2439 | ||
2440 | my $volid = $drive->{file}; | |
2441 | return undef if !$volid; | |
2442 | ||
24afaca0 | 2443 | return $drive->{size}; |
1e3baf05 DM |
2444 | } |
2445 | ||
2446 | my $last_proc_pid_stat; | |
2447 | ||
03a33f30 DM |
2448 | # get VM status information |
2449 | # This must be fast and should not block ($full == false) | |
2450 | # We only query KVM using QMP if $full == true (this can be slow) | |
1e3baf05 | 2451 | sub vmstatus { |
03a33f30 | 2452 | my ($opt_vmid, $full) = @_; |
1e3baf05 DM |
2453 | |
2454 | my $res = {}; | |
2455 | ||
19672434 | 2456 | my $storecfg = PVE::Storage::config(); |
1e3baf05 DM |
2457 | |
2458 | my $list = vzlist(); | |
694fcad4 | 2459 | my ($uptime) = PVE::ProcFSTools::read_proc_uptime(1); |
1e3baf05 | 2460 | |
ae4915a2 DM |
2461 | my $cpucount = $cpuinfo->{cpus} || 1; |
2462 | ||
1e3baf05 DM |
2463 | foreach my $vmid (keys %$list) { |
2464 | next if $opt_vmid && ($vmid ne $opt_vmid); | |
2465 | ||
ffda963f | 2466 | my $cfspath = PVE::QemuConfig->cfs_config_path($vmid); |
1e3baf05 DM |
2467 | my $conf = PVE::Cluster::cfs_read_file($cfspath) || {}; |
2468 | ||
2469 | my $d = {}; | |
2470 | $d->{pid} = $list->{$vmid}->{pid}; | |
2471 | ||
2472 | # fixme: better status? | |
2473 | $d->{status} = $list->{$vmid}->{pid} ? 'running' : 'stopped'; | |
2474 | ||
af990afe DM |
2475 | my $size = disksize($storecfg, $conf); |
2476 | if (defined($size)) { | |
2477 | $d->{disk} = 0; # no info available | |
1e3baf05 DM |
2478 | $d->{maxdisk} = $size; |
2479 | } else { | |
2480 | $d->{disk} = 0; | |
2481 | $d->{maxdisk} = 0; | |
2482 | } | |
2483 | ||
2484 | $d->{cpus} = ($conf->{sockets} || 1) * ($conf->{cores} || 1); | |
ae4915a2 | 2485 | $d->{cpus} = $cpucount if $d->{cpus} > $cpucount; |
d7c8364b | 2486 | $d->{cpus} = $conf->{vcpus} if $conf->{vcpus}; |
ae4915a2 | 2487 | |
1e3baf05 | 2488 | $d->{name} = $conf->{name} || "VM $vmid"; |
19672434 | 2489 | $d->{maxmem} = $conf->{memory} ? $conf->{memory}*(1024*1024) : 0; |
1e3baf05 | 2490 | |
8b1accf7 | 2491 | if ($conf->{balloon}) { |
4bdb0514 | 2492 | $d->{balloon_min} = $conf->{balloon}*(1024*1024); |
074e01c8 | 2493 | $d->{shares} = defined($conf->{shares}) ? $conf->{shares} : 1000; |
8b1accf7 DM |
2494 | } |
2495 | ||
1e3baf05 DM |
2496 | $d->{uptime} = 0; |
2497 | $d->{cpu} = 0; | |
1e3baf05 DM |
2498 | $d->{mem} = 0; |
2499 | ||
2500 | $d->{netout} = 0; | |
2501 | $d->{netin} = 0; | |
2502 | ||
2503 | $d->{diskread} = 0; | |
2504 | $d->{diskwrite} = 0; | |
2505 | ||
ffda963f | 2506 | $d->{template} = PVE::QemuConfig->is_template($conf); |
4d8c851b | 2507 | |
1e3baf05 DM |
2508 | $res->{$vmid} = $d; |
2509 | } | |
2510 | ||
2511 | my $netdev = PVE::ProcFSTools::read_proc_net_dev(); | |
2512 | foreach my $dev (keys %$netdev) { | |
2513 | next if $dev !~ m/^tap([1-9]\d*)i/; | |
2514 | my $vmid = $1; | |
2515 | my $d = $res->{$vmid}; | |
2516 | next if !$d; | |
19672434 | 2517 | |
1e3baf05 DM |
2518 | $d->{netout} += $netdev->{$dev}->{receive}; |
2519 | $d->{netin} += $netdev->{$dev}->{transmit}; | |
604ea644 AD |
2520 | |
2521 | if ($full) { | |
2522 | $d->{nics}->{$dev}->{netout} = $netdev->{$dev}->{receive}; | |
2523 | $d->{nics}->{$dev}->{netin} = $netdev->{$dev}->{transmit}; | |
2524 | } | |
2525 | ||
1e3baf05 DM |
2526 | } |
2527 | ||
1e3baf05 DM |
2528 | my $ctime = gettimeofday; |
2529 | ||
2530 | foreach my $vmid (keys %$list) { | |
2531 | ||
2532 | my $d = $res->{$vmid}; | |
2533 | my $pid = $d->{pid}; | |
2534 | next if !$pid; | |
2535 | ||
694fcad4 DM |
2536 | my $pstat = PVE::ProcFSTools::read_proc_pid_stat($pid); |
2537 | next if !$pstat; # not running | |
19672434 | 2538 | |
694fcad4 | 2539 | my $used = $pstat->{utime} + $pstat->{stime}; |
1e3baf05 | 2540 | |
694fcad4 | 2541 | $d->{uptime} = int(($uptime - $pstat->{starttime})/$cpuinfo->{user_hz}); |
1e3baf05 | 2542 | |
694fcad4 | 2543 | if ($pstat->{vsize}) { |
6b64503e | 2544 | $d->{mem} = int(($pstat->{rss}/$pstat->{vsize})*$d->{maxmem}); |
1e3baf05 DM |
2545 | } |
2546 | ||
2547 | my $old = $last_proc_pid_stat->{$pid}; | |
2548 | if (!$old) { | |
19672434 DM |
2549 | $last_proc_pid_stat->{$pid} = { |
2550 | time => $ctime, | |
1e3baf05 DM |
2551 | used => $used, |
2552 | cpu => 0, | |
1e3baf05 DM |
2553 | }; |
2554 | next; | |
2555 | } | |
2556 | ||
7f0b5beb | 2557 | my $dtime = ($ctime - $old->{time}) * $cpucount * $cpuinfo->{user_hz}; |
1e3baf05 DM |
2558 | |
2559 | if ($dtime > 1000) { | |
2560 | my $dutime = $used - $old->{used}; | |
2561 | ||
ae4915a2 | 2562 | $d->{cpu} = (($dutime/$dtime)* $cpucount) / $d->{cpus}; |
1e3baf05 | 2563 | $last_proc_pid_stat->{$pid} = { |
19672434 | 2564 | time => $ctime, |
1e3baf05 DM |
2565 | used => $used, |
2566 | cpu => $d->{cpu}, | |
1e3baf05 DM |
2567 | }; |
2568 | } else { | |
2569 | $d->{cpu} = $old->{cpu}; | |
1e3baf05 DM |
2570 | } |
2571 | } | |
2572 | ||
f5eb281a | 2573 | return $res if !$full; |
03a33f30 DM |
2574 | |
2575 | my $qmpclient = PVE::QMPClient->new(); | |
2576 | ||
64e7fcf2 DM |
2577 | my $ballooncb = sub { |
2578 | my ($vmid, $resp) = @_; | |
2579 | ||
2580 | my $info = $resp->{'return'}; | |
38babf81 DM |
2581 | return if !$info->{max_mem}; |
2582 | ||
64e7fcf2 DM |
2583 | my $d = $res->{$vmid}; |
2584 | ||
38babf81 DM |
2585 | # use memory assigned to VM |
2586 | $d->{maxmem} = $info->{max_mem}; | |
2587 | $d->{balloon} = $info->{actual}; | |
2588 | ||
2589 | if (defined($info->{total_mem}) && defined($info->{free_mem})) { | |
2590 | $d->{mem} = $info->{total_mem} - $info->{free_mem}; | |
2591 | $d->{freemem} = $info->{free_mem}; | |
64e7fcf2 DM |
2592 | } |
2593 | ||
604ea644 | 2594 | $d->{ballooninfo} = $info; |
64e7fcf2 DM |
2595 | }; |
2596 | ||
03a33f30 DM |
2597 | my $blockstatscb = sub { |
2598 | my ($vmid, $resp) = @_; | |
2599 | my $data = $resp->{'return'} || []; | |
2600 | my $totalrdbytes = 0; | |
2601 | my $totalwrbytes = 0; | |
604ea644 | 2602 | |
03a33f30 DM |
2603 | for my $blockstat (@$data) { |
2604 | $totalrdbytes = $totalrdbytes + $blockstat->{stats}->{rd_bytes}; | |
2605 | $totalwrbytes = $totalwrbytes + $blockstat->{stats}->{wr_bytes}; | |
604ea644 AD |
2606 | |
2607 | $blockstat->{device} =~ s/drive-//; | |
2608 | $res->{$vmid}->{blockstat}->{$blockstat->{device}} = $blockstat->{stats}; | |
03a33f30 DM |
2609 | } |
2610 | $res->{$vmid}->{diskread} = $totalrdbytes; | |
2611 | $res->{$vmid}->{diskwrite} = $totalwrbytes; | |
2612 | }; | |
2613 | ||
2614 | my $statuscb = sub { | |
2615 | my ($vmid, $resp) = @_; | |
64e7fcf2 | 2616 | |
03a33f30 | 2617 | $qmpclient->queue_cmd($vmid, $blockstatscb, 'query-blockstats'); |
64e7fcf2 DM |
2618 | # this fails if ballon driver is not loaded, so this must be |
2619 | # the last commnand (following command are aborted if this fails). | |
38babf81 | 2620 | $qmpclient->queue_cmd($vmid, $ballooncb, 'query-balloon'); |
03a33f30 DM |
2621 | |
2622 | my $status = 'unknown'; | |
2623 | if (!defined($status = $resp->{'return'}->{status})) { | |
2624 | warn "unable to get VM status\n"; | |
2625 | return; | |
2626 | } | |
2627 | ||
2628 | $res->{$vmid}->{qmpstatus} = $resp->{'return'}->{status}; | |
2629 | }; | |
2630 | ||
2631 | foreach my $vmid (keys %$list) { | |
2632 | next if $opt_vmid && ($vmid ne $opt_vmid); | |
2633 | next if !$res->{$vmid}->{pid}; # not running | |
2634 | $qmpclient->queue_cmd($vmid, $statuscb, 'query-status'); | |
2635 | } | |
2636 | ||
c8125172 | 2637 | $qmpclient->queue_execute(undef, 1); |
03a33f30 DM |
2638 | |
2639 | foreach my $vmid (keys %$list) { | |
2640 | next if $opt_vmid && ($vmid ne $opt_vmid); | |
2641 | $res->{$vmid}->{qmpstatus} = $res->{$vmid}->{status} if !$res->{$vmid}->{qmpstatus}; | |
2642 | } | |
2643 | ||
1e3baf05 DM |
2644 | return $res; |
2645 | } | |
2646 | ||
e059fb4d AD |
2647 | sub foreach_dimm { |
2648 | my ($conf, $vmid, $memory, $sockets, $func) = @_; | |
2649 | ||
2650 | my $dimm_id = 0; | |
2651 | my $current_size = 1024; | |
2652 | my $dimm_size = 512; | |
2653 | return if $current_size == $memory; | |
2654 | ||
2655 | for (my $j = 0; $j < 8; $j++) { | |
2656 | for (my $i = 0; $i < 32; $i++) { | |
2657 | my $name = "dimm${dimm_id}"; | |
2658 | $dimm_id++; | |
2659 | my $numanode = $i % $sockets; | |
2660 | $current_size += $dimm_size; | |
2661 | &$func($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory); | |
2662 | return $current_size if $current_size >= $memory; | |
2663 | } | |
2664 | $dimm_size *= 2; | |
2665 | } | |
2666 | } | |
2667 | ||
525814b2 AD |
2668 | sub foreach_reverse_dimm { |
2669 | my ($conf, $vmid, $memory, $sockets, $func) = @_; | |
2670 | ||
2671 | my $dimm_id = 253; | |
2672 | my $current_size = 4177920; | |
2673 | my $dimm_size = 65536; | |
2674 | return if $current_size == $memory; | |
2675 | ||
2676 | for (my $j = 0; $j < 8; $j++) { | |
2677 | for (my $i = 0; $i < 32; $i++) { | |
2678 | my $name = "dimm${dimm_id}"; | |
2679 | $dimm_id--; | |
2680 | my $numanode = $i % $sockets; | |
2681 | $current_size -= $dimm_size; | |
2682 | &$func($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory); | |
2683 | return $current_size if $current_size <= $memory; | |
2684 | } | |
2685 | $dimm_size /= 2; | |
2686 | } | |
2687 | } | |
2688 | ||
1e3baf05 DM |
2689 | sub foreach_drive { |
2690 | my ($conf, $func) = @_; | |
2691 | ||
74479ee9 FG |
2692 | foreach my $ds (valid_drive_names()) { |
2693 | next if !defined($conf->{$ds}); | |
1e3baf05 | 2694 | |
6b64503e | 2695 | my $drive = parse_drive($ds, $conf->{$ds}); |
1e3baf05 DM |
2696 | next if !$drive; |
2697 | ||
2698 | &$func($ds, $drive); | |
2699 | } | |
2700 | } | |
2701 | ||
d5769dc2 DM |
2702 | sub foreach_volid { |
2703 | my ($conf, $func) = @_; | |
be190583 | 2704 | |
d5769dc2 DM |
2705 | my $volhash = {}; |
2706 | ||
2707 | my $test_volid = sub { | |
2708 | my ($volid, $is_cdrom) = @_; | |
2709 | ||
2710 | return if !$volid; | |
be190583 | 2711 | |
d5769dc2 DM |
2712 | $volhash->{$volid} = $is_cdrom || 0; |
2713 | }; | |
2714 | ||
ed221350 | 2715 | foreach_drive($conf, sub { |
d5769dc2 DM |
2716 | my ($ds, $drive) = @_; |
2717 | &$test_volid($drive->{file}, drive_is_cdrom($drive)); | |
2718 | }); | |
2719 | ||
2720 | foreach my $snapname (keys %{$conf->{snapshots}}) { | |
2721 | my $snap = $conf->{snapshots}->{$snapname}; | |
2722 | &$test_volid($snap->{vmstate}, 0); | |
ed221350 | 2723 | foreach_drive($snap, sub { |
d5769dc2 DM |
2724 | my ($ds, $drive) = @_; |
2725 | &$test_volid($drive->{file}, drive_is_cdrom($drive)); | |
2726 | }); | |
2727 | } | |
2728 | ||
2729 | foreach my $volid (keys %$volhash) { | |
be190583 | 2730 | &$func($volid, $volhash->{$volid}); |
d5769dc2 DM |
2731 | } |
2732 | } | |
2733 | ||
86b8228b DM |
2734 | sub vga_conf_has_spice { |
2735 | my ($vga) = @_; | |
2736 | ||
590e698c DM |
2737 | return 0 if !$vga || $vga !~ m/^qxl([234])?$/; |
2738 | ||
2739 | return $1 || 1; | |
86b8228b DM |
2740 | } |
2741 | ||
1e3baf05 | 2742 | sub config_to_command { |
67812f9c | 2743 | my ($storecfg, $vmid, $conf, $defaults, $forcemachine) = @_; |
1e3baf05 DM |
2744 | |
2745 | my $cmd = []; | |
8c559505 DM |
2746 | my $globalFlags = []; |
2747 | my $machineFlags = []; | |
2748 | my $rtcFlags = []; | |
519ed28c | 2749 | my $cpuFlags = []; |
5bdcf937 | 2750 | my $devices = []; |
b78ebef7 | 2751 | my $pciaddr = ''; |
5bdcf937 | 2752 | my $bridges = {}; |
1e3baf05 DM |
2753 | my $kvmver = kvm_user_version(); |
2754 | my $vernum = 0; # unknown | |
b42d3cf9 | 2755 | my $ostype = $conf->{ostype}; |
a3c52213 DM |
2756 | if ($kvmver =~ m/^(\d+)\.(\d+)$/) { |
2757 | $vernum = $1*1000000+$2*1000; | |
2758 | } elsif ($kvmver =~ m/^(\d+)\.(\d+)\.(\d+)$/) { | |
1e3baf05 DM |
2759 | $vernum = $1*1000000+$2*1000+$3; |
2760 | } | |
2761 | ||
a3c52213 | 2762 | die "detected old qemu-kvm binary ($kvmver)\n" if $vernum < 15000; |
1e3baf05 DM |
2763 | |
2764 | my $have_ovz = -f '/proc/vz/vestat'; | |
2765 | ||
db656e5f | 2766 | my $q35 = machine_type_is_q35($conf); |
4d3f29ed | 2767 | my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1'); |
23f73120 | 2768 | my $machine_type = $forcemachine || $conf->{machine}; |
249c4a6c AD |
2769 | my $use_old_bios_files = undef; |
2770 | ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type); | |
db656e5f | 2771 | |
f08e17c7 AD |
2772 | my $cpuunits = defined($conf->{cpuunits}) ? |
2773 | $conf->{cpuunits} : $defaults->{cpuunits}; | |
2774 | ||
2775 | push @$cmd, '/usr/bin/systemd-run'; | |
2776 | push @$cmd, '--scope'; | |
2777 | push @$cmd, '--slice', "qemu"; | |
2778 | push @$cmd, '--unit', $vmid; | |
19333c9b | 2779 | push @$cmd, '--description', "'Proxmox VE VM $vmid'"; |
ea002a8f DM |
2780 | # set KillMode=none, so that systemd don't kill those scopes |
2781 | # at shutdown (pve-manager service should stop the VMs instead) | |
2782 | push @$cmd, '-p', "KillMode=none"; | |
f08e17c7 | 2783 | push @$cmd, '-p', "CPUShares=$cpuunits"; |
58be00f1 | 2784 | if ($conf->{cpulimit}) { |
c6f773b8 DM |
2785 | my $cpulimit = int($conf->{cpulimit} * 100); |
2786 | push @$cmd, '-p', "CPUQuota=$cpulimit\%"; | |
58be00f1 | 2787 | } |
f08e17c7 | 2788 | |
1e3baf05 DM |
2789 | push @$cmd, '/usr/bin/kvm'; |
2790 | ||
2791 | push @$cmd, '-id', $vmid; | |
2792 | ||
2793 | my $use_virtio = 0; | |
2794 | ||
c971c4f2 AD |
2795 | my $qmpsocket = qmp_socket($vmid); |
2796 | push @$cmd, '-chardev', "socket,id=qmp,path=$qmpsocket,server,nowait"; | |
2797 | push @$cmd, '-mon', "chardev=qmp,mode=control"; | |
2798 | ||
1e3baf05 | 2799 | |
6b64503e | 2800 | push @$cmd, '-pidfile' , pidfile_name($vmid); |
19672434 | 2801 | |
1e3baf05 DM |
2802 | push @$cmd, '-daemonize'; |
2803 | ||
2796e7d5 DM |
2804 | if ($conf->{smbios1}) { |
2805 | push @$cmd, '-smbios', "type=1,$conf->{smbios1}"; | |
2806 | } | |
2807 | ||
3edb45e7 | 2808 | if ($conf->{bios} && $conf->{bios} eq 'ovmf') { |
a783c78e AD |
2809 | my $ovmfvar = "OVMF_VARS-pure-efi.fd"; |
2810 | my $ovmfvar_src = "/usr/share/kvm/$ovmfvar"; | |
2811 | my $ovmfvar_dst = "/tmp/$vmid-$ovmfvar"; | |
2bd70893 | 2812 | PVE::Tools::file_copy($ovmfvar_src, $ovmfvar_dst, 256*1024); |
a783c78e AD |
2813 | push @$cmd, '-drive', "if=pflash,format=raw,readonly,file=/usr/share/kvm/OVMF-pure-efi.fd"; |
2814 | push @$cmd, '-drive', "if=pflash,format=raw,file=$ovmfvar_dst"; | |
2815 | } | |
2816 | ||
db656e5f | 2817 | if ($q35) { |
b467f79a | 2818 | # the q35 chipset support native usb2, so we enable usb controller |
db656e5f | 2819 | # by default for this machine type |
f8e83f05 | 2820 | push @$devices, '-readconfig', '/usr/share/qemu-server/pve-q35.cfg'; |
db656e5f | 2821 | } else { |
f8e83f05 AD |
2822 | $pciaddr = print_pci_addr("piix3", $bridges); |
2823 | push @$devices, '-device', "piix3-usb-uhci,id=uhci$pciaddr.0x2"; | |
24f0d39a | 2824 | |
f8e83f05 | 2825 | my $use_usb2 = 0; |
db656e5f DM |
2826 | for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) { |
2827 | next if !$conf->{"usb$i"}; | |
a6b9aee4 DC |
2828 | my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) }; |
2829 | next if !$d || $d->{usb3}; # do not add usb2 controller if we have only usb3 devices | |
db656e5f DM |
2830 | $use_usb2 = 1; |
2831 | } | |
2832 | # include usb device config | |
2833 | push @$devices, '-readconfig', '/usr/share/qemu-server/pve-usb.cfg' if $use_usb2; | |
fcc573ab | 2834 | } |
19672434 | 2835 | |
da8b4189 DC |
2836 | # add usb3 controller if needed |
2837 | ||
2838 | my $use_usb3 = 0; | |
2839 | for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) { | |
2840 | next if !$conf->{"usb$i"}; | |
a6b9aee4 DC |
2841 | my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) }; |
2842 | next if !$d || !$d->{usb3}; | |
da8b4189 DC |
2843 | $use_usb3 = 1; |
2844 | } | |
2845 | ||
2846 | $pciaddr = print_pci_addr("xhci", $bridges); | |
2847 | push @$devices, '-device', "nec-usb-xhci,id=xhci$pciaddr" if $use_usb3; | |
2848 | ||
5acbfe9e | 2849 | my $vga = $conf->{vga}; |
2fa3151e | 2850 | |
590e698c DM |
2851 | my $qxlnum = vga_conf_has_spice($vga); |
2852 | $vga = 'qxl' if $qxlnum; | |
2fa3151e | 2853 | |
5acbfe9e | 2854 | if (!$vga) { |
264e519f DM |
2855 | if ($conf->{ostype} && ($conf->{ostype} eq 'win8' || |
2856 | $conf->{ostype} eq 'win7' || | |
5acbfe9e DM |
2857 | $conf->{ostype} eq 'w2k8')) { |
2858 | $vga = 'std'; | |
2859 | } else { | |
2860 | $vga = 'cirrus'; | |
2861 | } | |
2862 | } | |
2863 | ||
1e3baf05 | 2864 | # enable absolute mouse coordinates (needed by vnc) |
5acbfe9e DM |
2865 | my $tablet; |
2866 | if (defined($conf->{tablet})) { | |
2867 | $tablet = $conf->{tablet}; | |
2868 | } else { | |
2869 | $tablet = $defaults->{tablet}; | |
590e698c | 2870 | $tablet = 0 if $qxlnum; # disable for spice because it is not needed |
ef5e2be2 | 2871 | $tablet = 0 if $vga =~ m/^serial\d+$/; # disable if we use serial terminal (no vga card) |
5acbfe9e DM |
2872 | } |
2873 | ||
db656e5f | 2874 | push @$devices, '-device', print_tabletdevice_full($conf) if $tablet; |
b467f79a | 2875 | |
16a91d65 | 2876 | my $kvm_off = 0; |
1e3baf05 | 2877 | # host pci devices |
040b06b7 | 2878 | for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) { |
2e3b7e2a AD |
2879 | my $d = parse_hostpci($conf->{"hostpci$i"}); |
2880 | next if !$d; | |
2881 | ||
2882 | my $pcie = $d->{pcie}; | |
2883 | if($pcie){ | |
2884 | die "q35 machine model is not enabled" if !$q35; | |
2885 | $pciaddr = print_pcie_addr("hostpci$i"); | |
2886 | }else{ | |
2887 | $pciaddr = print_pci_addr("hostpci$i", $bridges); | |
2888 | } | |
2889 | ||
1f4f447b WB |
2890 | my $rombar = defined($d->{rombar}) && !$d->{rombar} ? ',rombar=0' : ''; |
2891 | my $xvga = ''; | |
2892 | if ($d->{'x-vga'}) { | |
2893 | $xvga = ',x-vga=on'; | |
16a91d65 | 2894 | $kvm_off = 1; |
137483c0 | 2895 | $vga = 'none'; |
b42d3cf9 | 2896 | if ($ostype eq 'win7' || $ostype eq 'win8' || $ostype eq 'w2k8') { |
bd92244c AD |
2897 | push @$cpuFlags , 'hv_vendor_id=proxmox'; |
2898 | } | |
230a4382 AD |
2899 | if ($conf->{bios} && $conf->{bios} eq 'ovmf') { |
2900 | $xvga = ""; | |
2901 | } | |
137483c0 | 2902 | } |
4543ecf0 AD |
2903 | my $pcidevices = $d->{pciid}; |
2904 | my $multifunction = 1 if @$pcidevices > 1; | |
2e3b7e2a | 2905 | |
4543ecf0 AD |
2906 | my $j=0; |
2907 | foreach my $pcidevice (@$pcidevices) { | |
2e3b7e2a | 2908 | |
4543ecf0 AD |
2909 | my $id = "hostpci$i"; |
2910 | $id .= ".$j" if $multifunction; | |
2911 | my $addr = $pciaddr; | |
2912 | $addr .= ".$j" if $multifunction; | |
6ea8cd3b | 2913 | my $devicestr = "vfio-pci,host=$pcidevice->{id}.$pcidevice->{function},id=$id$addr"; |
4543ecf0 AD |
2914 | |
2915 | if($j == 0){ | |
2916 | $devicestr .= "$rombar$xvga"; | |
2917 | $devicestr .= ",multifunction=on" if $multifunction; | |
2918 | } | |
2919 | ||
2920 | push @$devices, '-device', $devicestr; | |
2921 | $j++; | |
2922 | } | |
1e3baf05 DM |
2923 | } |
2924 | ||
2925 | # usb devices | |
2926 | for (my $i = 0; $i < $MAX_USB_DEVICES; $i++) { | |
a6b9aee4 DC |
2927 | next if !$conf->{"usb$i"}; |
2928 | my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format},$conf->{"usb$i"}) }; | |
1e3baf05 | 2929 | next if !$d; |
da8b4189 DC |
2930 | |
2931 | # if it is a usb3 device, attach it to the xhci controller, else omit the bus option | |
2932 | my $usbbus = ''; | |
a6b9aee4 | 2933 | if (defined($d->{usb3}) && $d->{usb3}) { |
da8b4189 DC |
2934 | $usbbus = ',bus=xhci.0'; |
2935 | } | |
2936 | ||
a6b9aee4 DC |
2937 | if (defined($d->{host})) { |
2938 | $d = parse_usb_device($d->{host}); | |
2939 | if (defined($d->{vendorid}) && defined($d->{productid})) { | |
2940 | push @$devices, '-device', "usb-host$usbbus,vendorid=0x$d->{vendorid},productid=0x$d->{productid}"; | |
2941 | } elsif (defined($d->{hostbus}) && defined($d->{hostport})) { | |
2942 | push @$devices, '-device', "usb-host$usbbus,hostbus=$d->{hostbus},hostport=$d->{hostport}"; | |
2943 | } elsif (defined($d->{spice}) && $d->{spice}) { | |
2944 | # usb redir support for spice, currently no usb3 | |
2945 | push @$devices, '-chardev', "spicevmc,id=usbredirchardev$i,name=usbredir"; | |
2946 | push @$devices, '-device', "usb-redir,chardev=usbredirchardev$i,id=usbredirdev$i,bus=ehci.0"; | |
2947 | } | |
1e3baf05 DM |
2948 | } |
2949 | } | |
2950 | ||
1e3baf05 | 2951 | # serial devices |
bae179aa | 2952 | for (my $i = 0; $i < $MAX_SERIAL_PORTS; $i++) { |
34978be3 | 2953 | if (my $path = $conf->{"serial$i"}) { |
9f9d2fb2 DM |
2954 | if ($path eq 'socket') { |
2955 | my $socket = "/var/run/qemu-server/${vmid}.serial$i"; | |
2956 | push @$devices, '-chardev', "socket,id=serial$i,path=$socket,server,nowait"; | |
2957 | push @$devices, '-device', "isa-serial,chardev=serial$i"; | |
2958 | } else { | |
2959 | die "no such serial device\n" if ! -c $path; | |
2960 | push @$devices, '-chardev', "tty,id=serial$i,path=$path"; | |
2961 | push @$devices, '-device', "isa-serial,chardev=serial$i"; | |
2962 | } | |
34978be3 | 2963 | } |
1e3baf05 DM |
2964 | } |
2965 | ||
2966 | # parallel devices | |
1989a89c | 2967 | for (my $i = 0; $i < $MAX_PARALLEL_PORTS; $i++) { |
34978be3 | 2968 | if (my $path = $conf->{"parallel$i"}) { |
19672434 | 2969 | die "no such parallel device\n" if ! -c $path; |
32e69805 | 2970 | my $devtype = $path =~ m!^/dev/usb/lp! ? 'tty' : 'parport'; |
4c5dbaf6 | 2971 | push @$devices, '-chardev', "$devtype,id=parallel$i,path=$path"; |
5bdcf937 | 2972 | push @$devices, '-device', "isa-parallel,chardev=parallel$i"; |
34978be3 | 2973 | } |
1e3baf05 DM |
2974 | } |
2975 | ||
2976 | my $vmname = $conf->{name} || "vm$vmid"; | |
2977 | ||
2978 | push @$cmd, '-name', $vmname; | |
19672434 | 2979 | |
1e3baf05 DM |
2980 | my $sockets = 1; |
2981 | $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused | |
2982 | $sockets = $conf->{sockets} if $conf->{sockets}; | |
2983 | ||
2984 | my $cores = $conf->{cores} || 1; | |
3bd18e48 | 2985 | |
de9d1e55 | 2986 | my $maxcpus = $sockets * $cores; |
76267728 | 2987 | |
de9d1e55 | 2988 | my $vcpus = $conf->{vcpus} ? $conf->{vcpus} : $maxcpus; |
76267728 | 2989 | |
de9d1e55 AD |
2990 | my $allowed_vcpus = $cpuinfo->{cpus}; |
2991 | ||
6965d5d1 | 2992 | die "MAX $allowed_vcpus vcpus allowed per VM on this node\n" |
de9d1e55 AD |
2993 | if ($allowed_vcpus < $maxcpus); |
2994 | ||
2995 | push @$cmd, '-smp', "$vcpus,sockets=$sockets,cores=$cores,maxcpus=$maxcpus"; | |
1e3baf05 | 2996 | |
1e3baf05 DM |
2997 | push @$cmd, '-nodefaults'; |
2998 | ||
32baffb4 | 2999 | my $bootorder = $conf->{boot} || $confdesc->{boot}->{default}; |
3b408e82 | 3000 | |
0888fdce DM |
3001 | my $bootindex_hash = {}; |
3002 | my $i = 1; | |
3003 | foreach my $o (split(//, $bootorder)) { | |
3004 | $bootindex_hash->{$o} = $i*100; | |
3005 | $i++; | |
afdb31d5 | 3006 | } |
3b408e82 | 3007 | |
cf71f776 | 3008 | push @$cmd, '-boot', "menu=on,strict=on,reboot-timeout=1000"; |
1e3baf05 | 3009 | |
6b64503e | 3010 | push @$cmd, '-no-acpi' if defined($conf->{acpi}) && $conf->{acpi} == 0; |
1e3baf05 | 3011 | |
6b64503e | 3012 | push @$cmd, '-no-reboot' if defined($conf->{reboot}) && $conf->{reboot} == 0; |
1e3baf05 | 3013 | |
ef5e2be2 | 3014 | push @$cmd, '-vga', $vga if $vga && $vga !~ m/^serial\d+$/; # for kvm 77 and later |
1e3baf05 | 3015 | |
b7be4ba9 AD |
3016 | if ($vga && $vga !~ m/^serial\d+$/ && $vga ne 'none'){ |
3017 | my $socket = vnc_socket($vmid); | |
3018 | push @$cmd, '-vnc', "unix:$socket,x509,password"; | |
3019 | } else { | |
3020 | push @$cmd, '-nographic'; | |
3021 | } | |
3022 | ||
1e3baf05 | 3023 | # time drift fix |
6b64503e | 3024 | my $tdf = defined($conf->{tdf}) ? $conf->{tdf} : $defaults->{tdf}; |
1e3baf05 | 3025 | |
6b64503e | 3026 | my $nokvm = defined($conf->{kvm}) && $conf->{kvm} == 0 ? 1 : 0; |
8c559505 | 3027 | my $useLocaltime = $conf->{localtime}; |
1e3baf05 | 3028 | |
b42d3cf9 | 3029 | if ($ostype) { |
6b9d84cf | 3030 | # other, wxp, w2k, w2k3, w2k8, wvista, win7, win8, l24, l26, solaris |
1e3baf05 | 3031 | |
b42d3cf9 | 3032 | if ($ostype =~ m/^w/) { # windows |
8c559505 | 3033 | $useLocaltime = 1 if !defined($conf->{localtime}); |
1e3baf05 | 3034 | |
8c559505 | 3035 | # use time drift fix when acpi is enabled |
6b64503e | 3036 | if (!(defined($conf->{acpi}) && $conf->{acpi} == 0)) { |
8c559505 | 3037 | $tdf = 1 if !defined($conf->{tdf}); |
1e3baf05 DM |
3038 | } |
3039 | } | |
3040 | ||
b42d3cf9 DM |
3041 | if ($ostype eq 'win7' || $ostype eq 'win8' || $ostype eq 'w2k8' || |
3042 | $ostype eq 'wvista') { | |
8c559505 | 3043 | push @$globalFlags, 'kvm-pit.lost_tick_policy=discard'; |
b7e0c8bf | 3044 | push @$cmd, '-no-hpet'; |
8f3f959d | 3045 | if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) { |
bd92244c AD |
3046 | push @$cpuFlags , 'hv_spinlocks=0x1fff' if !$nokvm; |
3047 | push @$cpuFlags , 'hv_vapic' if !$nokvm; | |
3048 | push @$cpuFlags , 'hv_time' if !$nokvm; | |
8a054ffd | 3049 | |
a1b7d579 | 3050 | } else { |
bd92244c | 3051 | push @$cpuFlags , 'hv_spinlocks=0xffff' if !$nokvm; |
8f3f959d | 3052 | } |
462e8d19 AD |
3053 | } |
3054 | ||
b42d3cf9 | 3055 | if ($ostype eq 'win7' || $ostype eq 'win8') { |
bd92244c | 3056 | push @$cpuFlags , 'hv_relaxed' if !$nokvm; |
b7e0c8bf | 3057 | } |
1e3baf05 DM |
3058 | } |
3059 | ||
8c559505 DM |
3060 | push @$rtcFlags, 'driftfix=slew' if $tdf; |
3061 | ||
7f0b5beb | 3062 | if ($nokvm) { |
8c559505 | 3063 | push @$machineFlags, 'accel=tcg'; |
7f0b5beb DM |
3064 | } else { |
3065 | die "No accelerator found!\n" if !$cpuinfo->{hvm}; | |
3066 | } | |
1e3baf05 | 3067 | |
952958bc DM |
3068 | if ($machine_type) { |
3069 | push @$machineFlags, "type=${machine_type}"; | |
3bafc510 DM |
3070 | } |
3071 | ||
8c559505 DM |
3072 | if ($conf->{startdate}) { |
3073 | push @$rtcFlags, "base=$conf->{startdate}"; | |
3074 | } elsif ($useLocaltime) { | |
3075 | push @$rtcFlags, 'base=localtime'; | |
3076 | } | |
1e3baf05 | 3077 | |
519ed28c | 3078 | my $cpu = $nokvm ? "qemu64" : "kvm64"; |
16a91d65 | 3079 | if (my $cputype = $conf->{cpu}) { |
ff6ffe20 | 3080 | my $cpuconf = PVE::JSONSchema::parse_property_string($cpu_fmt, $cputype) |
16a91d65 WB |
3081 | or die "Cannot parse cpu description: $cputype\n"; |
3082 | $cpu = $cpuconf->{cputype}; | |
3083 | $kvm_off = 1 if $cpuconf->{hidden}; | |
3084 | } | |
519ed28c | 3085 | |
4dc339e7 AD |
3086 | push @$cpuFlags , '+lahf_lm' if $cpu eq 'kvm64'; |
3087 | ||
d853f40a DM |
3088 | push @$cpuFlags , '-x2apic' |
3089 | if $conf->{ostype} && $conf->{ostype} eq 'solaris'; | |
519ed28c | 3090 | |
2e1a5389 AD |
3091 | push @$cpuFlags, '+sep' if $cpu eq 'kvm64' || $cpu eq 'kvm32'; |
3092 | ||
0dc48c3d AD |
3093 | push @$cpuFlags, '-rdtscp' if $cpu =~ m/^Opteron/; |
3094 | ||
117a0414 AD |
3095 | if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) { |
3096 | ||
3097 | push @$cpuFlags , '+kvm_pv_unhalt' if !$nokvm; | |
0da5a08c | 3098 | push @$cpuFlags , '+kvm_pv_eoi' if !$nokvm; |
117a0414 AD |
3099 | } |
3100 | ||
f1f7ea88 | 3101 | push @$cpuFlags, 'enforce' if $cpu ne 'host' && !$nokvm; |
dac7c619 | 3102 | |
16a91d65 WB |
3103 | push @$cpuFlags, 'kvm=off' if $kvm_off; |
3104 | ||
8930da74 DM |
3105 | my $cpu_vendor = $cpu_vendor_list->{$cpu} || |
3106 | die "internal error"; # should not happen | |
3107 | ||
3108 | push @$cpuFlags, "vendor=${cpu_vendor}" | |
3109 | if $cpu_vendor ne 'default'; | |
3110 | ||
be190583 | 3111 | $cpu .= "," . join(',', @$cpuFlags) if scalar(@$cpuFlags); |
519ed28c | 3112 | |
dac7c619 | 3113 | push @$cmd, '-cpu', $cpu; |
519ed28c | 3114 | |
4d3f29ed AD |
3115 | my $memory = $conf->{memory} || $defaults->{memory}; |
3116 | my $static_memory = 0; | |
3117 | my $dimm_memory = 0; | |
3118 | ||
3119 | if ($hotplug_features->{memory}) { | |
52261945 | 3120 | die "NUMA need to be enabled for memory hotplug\n" if !$conf->{numa}; |
996635e5 | 3121 | die "Total memory is bigger than ${MAX_MEM}MB\n" if $memory > $MAX_MEM; |
4d3f29ed | 3122 | $static_memory = $STATICMEM; |
996635e5 | 3123 | die "minimum memory must be ${static_memory}MB\n" if($memory < $static_memory); |
4d3f29ed | 3124 | $dimm_memory = $memory - $static_memory; |
996635e5 | 3125 | push @$cmd, '-m', "size=${static_memory},slots=255,maxmem=${MAX_MEM}M"; |
4d3f29ed AD |
3126 | |
3127 | } else { | |
3128 | ||
3129 | $static_memory = $memory; | |
3130 | push @$cmd, '-m', $static_memory; | |
3131 | } | |
8a010eae | 3132 | |
67fb9de6 | 3133 | if ($conf->{numa}) { |
8a010eae | 3134 | |
2ed5d572 AD |
3135 | my $numa_totalmemory = undef; |
3136 | for (my $i = 0; $i < $MAX_NUMA; $i++) { | |
3137 | next if !$conf->{"numa$i"}; | |
3138 | my $numa = parse_numa($conf->{"numa$i"}); | |
3139 | next if !$numa; | |
67fb9de6 | 3140 | # memory |
52261945 | 3141 | die "missing NUMA node$i memory value\n" if !$numa->{memory}; |
2ed5d572 AD |
3142 | my $numa_memory = $numa->{memory}; |
3143 | $numa_totalmemory += $numa_memory; | |
996635e5 | 3144 | my $numa_object = "memory-backend-ram,id=ram-node$i,size=${numa_memory}M"; |
2ed5d572 | 3145 | |
67fb9de6 | 3146 | # cpus |
ffc0d8c7 | 3147 | my $cpulists = $numa->{cpus}; |
52261945 | 3148 | die "missing NUMA node$i cpus\n" if !defined($cpulists); |
ffc0d8c7 WB |
3149 | my $cpus = join(',', map { |
3150 | my ($start, $end) = @$_; | |
3151 | defined($end) ? "$start-$end" : $start | |
3152 | } @$cpulists); | |
8a010eae | 3153 | |
67fb9de6 | 3154 | # hostnodes |
ffc0d8c7 WB |
3155 | my $hostnodelists = $numa->{hostnodes}; |
3156 | if (defined($hostnodelists)) { | |
3157 | my $hostnodes; | |
3158 | foreach my $hostnoderange (@$hostnodelists) { | |
3159 | my ($start, $end) = @$hostnoderange; | |
3160 | $hostnodes .= ',' if $hostnodes; | |
3161 | $hostnodes .= $start; | |
3162 | $hostnodes .= "-$end" if defined($end); | |
3163 | $end //= $start; | |
3164 | for (my $i = $start; $i <= $end; ++$i ) { | |
52261945 | 3165 | die "host NUMA node$i don't exist\n" if ! -d "/sys/devices/system/node/node$i/"; |
ffc0d8c7 | 3166 | } |
2ed5d572 | 3167 | } |
8a010eae | 3168 | |
67fb9de6 | 3169 | # policy |
2ed5d572 | 3170 | my $policy = $numa->{policy}; |
67fb9de6 DM |
3171 | die "you need to define a policy for hostnode $hostnodes\n" if !$policy; |
3172 | $numa_object .= ",host-nodes=$hostnodes,policy=$policy"; | |
2ed5d572 AD |
3173 | } |
3174 | ||
3175 | push @$cmd, '-object', $numa_object; | |
8a010eae AD |
3176 | push @$cmd, '-numa', "node,nodeid=$i,cpus=$cpus,memdev=ram-node$i"; |
3177 | } | |
67fb9de6 | 3178 | |
4d3f29ed AD |
3179 | die "total memory for NUMA nodes must be equal to vm static memory\n" |
3180 | if $numa_totalmemory && $numa_totalmemory != $static_memory; | |
2ed5d572 AD |
3181 | |
3182 | #if no custom tology, we split memory and cores across numa nodes | |
3183 | if(!$numa_totalmemory) { | |
3184 | ||
4d3f29ed | 3185 | my $numa_memory = ($static_memory / $sockets) . "M"; |
2ed5d572 AD |
3186 | |
3187 | for (my $i = 0; $i < $sockets; $i++) { | |
3188 | ||
3189 | my $cpustart = ($cores * $i); | |
3190 | my $cpuend = ($cpustart + $cores - 1) if $cores && $cores > 1; | |
3191 | my $cpus = $cpustart; | |
3192 | $cpus .= "-$cpuend" if $cpuend; | |
3193 | ||
3194 | push @$cmd, '-object', "memory-backend-ram,size=$numa_memory,id=ram-node$i"; | |
3195 | push @$cmd, '-numa', "node,nodeid=$i,cpus=$cpus,memdev=ram-node$i"; | |
3196 | } | |
3197 | } | |
8a010eae AD |
3198 | } |
3199 | ||
4d3f29ed | 3200 | if ($hotplug_features->{memory}) { |
e059fb4d AD |
3201 | foreach_dimm($conf, $vmid, $memory, $sockets, sub { |
3202 | my ($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory) = @_; | |
996635e5 | 3203 | push @$cmd, "-object" , "memory-backend-ram,id=mem-$name,size=${dimm_size}M"; |
e059fb4d AD |
3204 | push @$cmd, "-device", "pc-dimm,id=$name,memdev=mem-$name,node=$numanode"; |
3205 | ||
3206 | #if dimm_memory is not aligned to dimm map | |
3207 | if($current_size > $memory) { | |
3208 | $conf->{memory} = $current_size; | |
ffda963f | 3209 | PVE::QemuConfig->write_config($vmid, $conf); |
e059fb4d AD |
3210 | } |
3211 | }); | |
4d3f29ed AD |
3212 | } |
3213 | ||
1e3baf05 DM |
3214 | push @$cmd, '-S' if $conf->{freeze}; |
3215 | ||
3216 | # set keyboard layout | |
3217 | my $kb = $conf->{keyboard} || $defaults->{keyboard}; | |
3218 | push @$cmd, '-k', $kb if $kb; | |
3219 | ||
3220 | # enable sound | |
3221 | #my $soundhw = $conf->{soundhw} || $defaults->{soundhw}; | |
3222 | #push @$cmd, '-soundhw', 'es1370'; | |
3223 | #push @$cmd, '-soundhw', $soundhw if $soundhw; | |
ab6a046f | 3224 | |
bc84dcca | 3225 | if($conf->{agent}) { |
7a6c2150 | 3226 | my $qgasocket = qmp_socket($vmid, 1); |
ab6a046f AD |
3227 | my $pciaddr = print_pci_addr("qga0", $bridges); |
3228 | push @$devices, '-chardev', "socket,path=$qgasocket,server,nowait,id=qga0"; | |
3229 | push @$devices, '-device', "virtio-serial,id=qga0$pciaddr"; | |
3230 | push @$devices, '-device', 'virtserialport,chardev=qga0,name=org.qemu.guest_agent.0'; | |
3231 | } | |
3232 | ||
1d794448 | 3233 | my $spice_port; |
2fa3151e | 3234 | |
590e698c DM |
3235 | if ($qxlnum) { |
3236 | if ($qxlnum > 1) { | |
3237 | if ($conf->{ostype} && $conf->{ostype} =~ m/^w/){ | |
3238 | for(my $i = 1; $i < $qxlnum; $i++){ | |
3239 | my $pciaddr = print_pci_addr("vga$i", $bridges); | |
3240 | push @$cmd, '-device', "qxl,id=vga$i,ram_size=67108864,vram_size=33554432$pciaddr"; | |
3241 | } | |
3242 | } else { | |
3243 | # assume other OS works like Linux | |
3244 | push @$cmd, '-global', 'qxl-vga.ram_size=134217728'; | |
3245 | push @$cmd, '-global', 'qxl-vga.vram_size=67108864'; | |
2fa3151e AD |
3246 | } |
3247 | } | |
3248 | ||
1011b570 | 3249 | my $pciaddr = print_pci_addr("spice", $bridges); |
95a4b4a9 | 3250 | |
af0eba7e WB |
3251 | my $nodename = PVE::INotify::nodename(); |
3252 | my $pfamily = PVE::Tools::get_host_address_family($nodename); | |
3253 | $spice_port = PVE::Tools::next_spice_port($pfamily); | |
943340a6 | 3254 | |
fd1f36ac | 3255 | push @$devices, '-spice', "tls-port=${spice_port},addr=localhost,tls-ciphers=DES-CBC3-SHA,seamless-migration=on"; |
1011b570 | 3256 | |
d2da6d9b AD |
3257 | push @$devices, '-device', "virtio-serial,id=spice$pciaddr"; |
3258 | push @$devices, '-chardev', "spicevmc,id=vdagent,name=vdagent"; | |
3259 | push @$devices, '-device', "virtserialport,chardev=vdagent,name=com.redhat.spice.0"; | |
1011b570 DM |
3260 | } |
3261 | ||
8d9ae0d2 DM |
3262 | # enable balloon by default, unless explicitly disabled |
3263 | if (!defined($conf->{balloon}) || $conf->{balloon}) { | |
3264 | $pciaddr = print_pci_addr("balloon0", $bridges); | |
3265 | push @$devices, '-device', "virtio-balloon-pci,id=balloon0$pciaddr"; | |
3266 | } | |
1e3baf05 | 3267 | |
0ea9541d DM |
3268 | if ($conf->{watchdog}) { |
3269 | my $wdopts = parse_watchdog($conf->{watchdog}); | |
5bdcf937 | 3270 | $pciaddr = print_pci_addr("watchdog", $bridges); |
0a40e8ea | 3271 | my $watchdog = $wdopts->{model} || 'i6300esb'; |
5bdcf937 AD |
3272 | push @$devices, '-device', "$watchdog$pciaddr"; |
3273 | push @$devices, '-watchdog-action', $wdopts->{action} if $wdopts->{action}; | |
0ea9541d DM |
3274 | } |
3275 | ||
1e3baf05 | 3276 | my $vollist = []; |
941e0c42 | 3277 | my $scsicontroller = {}; |
26ee04b6 | 3278 | my $ahcicontroller = {}; |
cdd20088 | 3279 | my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : $defaults->{scsihw}; |
1e3baf05 | 3280 | |
5881b913 DM |
3281 | # Add iscsi initiator name if available |
3282 | if (my $initiator = get_initiator_name()) { | |
3283 | push @$devices, '-iscsi', "initiator-name=$initiator"; | |
3284 | } | |
3285 | ||
1e3baf05 DM |
3286 | foreach_drive($conf, sub { |
3287 | my ($ds, $drive) = @_; | |
3288 | ||
ff1a2432 | 3289 | if (PVE::Storage::parse_volume_id($drive->{file}, 1)) { |
1e3baf05 | 3290 | push @$vollist, $drive->{file}; |
ff1a2432 | 3291 | } |
afdb31d5 | 3292 | |
1e3baf05 | 3293 | $use_virtio = 1 if $ds =~ m/^virtio/; |
3b408e82 DM |
3294 | |
3295 | if (drive_is_cdrom ($drive)) { | |
3296 | if ($bootindex_hash->{d}) { | |
3297 | $drive->{bootindex} = $bootindex_hash->{d}; | |
3298 | $bootindex_hash->{d} += 1; | |
3299 | } | |
3300 | } else { | |
3301 | if ($bootindex_hash->{c}) { | |
3302 | $drive->{bootindex} = $bootindex_hash->{c} if $conf->{bootdisk} && ($conf->{bootdisk} eq $ds); | |
3303 | $bootindex_hash->{c} += 1; | |
3304 | } | |
3305 | } | |
3306 | ||
51f492cd AD |
3307 | if($drive->{interface} eq 'virtio'){ |
3308 | push @$cmd, '-object', "iothread,id=iothread-$ds" if $drive->{iothread}; | |
3309 | } | |
3310 | ||
941e0c42 | 3311 | if ($drive->{interface} eq 'scsi') { |
cdd20088 | 3312 | |
ee034f5c | 3313 | my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $drive); |
6731a4cf | 3314 | |
6731a4cf | 3315 | $pciaddr = print_pci_addr("$controller_prefix$controller", $bridges); |
a1b7d579 | 3316 | my $scsihw_type = $scsihw =~ m/^virtio-scsi-single/ ? "virtio-scsi-pci" : $scsihw; |
fc8b40fd AD |
3317 | |
3318 | my $iothread = ''; | |
3319 | if($conf->{scsihw} && $conf->{scsihw} eq "virtio-scsi-single" && $drive->{iothread}){ | |
3320 | $iothread .= ",iothread=iothread-$controller_prefix$controller"; | |
3321 | push @$cmd, '-object', "iothread,id=iothread-$controller_prefix$controller"; | |
e7a5104d DC |
3322 | } elsif ($drive->{iothread}) { |
3323 | warn "iothread is only valid with virtio disk or virtio-scsi-single controller, ignoring\n"; | |
fc8b40fd AD |
3324 | } |
3325 | ||
6e11f143 AD |
3326 | my $queues = ''; |
3327 | if($conf->{scsihw} && $conf->{scsihw} eq "virtio-scsi-single" && $drive->{queues}){ | |
3328 | $queues = ",num_queues=$drive->{queues}"; | |
3329 | } | |
3330 | ||
3331 | push @$devices, '-device', "$scsihw_type,id=$controller_prefix$controller$pciaddr$iothread$queues" if !$scsicontroller->{$controller}; | |
cdd20088 | 3332 | $scsicontroller->{$controller}=1; |
941e0c42 | 3333 | } |
3b408e82 | 3334 | |
26ee04b6 DA |
3335 | if ($drive->{interface} eq 'sata') { |
3336 | my $controller = int($drive->{index} / $MAX_SATA_DISKS); | |
5bdcf937 AD |
3337 | $pciaddr = print_pci_addr("ahci$controller", $bridges); |
3338 | push @$devices, '-device', "ahci,id=ahci$controller,multifunction=on$pciaddr" if !$ahcicontroller->{$controller}; | |
26ee04b6 DA |
3339 | $ahcicontroller->{$controller}=1; |
3340 | } | |
46f58b5f | 3341 | |
15b21acc MR |
3342 | my $drive_cmd = print_drive_full($storecfg, $vmid, $drive); |
3343 | push @$devices, '-drive',$drive_cmd; | |
46f58b5f | 3344 | push @$devices, '-device', print_drivedevice_full($storecfg, $conf, $vmid, $drive, $bridges); |
1e3baf05 DM |
3345 | }); |
3346 | ||
cc4d6182 | 3347 | for (my $i = 0; $i < $MAX_NETS; $i++) { |
5f0c4c32 | 3348 | next if !$conf->{"net$i"}; |
cc4d6182 DA |
3349 | my $d = parse_net($conf->{"net$i"}); |
3350 | next if !$d; | |
1e3baf05 | 3351 | |
cc4d6182 | 3352 | $use_virtio = 1 if $d->{model} eq 'virtio'; |
1e3baf05 | 3353 | |
cc4d6182 DA |
3354 | if ($bootindex_hash->{n}) { |
3355 | $d->{bootindex} = $bootindex_hash->{n}; | |
3356 | $bootindex_hash->{n} += 1; | |
3357 | } | |
1e3baf05 | 3358 | |
cc4d6182 | 3359 | my $netdevfull = print_netdev_full($vmid,$conf,$d,"net$i"); |
5bdcf937 AD |
3360 | push @$devices, '-netdev', $netdevfull; |
3361 | ||
ba9e1000 | 3362 | my $netdevicefull = print_netdevice_full($vmid, $conf, $d, "net$i", $bridges, $use_old_bios_files); |
5bdcf937 AD |
3363 | push @$devices, '-device', $netdevicefull; |
3364 | } | |
1e3baf05 | 3365 | |
db656e5f DM |
3366 | if (!$q35) { |
3367 | # add pci bridges | |
fc79e813 AD |
3368 | if (qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 3)) { |
3369 | $bridges->{1} = 1; | |
3370 | $bridges->{2} = 1; | |
3371 | } | |
3372 | ||
6731a4cf AD |
3373 | $bridges->{3} = 1 if $scsihw =~ m/^virtio-scsi-single/; |
3374 | ||
f8e83f05 AD |
3375 | while (my ($k, $v) = each %$bridges) { |
3376 | $pciaddr = print_pci_addr("pci.$k"); | |
3377 | unshift @$devices, '-device', "pci-bridge,id=pci.$k,chassis_nr=$k$pciaddr" if $k > 0; | |
3378 | } | |
19672434 DM |
3379 | } |
3380 | ||
1e3baf05 DM |
3381 | # add custom args |
3382 | if ($conf->{args}) { | |
3ada46c9 | 3383 | my $aa = PVE::Tools::split_args($conf->{args}); |
1e3baf05 DM |
3384 | push @$cmd, @$aa; |
3385 | } | |
3386 | ||
5bdcf937 | 3387 | push @$cmd, @$devices; |
be190583 | 3388 | push @$cmd, '-rtc', join(',', @$rtcFlags) |
8c559505 | 3389 | if scalar(@$rtcFlags); |
be190583 | 3390 | push @$cmd, '-machine', join(',', @$machineFlags) |
8c559505 DM |
3391 | if scalar(@$machineFlags); |
3392 | push @$cmd, '-global', join(',', @$globalFlags) | |
3393 | if scalar(@$globalFlags); | |
3394 | ||
1d794448 | 3395 | return wantarray ? ($cmd, $vollist, $spice_port) : $cmd; |
1e3baf05 | 3396 | } |
19672434 | 3397 | |
1e3baf05 DM |
3398 | sub vnc_socket { |
3399 | my ($vmid) = @_; | |
3400 | return "${var_run_tmpdir}/$vmid.vnc"; | |
3401 | } | |
3402 | ||
943340a6 | 3403 | sub spice_port { |
1011b570 | 3404 | my ($vmid) = @_; |
943340a6 | 3405 | |
1d794448 | 3406 | my $res = vm_mon_cmd($vmid, 'query-spice'); |
943340a6 DM |
3407 | |
3408 | return $res->{'tls-port'} || $res->{'port'} || die "no spice port\n"; | |
1011b570 DM |
3409 | } |
3410 | ||
c971c4f2 | 3411 | sub qmp_socket { |
693d12a2 AD |
3412 | my ($vmid, $qga) = @_; |
3413 | my $sockettype = $qga ? 'qga' : 'qmp'; | |
3414 | return "${var_run_tmpdir}/$vmid.$sockettype"; | |
c971c4f2 AD |
3415 | } |
3416 | ||
1e3baf05 DM |
3417 | sub pidfile_name { |
3418 | my ($vmid) = @_; | |
3419 | return "${var_run_tmpdir}/$vmid.pid"; | |
3420 | } | |
3421 | ||
86fdcfb2 DA |
3422 | sub vm_devices_list { |
3423 | my ($vmid) = @_; | |
3424 | ||
ceea9078 | 3425 | my $res = vm_mon_cmd($vmid, 'query-pci'); |
ceea9078 DM |
3426 | my $devices = {}; |
3427 | foreach my $pcibus (@$res) { | |
3428 | foreach my $device (@{$pcibus->{devices}}) { | |
6e62a21f | 3429 | next if !$device->{'qdev_id'}; |
200644a7 | 3430 | if ($device->{'pci_bridge'}) { |
200644a7 AD |
3431 | $devices->{$device->{'qdev_id'}} = 1; |
3432 | foreach my $bridge_device (@{$device->{'pci_bridge'}->{devices}}) { | |
3433 | next if !$bridge_device->{'qdev_id'}; | |
3434 | $devices->{$bridge_device->{'qdev_id'}} = 1; | |
3435 | $devices->{$device->{'qdev_id'}}++; | |
3436 | } | |
3437 | } else { | |
200644a7 AD |
3438 | $devices->{$device->{'qdev_id'}} = 1; |
3439 | } | |
f78cc802 AD |
3440 | } |
3441 | } | |
3442 | ||
3443 | my $resblock = vm_mon_cmd($vmid, 'query-block'); | |
3444 | foreach my $block (@$resblock) { | |
3445 | if($block->{device} =~ m/^drive-(\S+)/){ | |
3446 | $devices->{$1} = 1; | |
1dc4f496 DM |
3447 | } |
3448 | } | |
86fdcfb2 | 3449 | |
3d7389fe DM |
3450 | my $resmice = vm_mon_cmd($vmid, 'query-mice'); |
3451 | foreach my $mice (@$resmice) { | |
3452 | if ($mice->{name} eq 'QEMU HID Tablet') { | |
3453 | $devices->{tablet} = 1; | |
3454 | last; | |
3455 | } | |
3456 | } | |
3457 | ||
1dc4f496 | 3458 | return $devices; |
86fdcfb2 DA |
3459 | } |
3460 | ||
ec21aa11 | 3461 | sub vm_deviceplug { |
f19d1c47 | 3462 | my ($storecfg, $conf, $vmid, $deviceid, $device) = @_; |
ae57f6b3 | 3463 | |
db656e5f DM |
3464 | my $q35 = machine_type_is_q35($conf); |
3465 | ||
95d6343b DA |
3466 | my $devices_list = vm_devices_list($vmid); |
3467 | return 1 if defined($devices_list->{$deviceid}); | |
3468 | ||
fee46675 DM |
3469 | qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid); # add PCI bridge if we need it for the device |
3470 | ||
3d7389fe | 3471 | if ($deviceid eq 'tablet') { |
fee46675 | 3472 | |
3d7389fe | 3473 | qemu_deviceadd($vmid, print_tabletdevice_full($conf)); |
3d7389fe | 3474 | |
fee46675 | 3475 | } elsif ($deviceid =~ m/^(virtio)(\d+)$/) { |
40f28a9f | 3476 | |
22de899a AD |
3477 | qemu_iothread_add($vmid, $deviceid, $device); |
3478 | ||
fee46675 | 3479 | qemu_driveadd($storecfg, $vmid, $device); |
cdd20088 | 3480 | my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); |
fee46675 | 3481 | |
5e5dcb73 | 3482 | qemu_deviceadd($vmid, $devicefull); |
fee46675 DM |
3483 | eval { qemu_deviceaddverify($vmid, $deviceid); }; |
3484 | if (my $err = $@) { | |
63c2da2f DM |
3485 | eval { qemu_drivedel($vmid, $deviceid); }; |
3486 | warn $@ if $@; | |
fee46675 | 3487 | die $err; |
5e5dcb73 | 3488 | } |
cfc817c7 | 3489 | |
2733141c | 3490 | } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) { |
fee46675 | 3491 | |
fc8b40fd | 3492 | |
cdd20088 | 3493 | my $scsihw = defined($conf->{scsihw}) ? $conf->{scsihw} : "lsi"; |
cfc817c7 | 3494 | my $pciaddr = print_pci_addr($deviceid); |
a1b7d579 | 3495 | my $scsihw_type = $scsihw eq 'virtio-scsi-single' ? "virtio-scsi-pci" : $scsihw; |
2733141c AD |
3496 | |
3497 | my $devicefull = "$scsihw_type,id=$deviceid$pciaddr"; | |
fee46675 | 3498 | |
fc8b40fd AD |
3499 | if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{iothread}) { |
3500 | qemu_iothread_add($vmid, $deviceid, $device); | |
3501 | $devicefull .= ",iothread=iothread-$deviceid"; | |
3502 | } | |
3503 | ||
6e11f143 AD |
3504 | if($deviceid =~ m/^virtioscsi(\d+)$/ && $device->{queues}) { |
3505 | $devicefull .= ",num_queues=$device->{queues}"; | |
3506 | } | |
3507 | ||
cfc817c7 | 3508 | qemu_deviceadd($vmid, $devicefull); |
fee46675 | 3509 | qemu_deviceaddverify($vmid, $deviceid); |
cfc817c7 | 3510 | |
fee46675 DM |
3511 | } elsif ($deviceid =~ m/^(scsi)(\d+)$/) { |
3512 | ||
3513 | qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device); | |
3514 | qemu_driveadd($storecfg, $vmid, $device); | |
a1b7d579 | 3515 | |
fee46675 DM |
3516 | my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); |
3517 | eval { qemu_deviceadd($vmid, $devicefull); }; | |
3518 | if (my $err = $@) { | |
63c2da2f DM |
3519 | eval { qemu_drivedel($vmid, $deviceid); }; |
3520 | warn $@ if $@; | |
fee46675 | 3521 | die $err; |
a4f091a0 | 3522 | } |
a4f091a0 | 3523 | |
fee46675 DM |
3524 | } elsif ($deviceid =~ m/^(net)(\d+)$/) { |
3525 | ||
2630d2a9 | 3526 | return undef if !qemu_netdevadd($vmid, $conf, $device, $deviceid); |
8718099c AD |
3527 | |
3528 | my $machine_type = PVE::QemuServer::qemu_machine_pxe($vmid, $conf); | |
3529 | my $use_old_bios_files = undef; | |
3530 | ($use_old_bios_files, $machine_type) = qemu_use_old_bios_files($machine_type); | |
3531 | ||
3532 | my $netdevicefull = print_netdevice_full($vmid, $conf, $device, $deviceid, undef, $use_old_bios_files); | |
2630d2a9 | 3533 | qemu_deviceadd($vmid, $netdevicefull); |
fee46675 DM |
3534 | eval { qemu_deviceaddverify($vmid, $deviceid); }; |
3535 | if (my $err = $@) { | |
3536 | eval { qemu_netdevdel($vmid, $deviceid); }; | |
3537 | warn $@ if $@; | |
3538 | die $err; | |
2630d2a9 | 3539 | } |
2630d2a9 | 3540 | |
fee46675 | 3541 | } elsif (!$q35 && $deviceid =~ m/^(pci\.)(\d+)$/) { |
b467f79a | 3542 | |
40f28a9f AD |
3543 | my $bridgeid = $2; |
3544 | my $pciaddr = print_pci_addr($deviceid); | |
3545 | my $devicefull = "pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr"; | |
a1b7d579 | 3546 | |
40f28a9f | 3547 | qemu_deviceadd($vmid, $devicefull); |
fee46675 DM |
3548 | qemu_deviceaddverify($vmid, $deviceid); |
3549 | ||
3550 | } else { | |
a1b7d579 | 3551 | die "can't hotplug device '$deviceid'\n"; |
40f28a9f AD |
3552 | } |
3553 | ||
5e5dcb73 | 3554 | return 1; |
a4dea331 DA |
3555 | } |
3556 | ||
3eec5767 | 3557 | # fixme: this should raise exceptions on error! |
ec21aa11 | 3558 | sub vm_deviceunplug { |
f19d1c47 | 3559 | my ($vmid, $conf, $deviceid) = @_; |
873c2d69 | 3560 | |
95d6343b DA |
3561 | my $devices_list = vm_devices_list($vmid); |
3562 | return 1 if !defined($devices_list->{$deviceid}); | |
3563 | ||
63c2da2f DM |
3564 | die "can't unplug bootdisk" if $conf->{bootdisk} && $conf->{bootdisk} eq $deviceid; |
3565 | ||
3d7389fe | 3566 | if ($deviceid eq 'tablet') { |
63c2da2f | 3567 | |
3d7389fe | 3568 | qemu_devicedel($vmid, $deviceid); |
3d7389fe | 3569 | |
63c2da2f | 3570 | } elsif ($deviceid =~ m/^(virtio)(\d+)$/) { |
f19d1c47 | 3571 | |
5e5dcb73 | 3572 | qemu_devicedel($vmid, $deviceid); |
63c2da2f DM |
3573 | qemu_devicedelverify($vmid, $deviceid); |
3574 | qemu_drivedel($vmid, $deviceid); | |
22de899a AD |
3575 | qemu_iothread_del($conf, $vmid, $deviceid); |
3576 | ||
2733141c | 3577 | } elsif ($deviceid =~ m/^(virtioscsi|scsihw)(\d+)$/) { |
a1b7d579 | 3578 | |
63c2da2f | 3579 | qemu_devicedel($vmid, $deviceid); |
8ce30dde | 3580 | qemu_devicedelverify($vmid, $deviceid); |
fc8b40fd | 3581 | qemu_iothread_del($conf, $vmid, $deviceid); |
a1b7d579 | 3582 | |
63c2da2f | 3583 | } elsif ($deviceid =~ m/^(scsi)(\d+)$/) { |
cfc817c7 | 3584 | |
8bcf3068 AD |
3585 | #qemu 2.3 segfault on drive_del with virtioscsi + iothread |
3586 | my $device = parse_drive($deviceid, $conf->{$deviceid}); | |
3587 | die "virtioscsi with iothread is not hot-unplugglable currently" if $device->{iothread}; | |
3588 | ||
63c2da2f DM |
3589 | qemu_devicedel($vmid, $deviceid); |
3590 | qemu_drivedel($vmid, $deviceid); | |
a1b7d579 | 3591 | qemu_deletescsihw($conf, $vmid, $deviceid); |
8ce30dde | 3592 | |
63c2da2f | 3593 | } elsif ($deviceid =~ m/^(net)(\d+)$/) { |
a4f091a0 | 3594 | |
2630d2a9 | 3595 | qemu_devicedel($vmid, $deviceid); |
63c2da2f DM |
3596 | qemu_devicedelverify($vmid, $deviceid); |
3597 | qemu_netdevdel($vmid, $deviceid); | |
3598 | ||
3599 | } else { | |
3600 | die "can't unplug device '$deviceid'\n"; | |
2630d2a9 DA |
3601 | } |
3602 | ||
5e5dcb73 DA |
3603 | return 1; |
3604 | } | |
3605 | ||
3606 | sub qemu_deviceadd { | |
3607 | my ($vmid, $devicefull) = @_; | |
873c2d69 | 3608 | |
d695b5b7 AD |
3609 | $devicefull = "driver=".$devicefull; |
3610 | my %options = split(/[=,]/, $devicefull); | |
f19d1c47 | 3611 | |
d695b5b7 | 3612 | vm_mon_cmd($vmid, "device_add" , %options); |
5e5dcb73 | 3613 | } |
afdb31d5 | 3614 | |
5e5dcb73 | 3615 | sub qemu_devicedel { |
fee46675 | 3616 | my ($vmid, $deviceid) = @_; |
63c2da2f | 3617 | |
5a77d8c1 | 3618 | my $ret = vm_mon_cmd($vmid, "device_del", id => $deviceid); |
5e5dcb73 DA |
3619 | } |
3620 | ||
22de899a AD |
3621 | sub qemu_iothread_add { |
3622 | my($vmid, $deviceid, $device) = @_; | |
3623 | ||
3624 | if ($device->{iothread}) { | |
3625 | my $iothreads = vm_iothreads_list($vmid); | |
3626 | qemu_objectadd($vmid, "iothread-$deviceid", "iothread") if !$iothreads->{"iothread-$deviceid"}; | |
3627 | } | |
3628 | } | |
3629 | ||
3630 | sub qemu_iothread_del { | |
3631 | my($conf, $vmid, $deviceid) = @_; | |
3632 | ||
3633 | my $device = parse_drive($deviceid, $conf->{$deviceid}); | |
3634 | if ($device->{iothread}) { | |
3635 | my $iothreads = vm_iothreads_list($vmid); | |
3636 | qemu_objectdel($vmid, "iothread-$deviceid") if $iothreads->{"iothread-$deviceid"}; | |
3637 | } | |
3638 | } | |
3639 | ||
4d3f29ed AD |
3640 | sub qemu_objectadd { |
3641 | my($vmid, $objectid, $qomtype) = @_; | |
3642 | ||
3643 | vm_mon_cmd($vmid, "object-add", id => $objectid, "qom-type" => $qomtype); | |
3644 | ||
3645 | return 1; | |
3646 | } | |
3647 | ||
3648 | sub qemu_objectdel { | |
3649 | my($vmid, $objectid) = @_; | |
3650 | ||
3651 | vm_mon_cmd($vmid, "object-del", id => $objectid); | |
3652 | ||
3653 | return 1; | |
3654 | } | |
3655 | ||
5e5dcb73 | 3656 | sub qemu_driveadd { |
fee46675 | 3657 | my ($storecfg, $vmid, $device) = @_; |
5e5dcb73 DA |
3658 | |
3659 | my $drive = print_drive_full($storecfg, $vmid, $device); | |
7a69fc3c | 3660 | $drive =~ s/\\/\\\\/g; |
8ead5ec7 | 3661 | my $ret = vm_human_monitor_command($vmid, "drive_add auto \"$drive\""); |
fee46675 | 3662 | |
5e5dcb73 | 3663 | # If the command succeeds qemu prints: "OK" |
fee46675 DM |
3664 | return 1 if $ret =~ m/OK/s; |
3665 | ||
3666 | die "adding drive failed: $ret\n"; | |
5e5dcb73 | 3667 | } |
afdb31d5 | 3668 | |
5e5dcb73 DA |
3669 | sub qemu_drivedel { |
3670 | my($vmid, $deviceid) = @_; | |
873c2d69 | 3671 | |
7b7c6d1b | 3672 | my $ret = vm_human_monitor_command($vmid, "drive_del drive-$deviceid"); |
5e5dcb73 | 3673 | $ret =~ s/^\s+//; |
a1b7d579 | 3674 | |
63c2da2f | 3675 | return 1 if $ret eq ""; |
a1b7d579 | 3676 | |
63c2da2f | 3677 | # NB: device not found errors mean the drive was auto-deleted and we ignore the error |
a1b7d579 DM |
3678 | return 1 if $ret =~ m/Device \'.*?\' not found/s; |
3679 | ||
63c2da2f | 3680 | die "deleting drive $deviceid failed : $ret\n"; |
5e5dcb73 | 3681 | } |
f19d1c47 | 3682 | |
5e5dcb73 | 3683 | sub qemu_deviceaddverify { |
fee46675 | 3684 | my ($vmid, $deviceid) = @_; |
873c2d69 | 3685 | |
5e5dcb73 DA |
3686 | for (my $i = 0; $i <= 5; $i++) { |
3687 | my $devices_list = vm_devices_list($vmid); | |
3688 | return 1 if defined($devices_list->{$deviceid}); | |
3689 | sleep 1; | |
afdb31d5 | 3690 | } |
fee46675 DM |
3691 | |
3692 | die "error on hotplug device '$deviceid'\n"; | |
5e5dcb73 | 3693 | } |
afdb31d5 | 3694 | |
5e5dcb73 DA |
3695 | |
3696 | sub qemu_devicedelverify { | |
63c2da2f DM |
3697 | my ($vmid, $deviceid) = @_; |
3698 | ||
a1b7d579 | 3699 | # need to verify that the device is correctly removed as device_del |
63c2da2f | 3700 | # is async and empty return is not reliable |
5e5dcb73 | 3701 | |
5e5dcb73 DA |
3702 | for (my $i = 0; $i <= 5; $i++) { |
3703 | my $devices_list = vm_devices_list($vmid); | |
3704 | return 1 if !defined($devices_list->{$deviceid}); | |
3705 | sleep 1; | |
afdb31d5 | 3706 | } |
63c2da2f DM |
3707 | |
3708 | die "error on hot-unplugging device '$deviceid'\n"; | |
873c2d69 DA |
3709 | } |
3710 | ||
cdd20088 | 3711 | sub qemu_findorcreatescsihw { |
cfc817c7 DA |
3712 | my ($storecfg, $conf, $vmid, $device) = @_; |
3713 | ||
ee034f5c | 3714 | my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device); |
2733141c AD |
3715 | |
3716 | my $scsihwid="$controller_prefix$controller"; | |
cfc817c7 DA |
3717 | my $devices_list = vm_devices_list($vmid); |
3718 | ||
cdd20088 | 3719 | if(!defined($devices_list->{$scsihwid})) { |
fc8b40fd | 3720 | vm_deviceplug($storecfg, $conf, $vmid, $scsihwid, $device); |
cfc817c7 | 3721 | } |
fee46675 | 3722 | |
cfc817c7 DA |
3723 | return 1; |
3724 | } | |
3725 | ||
8ce30dde AD |
3726 | sub qemu_deletescsihw { |
3727 | my ($conf, $vmid, $opt) = @_; | |
3728 | ||
3729 | my $device = parse_drive($opt, $conf->{$opt}); | |
3730 | ||
a1511b3c | 3731 | if ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) { |
2733141c AD |
3732 | vm_deviceunplug($vmid, $conf, "virtioscsi$device->{index}"); |
3733 | return 1; | |
3734 | } | |
3735 | ||
ee034f5c | 3736 | my ($maxdev, $controller, $controller_prefix) = scsihw_infos($conf, $device); |
8ce30dde AD |
3737 | |
3738 | my $devices_list = vm_devices_list($vmid); | |
3739 | foreach my $opt (keys %{$devices_list}) { | |
74479ee9 | 3740 | if (PVE::QemuServer::is_valid_drivename($opt)) { |
8ce30dde AD |
3741 | my $drive = PVE::QemuServer::parse_drive($opt, $conf->{$opt}); |
3742 | if($drive->{interface} eq 'scsi' && $drive->{index} < (($maxdev-1)*($controller+1))) { | |
3743 | return 1; | |
3744 | } | |
3745 | } | |
3746 | } | |
3747 | ||
3748 | my $scsihwid="scsihw$controller"; | |
3749 | ||
3750 | vm_deviceunplug($vmid, $conf, $scsihwid); | |
3751 | ||
3752 | return 1; | |
3753 | } | |
3754 | ||
281fedb3 | 3755 | sub qemu_add_pci_bridge { |
40f28a9f AD |
3756 | my ($storecfg, $conf, $vmid, $device) = @_; |
3757 | ||
3758 | my $bridges = {}; | |
281fedb3 DM |
3759 | |
3760 | my $bridgeid; | |
3761 | ||
40f28a9f AD |
3762 | print_pci_addr($device, $bridges); |
3763 | ||
3764 | while (my ($k, $v) = each %$bridges) { | |
3765 | $bridgeid = $k; | |
3766 | } | |
fee46675 | 3767 | return 1 if !defined($bridgeid) || $bridgeid < 1; |
281fedb3 | 3768 | |
40f28a9f AD |
3769 | my $bridge = "pci.$bridgeid"; |
3770 | my $devices_list = vm_devices_list($vmid); | |
3771 | ||
281fedb3 | 3772 | if (!defined($devices_list->{$bridge})) { |
fee46675 | 3773 | vm_deviceplug($storecfg, $conf, $vmid, $bridge); |
40f28a9f | 3774 | } |
281fedb3 | 3775 | |
40f28a9f AD |
3776 | return 1; |
3777 | } | |
3778 | ||
25088687 DM |
3779 | sub qemu_set_link_status { |
3780 | my ($vmid, $device, $up) = @_; | |
3781 | ||
a1b7d579 | 3782 | vm_mon_cmd($vmid, "set_link", name => $device, |
25088687 DM |
3783 | up => $up ? JSON::true : JSON::false); |
3784 | } | |
3785 | ||
2630d2a9 DA |
3786 | sub qemu_netdevadd { |
3787 | my ($vmid, $conf, $device, $deviceid) = @_; | |
3788 | ||
208ba94e | 3789 | my $netdev = print_netdev_full($vmid, $conf, $device, $deviceid, 1); |
73aa03b8 | 3790 | my %options = split(/[=,]/, $netdev); |
2630d2a9 | 3791 | |
73aa03b8 AD |
3792 | vm_mon_cmd($vmid, "netdev_add", %options); |
3793 | return 1; | |
2630d2a9 DA |
3794 | } |
3795 | ||
3796 | sub qemu_netdevdel { | |
3797 | my ($vmid, $deviceid) = @_; | |
3798 | ||
89c1e0f4 | 3799 | vm_mon_cmd($vmid, "netdev_del", id => $deviceid); |
2630d2a9 DA |
3800 | } |
3801 | ||
838776ab | 3802 | sub qemu_cpu_hotplug { |
8edc9c08 | 3803 | my ($vmid, $conf, $vcpus) = @_; |
838776ab | 3804 | |
8edc9c08 AD |
3805 | my $sockets = 1; |
3806 | $sockets = $conf->{smp} if $conf->{smp}; # old style - no longer iused | |
3807 | $sockets = $conf->{sockets} if $conf->{sockets}; | |
3808 | my $cores = $conf->{cores} || 1; | |
3809 | my $maxcpus = $sockets * $cores; | |
838776ab | 3810 | |
8edc9c08 | 3811 | $vcpus = $maxcpus if !$vcpus; |
3a11fadb | 3812 | |
8edc9c08 AD |
3813 | die "you can't add more vcpus than maxcpus\n" |
3814 | if $vcpus > $maxcpus; | |
3a11fadb | 3815 | |
8edc9c08 | 3816 | my $currentvcpus = $conf->{vcpus} || $maxcpus; |
3a11fadb | 3817 | die "online cpu unplug is not yet possible\n" |
8edc9c08 | 3818 | if $vcpus < $currentvcpus; |
838776ab | 3819 | |
8edc9c08 AD |
3820 | my $currentrunningvcpus = vm_mon_cmd($vmid, "query-cpus"); |
3821 | die "vcpus in running vm is different than configuration\n" | |
3822 | if scalar(@{$currentrunningvcpus}) != $currentvcpus; | |
838776ab | 3823 | |
8edc9c08 | 3824 | for (my $i = $currentvcpus; $i < $vcpus; $i++) { |
838776ab AD |
3825 | vm_mon_cmd($vmid, "cpu-add", id => int($i)); |
3826 | } | |
3827 | } | |
3828 | ||
4d3f29ed AD |
3829 | sub qemu_memory_hotplug { |
3830 | my ($vmid, $conf, $defaults, $opt, $value) = @_; | |
3831 | ||
3832 | return $value if !check_running($vmid); | |
a1b7d579 | 3833 | |
4d3f29ed | 3834 | my $memory = $conf->{memory} || $defaults->{memory}; |
a1b7d579 | 3835 | $value = $defaults->{memory} if !$value; |
4d3f29ed AD |
3836 | return $value if $value == $memory; |
3837 | ||
3838 | my $static_memory = $STATICMEM; | |
3839 | my $dimm_memory = $memory - $static_memory; | |
3840 | ||
3841 | die "memory can't be lower than $static_memory MB" if $value < $static_memory; | |
4d3f29ed AD |
3842 | die "you cannot add more memory than $MAX_MEM MB!\n" if $memory > $MAX_MEM; |
3843 | ||
3844 | ||
3845 | my $sockets = 1; | |
3846 | $sockets = $conf->{sockets} if $conf->{sockets}; | |
3847 | ||
525814b2 | 3848 | if($value > $memory) { |
e059fb4d | 3849 | |
525814b2 AD |
3850 | foreach_dimm($conf, $vmid, $value, $sockets, sub { |
3851 | my ($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory) = @_; | |
4d3f29ed | 3852 | |
525814b2 | 3853 | return if $current_size <= $conf->{memory}; |
4d3f29ed | 3854 | |
525814b2 AD |
3855 | eval { vm_mon_cmd($vmid, "object-add", 'qom-type' => "memory-backend-ram", id => "mem-$name", props => { size => int($dimm_size*1024*1024) } ) }; |
3856 | if (my $err = $@) { | |
3857 | eval { qemu_objectdel($vmid, "mem-$name"); }; | |
3858 | die $err; | |
3859 | } | |
3860 | ||
3861 | eval { vm_mon_cmd($vmid, "device_add", driver => "pc-dimm", id => "$name", memdev => "mem-$name", node => $numanode) }; | |
3862 | if (my $err = $@) { | |
3863 | eval { qemu_objectdel($vmid, "mem-$name"); }; | |
3864 | die $err; | |
3865 | } | |
3866 | #update conf after each succesful module hotplug | |
3867 | $conf->{memory} = $current_size; | |
ffda963f | 3868 | PVE::QemuConfig->write_config($vmid, $conf); |
525814b2 AD |
3869 | }); |
3870 | ||
3871 | } else { | |
3872 | ||
3873 | foreach_reverse_dimm($conf, $vmid, $value, $sockets, sub { | |
3874 | my ($conf, $vmid, $name, $dimm_size, $numanode, $current_size, $memory) = @_; | |
3875 | ||
3876 | return if $current_size >= $conf->{memory}; | |
3877 | print "try to unplug memory dimm $name\n"; | |
3878 | ||
3879 | my $retry = 0; | |
3880 | while (1) { | |
3881 | eval { qemu_devicedel($vmid, $name) }; | |
3882 | sleep 3; | |
3883 | my $dimm_list = qemu_dimm_list($vmid); | |
3884 | last if !$dimm_list->{$name}; | |
3885 | raise_param_exc({ $name => "error unplug memory module" }) if $retry > 5; | |
3886 | $retry++; | |
3887 | } | |
3888 | ||
3889 | #update conf after each succesful module unplug | |
3890 | $conf->{memory} = $current_size; | |
3891 | ||
3892 | eval { qemu_objectdel($vmid, "mem-$name"); }; | |
ffda963f | 3893 | PVE::QemuConfig->write_config($vmid, $conf); |
525814b2 AD |
3894 | }); |
3895 | } | |
3896 | } | |
3897 | ||
3898 | sub qemu_dimm_list { | |
3899 | my ($vmid) = @_; | |
3900 | ||
3901 | my $dimmarray = vm_mon_cmd_nocheck($vmid, "query-memory-devices"); | |
3902 | my $dimms = {}; | |
3903 | ||
3904 | foreach my $dimm (@$dimmarray) { | |
3905 | ||
3906 | $dimms->{$dimm->{data}->{id}}->{id} = $dimm->{data}->{id}; | |
3907 | $dimms->{$dimm->{data}->{id}}->{node} = $dimm->{data}->{node}; | |
3908 | $dimms->{$dimm->{data}->{id}}->{addr} = $dimm->{data}->{addr}; | |
3909 | $dimms->{$dimm->{data}->{id}}->{size} = $dimm->{data}->{size}; | |
3910 | $dimms->{$dimm->{data}->{id}}->{slot} = $dimm->{data}->{slot}; | |
3911 | } | |
3912 | return $dimms; | |
4d3f29ed AD |
3913 | } |
3914 | ||
affd2f88 | 3915 | sub qemu_block_set_io_throttle { |
277ca170 WB |
3916 | my ($vmid, $deviceid, |
3917 | $bps, $bps_rd, $bps_wr, $iops, $iops_rd, $iops_wr, | |
3918 | $bps_max, $bps_rd_max, $bps_wr_max, $iops_max, $iops_rd_max, $iops_wr_max) = @_; | |
affd2f88 | 3919 | |
f3f323a3 AD |
3920 | return if !check_running($vmid) ; |
3921 | ||
277ca170 WB |
3922 | vm_mon_cmd($vmid, "block_set_io_throttle", device => $deviceid, |
3923 | bps => int($bps), | |
3924 | bps_rd => int($bps_rd), | |
3925 | bps_wr => int($bps_wr), | |
3926 | iops => int($iops), | |
3927 | iops_rd => int($iops_rd), | |
3928 | iops_wr => int($iops_wr), | |
3929 | bps_max => int($bps_max), | |
3930 | bps_rd_max => int($bps_rd_max), | |
3931 | bps_wr_max => int($bps_wr_max), | |
3932 | iops_max => int($iops_max), | |
3933 | iops_rd_max => int($iops_rd_max), | |
3934 | iops_wr_max => int($iops_wr_max) | |
3935 | ); | |
f3f323a3 | 3936 | |
affd2f88 AD |
3937 | } |
3938 | ||
f5eb281a | 3939 | # old code, only used to shutdown old VM after update |
dab36e1e DM |
3940 | sub __read_avail { |
3941 | my ($fh, $timeout) = @_; | |
3942 | ||
3943 | my $sel = new IO::Select; | |
3944 | $sel->add($fh); | |
3945 | ||
3946 | my $res = ''; | |
3947 | my $buf; | |
3948 | ||
3949 | my @ready; | |
3950 | while (scalar (@ready = $sel->can_read($timeout))) { | |
3951 | my $count; | |
3952 | if ($count = $fh->sysread($buf, 8192)) { | |
3953 | if ($buf =~ /^(.*)\(qemu\) $/s) { | |
3954 | $res .= $1; | |
3955 | last; | |
3956 | } else { | |
3957 | $res .= $buf; | |
3958 | } | |
3959 | } else { | |
3960 | if (!defined($count)) { | |
3961 | die "$!\n"; | |
3962 | } | |
3963 | last; | |
3964 | } | |
3965 | } | |
3966 | ||
3967 | die "monitor read timeout\n" if !scalar(@ready); | |
f5eb281a | 3968 | |
dab36e1e DM |
3969 | return $res; |
3970 | } | |
3971 | ||
f5eb281a | 3972 | # old code, only used to shutdown old VM after update |
dab36e1e DM |
3973 | sub vm_monitor_command { |
3974 | my ($vmid, $cmdstr, $nocheck) = @_; | |
f5eb281a | 3975 | |
dab36e1e DM |
3976 | my $res; |
3977 | ||
3978 | eval { | |
3979 | die "VM $vmid not running\n" if !check_running($vmid, $nocheck); | |
3980 | ||
3981 | my $sname = "${var_run_tmpdir}/$vmid.mon"; | |
3982 | ||
3983 | my $sock = IO::Socket::UNIX->new( Peer => $sname ) || | |
3984 | die "unable to connect to VM $vmid socket - $!\n"; | |
3985 | ||
3986 | my $timeout = 3; | |
3987 | ||
3988 | # hack: migrate sometime blocks the monitor (when migrate_downtime | |
3989 | # is set) | |
3990 | if ($cmdstr =~ m/^(info\s+migrate|migrate\s)/) { | |
3991 | $timeout = 60*60; # 1 hour | |
3992 | } | |
3993 | ||
3994 | # read banner; | |
3995 | my $data = __read_avail($sock, $timeout); | |
3996 | ||
3997 | if ($data !~ m/^QEMU\s+(\S+)\s+monitor\s/) { | |
3998 | die "got unexpected qemu monitor banner\n"; | |
3999 | } | |
4000 | ||
4001 | my $sel = new IO::Select; | |
4002 | $sel->add($sock); | |
4003 | ||
4004 | if (!scalar(my @ready = $sel->can_write($timeout))) { | |
4005 | die "monitor write error - timeout"; | |
4006 | } | |
4007 | ||
4008 | my $fullcmd = "$cmdstr\r"; | |
4009 | ||
4010 | # syslog('info', "VM $vmid monitor command: $cmdstr"); | |
4011 | ||
4012 | my $b; | |
4013 | if (!($b = $sock->syswrite($fullcmd)) || ($b != length($fullcmd))) { | |
4014 | die "monitor write error - $!"; | |
4015 | } | |
4016 | ||
4017 | return if ($cmdstr eq 'q') || ($cmdstr eq 'quit'); | |
4018 | ||
4019 | $timeout = 20; | |
4020 | ||
4021 | if ($cmdstr =~ m/^(info\s+migrate|migrate\s)/) { | |
4022 | $timeout = 60*60; # 1 hour | |
4023 | } elsif ($cmdstr =~ m/^(eject|change)/) { | |
4024 | $timeout = 60; # note: cdrom mount command is slow | |
4025 | } | |
4026 | if ($res = __read_avail($sock, $timeout)) { | |
4027 | ||
4028 | my @lines = split("\r?\n", $res); | |
f5eb281a | 4029 | |
dab36e1e | 4030 | shift @lines if $lines[0] !~ m/^unknown command/; # skip echo |
f5eb281a | 4031 | |
dab36e1e DM |
4032 | $res = join("\n", @lines); |
4033 | $res .= "\n"; | |
4034 | } | |
4035 | }; | |
4036 | ||
4037 | my $err = $@; | |
4038 | ||
4039 | if ($err) { | |
4040 | syslog("err", "VM $vmid monitor command failed - $err"); | |
4041 | die $err; | |
4042 | } | |
f5eb281a | 4043 | |
dab36e1e DM |
4044 | return $res; |
4045 | } | |
4046 | ||
c1175c92 AD |
4047 | sub qemu_block_resize { |
4048 | my ($vmid, $deviceid, $storecfg, $volid, $size) = @_; | |
4049 | ||
ed221350 | 4050 | my $running = check_running($vmid); |
c1175c92 AD |
4051 | |
4052 | return if !PVE::Storage::volume_resize($storecfg, $volid, $size, $running); | |
4053 | ||
4054 | return if !$running; | |
4055 | ||
4056 | vm_mon_cmd($vmid, "block_resize", device => $deviceid, size => int($size)); | |
4057 | ||
4058 | } | |
4059 | ||
1ab0057c AD |
4060 | sub qemu_volume_snapshot { |
4061 | my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_; | |
4062 | ||
ed221350 | 4063 | my $running = check_running($vmid); |
1ab0057c | 4064 | |
e5eaa028 WL |
4065 | if ($running && do_snapshots_with_qemu($storecfg, $volid)){ |
4066 | vm_mon_cmd($vmid, "snapshot-drive", device => $deviceid, name => $snap); | |
4067 | } else { | |
4068 | PVE::Storage::volume_snapshot($storecfg, $volid, $snap); | |
4069 | } | |
1ab0057c AD |
4070 | } |
4071 | ||
fc46aff9 AD |
4072 | sub qemu_volume_snapshot_delete { |
4073 | my ($vmid, $deviceid, $storecfg, $volid, $snap) = @_; | |
4074 | ||
ed221350 | 4075 | my $running = check_running($vmid); |
fc46aff9 AD |
4076 | |
4077 | return if !PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snap, $running); | |
4078 | ||
4079 | return if !$running; | |
4080 | ||
18bfb361 | 4081 | vm_mon_cmd($vmid, "delete-drive-snapshot", device => $deviceid, name => $snap); |
fc46aff9 AD |
4082 | } |
4083 | ||
264e519f DM |
4084 | sub set_migration_caps { |
4085 | my ($vmid) = @_; | |
a89fded1 | 4086 | |
8b8345f3 | 4087 | my $cap_ref = []; |
a89fded1 AD |
4088 | |
4089 | my $enabled_cap = { | |
8b8345f3 | 4090 | "auto-converge" => 1, |
0b0a47e8 | 4091 | "xbzrle" => 1, |
8b8345f3 DM |
4092 | "x-rdma-pin-all" => 0, |
4093 | "zero-blocks" => 0, | |
b62532e4 | 4094 | "compress" => 0 |
a89fded1 AD |
4095 | }; |
4096 | ||
8b8345f3 | 4097 | my $supported_capabilities = vm_mon_cmd_nocheck($vmid, "query-migrate-capabilities"); |
a89fded1 | 4098 | |
8b8345f3 | 4099 | for my $supported_capability (@$supported_capabilities) { |
b463a3ce SP |
4100 | push @$cap_ref, { |
4101 | capability => $supported_capability->{capability}, | |
22430fa2 DM |
4102 | state => $enabled_cap->{$supported_capability->{capability}} ? JSON::true : JSON::false, |
4103 | }; | |
a89fded1 AD |
4104 | } |
4105 | ||
8b8345f3 DM |
4106 | vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => $cap_ref); |
4107 | } | |
a89fded1 | 4108 | |
81d95ae1 | 4109 | my $fast_plug_option = { |
7498eb64 | 4110 | 'lock' => 1, |
81d95ae1 | 4111 | 'name' => 1, |
a1b7d579 | 4112 | 'onboot' => 1, |
81d95ae1 DM |
4113 | 'shares' => 1, |
4114 | 'startup' => 1, | |
b0ec896e | 4115 | 'description' => 1, |
81d95ae1 DM |
4116 | }; |
4117 | ||
3a11fadb DM |
4118 | # hotplug changes in [PENDING] |
4119 | # $selection hash can be used to only apply specified options, for | |
4120 | # example: { cores => 1 } (only apply changed 'cores') | |
4121 | # $errors ref is used to return error messages | |
c427973b | 4122 | sub vmconfig_hotplug_pending { |
3a11fadb | 4123 | my ($vmid, $conf, $storecfg, $selection, $errors) = @_; |
c427973b | 4124 | |
8e90138a | 4125 | my $defaults = load_defaults(); |
c427973b DM |
4126 | |
4127 | # commit values which do not have any impact on running VM first | |
3a11fadb DM |
4128 | # Note: those option cannot raise errors, we we do not care about |
4129 | # $selection and always apply them. | |
4130 | ||
4131 | my $add_error = sub { | |
4132 | my ($opt, $msg) = @_; | |
4133 | $errors->{$opt} = "hotplug problem - $msg"; | |
4134 | }; | |
c427973b DM |
4135 | |
4136 | my $changes = 0; | |
4137 | foreach my $opt (keys %{$conf->{pending}}) { # add/change | |
81d95ae1 | 4138 | if ($fast_plug_option->{$opt}) { |
c427973b DM |
4139 | $conf->{$opt} = $conf->{pending}->{$opt}; |
4140 | delete $conf->{pending}->{$opt}; | |
4141 | $changes = 1; | |
4142 | } | |
4143 | } | |
4144 | ||
4145 | if ($changes) { | |
ffda963f FG |
4146 | PVE::QemuConfig->write_config($vmid, $conf); |
4147 | $conf = PVE::QemuConfig->load_config($vmid); # update/reload | |
c427973b DM |
4148 | } |
4149 | ||
b3c2bdd1 | 4150 | my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1'); |
c427973b | 4151 | |
3dc38fbb WB |
4152 | my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete}); |
4153 | while (my ($opt, $force) = each %$pending_delete_hash) { | |
3a11fadb | 4154 | next if $selection && !$selection->{$opt}; |
3a11fadb | 4155 | eval { |
51a6f637 AD |
4156 | if ($opt eq 'hotplug') { |
4157 | die "skip\n" if ($conf->{hotplug} =~ /memory/); | |
4158 | } elsif ($opt eq 'tablet') { | |
b3c2bdd1 | 4159 | die "skip\n" if !$hotplug_features->{usb}; |
3a11fadb DM |
4160 | if ($defaults->{tablet}) { |
4161 | vm_deviceplug($storecfg, $conf, $vmid, $opt); | |
4162 | } else { | |
4163 | vm_deviceunplug($vmid, $conf, $opt); | |
4164 | } | |
8edc9c08 | 4165 | } elsif ($opt eq 'vcpus') { |
b3c2bdd1 | 4166 | die "skip\n" if !$hotplug_features->{cpu}; |
8edc9c08 | 4167 | qemu_cpu_hotplug($vmid, $conf, undef); |
9c2f7069 | 4168 | } elsif ($opt eq 'balloon') { |
81d95ae1 DM |
4169 | # enable balloon device is not hotpluggable |
4170 | die "skip\n" if !defined($conf->{balloon}) || $conf->{balloon}; | |
4171 | } elsif ($fast_plug_option->{$opt}) { | |
4172 | # do nothing | |
3eec5767 | 4173 | } elsif ($opt =~ m/^net(\d+)$/) { |
b3c2bdd1 | 4174 | die "skip\n" if !$hotplug_features->{network}; |
3eec5767 | 4175 | vm_deviceunplug($vmid, $conf, $opt); |
74479ee9 | 4176 | } elsif (is_valid_drivename($opt)) { |
b3c2bdd1 | 4177 | die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/; |
19120f99 | 4178 | vm_deviceunplug($vmid, $conf, $opt); |
3dc38fbb | 4179 | vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force); |
4d3f29ed AD |
4180 | } elsif ($opt =~ m/^memory$/) { |
4181 | die "skip\n" if !$hotplug_features->{memory}; | |
4182 | qemu_memory_hotplug($vmid, $conf, $defaults, $opt); | |
c8effec3 AD |
4183 | } elsif ($opt eq 'cpuunits') { |
4184 | cgroups_write("cpu", $vmid, "cpu.shares", $defaults->{cpuunits}); | |
58be00f1 AD |
4185 | } elsif ($opt eq 'cpulimit') { |
4186 | cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", -1); | |
3d7389fe | 4187 | } else { |
e56beeda | 4188 | die "skip\n"; |
3d7389fe | 4189 | } |
3a11fadb DM |
4190 | }; |
4191 | if (my $err = $@) { | |
e56beeda DM |
4192 | &$add_error($opt, $err) if $err ne "skip\n"; |
4193 | } else { | |
3a11fadb DM |
4194 | # save new config if hotplug was successful |
4195 | delete $conf->{$opt}; | |
4196 | vmconfig_undelete_pending_option($conf, $opt); | |
ffda963f FG |
4197 | PVE::QemuConfig->write_config($vmid, $conf); |
4198 | $conf = PVE::QemuConfig->load_config($vmid); # update/reload | |
3d7389fe | 4199 | } |
3d7389fe DM |
4200 | } |
4201 | ||
4202 | foreach my $opt (keys %{$conf->{pending}}) { | |
3a11fadb | 4203 | next if $selection && !$selection->{$opt}; |
3d7389fe | 4204 | my $value = $conf->{pending}->{$opt}; |
3a11fadb | 4205 | eval { |
51a6f637 AD |
4206 | if ($opt eq 'hotplug') { |
4207 | die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/); | |
4208 | } elsif ($opt eq 'tablet') { | |
b3c2bdd1 | 4209 | die "skip\n" if !$hotplug_features->{usb}; |
3a11fadb DM |
4210 | if ($value == 1) { |
4211 | vm_deviceplug($storecfg, $conf, $vmid, $opt); | |
4212 | } elsif ($value == 0) { | |
4213 | vm_deviceunplug($vmid, $conf, $opt); | |
4214 | } | |
8edc9c08 | 4215 | } elsif ($opt eq 'vcpus') { |
b3c2bdd1 | 4216 | die "skip\n" if !$hotplug_features->{cpu}; |
3a11fadb DM |
4217 | qemu_cpu_hotplug($vmid, $conf, $value); |
4218 | } elsif ($opt eq 'balloon') { | |
81d95ae1 | 4219 | # enable/disable balloning device is not hotpluggable |
8fe689e7 | 4220 | my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon}); |
a1b7d579 | 4221 | my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon}); |
81d95ae1 DM |
4222 | die "skip\n" if $old_balloon_enabled != $new_balloon_enabled; |
4223 | ||
3a11fadb | 4224 | # allow manual ballooning if shares is set to zero |
4cc1efa6 | 4225 | if ((defined($conf->{shares}) && ($conf->{shares} == 0))) { |
9c2f7069 AD |
4226 | my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory}; |
4227 | vm_mon_cmd($vmid, "balloon", value => $balloon*1024*1024); | |
4228 | } | |
a1b7d579 | 4229 | } elsif ($opt =~ m/^net(\d+)$/) { |
3eec5767 | 4230 | # some changes can be done without hotplug |
a1b7d579 | 4231 | vmconfig_update_net($storecfg, $conf, $hotplug_features->{network}, |
b3c2bdd1 | 4232 | $vmid, $opt, $value); |
74479ee9 | 4233 | } elsif (is_valid_drivename($opt)) { |
a05cff86 | 4234 | # some changes can be done without hotplug |
b3c2bdd1 DM |
4235 | vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk}, |
4236 | $vmid, $opt, $value, 1); | |
4d3f29ed AD |
4237 | } elsif ($opt =~ m/^memory$/) { #dimms |
4238 | die "skip\n" if !$hotplug_features->{memory}; | |
4239 | $value = qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value); | |
c8effec3 AD |
4240 | } elsif ($opt eq 'cpuunits') { |
4241 | cgroups_write("cpu", $vmid, "cpu.shares", $conf->{pending}->{$opt}); | |
58be00f1 | 4242 | } elsif ($opt eq 'cpulimit') { |
c6f773b8 | 4243 | my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000); |
58be00f1 | 4244 | cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", $cpulimit); |
3a11fadb | 4245 | } else { |
e56beeda | 4246 | die "skip\n"; # skip non-hot-pluggable options |
3d7389fe | 4247 | } |
3a11fadb DM |
4248 | }; |
4249 | if (my $err = $@) { | |
e56beeda DM |
4250 | &$add_error($opt, $err) if $err ne "skip\n"; |
4251 | } else { | |
3a11fadb DM |
4252 | # save new config if hotplug was successful |
4253 | $conf->{$opt} = $value; | |
4254 | delete $conf->{pending}->{$opt}; | |
ffda963f FG |
4255 | PVE::QemuConfig->write_config($vmid, $conf); |
4256 | $conf = PVE::QemuConfig->load_config($vmid); # update/reload | |
3d7389fe | 4257 | } |
3d7389fe | 4258 | } |
c427973b | 4259 | } |
055d554d | 4260 | |
3dc38fbb WB |
4261 | sub try_deallocate_drive { |
4262 | my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_; | |
4263 | ||
4264 | if (($force || $key =~ /^unused/) && !drive_is_cdrom($drive, 1)) { | |
4265 | my $volid = $drive->{file}; | |
4266 | if (vm_is_volid_owner($storecfg, $vmid, $volid)) { | |
4267 | my $sid = PVE::Storage::parse_volume_id($volid); | |
4268 | $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']); | |
cee01bcb WB |
4269 | |
4270 | # check if the disk is really unused | |
cee01bcb | 4271 | die "unable to delete '$volid' - volume is still in use (snapshot?)\n" |
77019edf | 4272 | if is_volume_in_use($storecfg, $conf, $key, $volid); |
cee01bcb | 4273 | PVE::Storage::vdisk_free($storecfg, $volid); |
3dc38fbb | 4274 | return 1; |
40b977f3 WL |
4275 | } else { |
4276 | # If vm is not owner of this disk remove from config | |
4277 | return 1; | |
3dc38fbb WB |
4278 | } |
4279 | } | |
4280 | ||
4281 | return undef; | |
4282 | } | |
4283 | ||
4284 | sub vmconfig_delete_or_detach_drive { | |
4285 | my ($vmid, $storecfg, $conf, $opt, $force) = @_; | |
4286 | ||
4287 | my $drive = parse_drive($opt, $conf->{$opt}); | |
4288 | ||
4289 | my $rpcenv = PVE::RPCEnvironment::get(); | |
4290 | my $authuser = $rpcenv->get_user(); | |
4291 | ||
4292 | if ($force) { | |
4293 | $rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); | |
4294 | try_deallocate_drive($storecfg, $vmid, $conf, $opt, $drive, $rpcenv, $authuser, $force); | |
4295 | } else { | |
4296 | vmconfig_register_unused_drive($storecfg, $vmid, $conf, $drive); | |
4297 | } | |
4298 | } | |
4299 | ||
055d554d | 4300 | sub vmconfig_apply_pending { |
3a11fadb | 4301 | my ($vmid, $conf, $storecfg) = @_; |
c427973b DM |
4302 | |
4303 | # cold plug | |
055d554d | 4304 | |
3dc38fbb WB |
4305 | my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete}); |
4306 | while (my ($opt, $force) = each %$pending_delete_hash) { | |
055d554d | 4307 | die "internal error" if $opt =~ m/^unused/; |
ffda963f | 4308 | $conf = PVE::QemuConfig->load_config($vmid); # update/reload |
055d554d DM |
4309 | if (!defined($conf->{$opt})) { |
4310 | vmconfig_undelete_pending_option($conf, $opt); | |
ffda963f | 4311 | PVE::QemuConfig->write_config($vmid, $conf); |
74479ee9 | 4312 | } elsif (is_valid_drivename($opt)) { |
3dc38fbb | 4313 | vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force); |
055d554d DM |
4314 | vmconfig_undelete_pending_option($conf, $opt); |
4315 | delete $conf->{$opt}; | |
ffda963f | 4316 | PVE::QemuConfig->write_config($vmid, $conf); |
055d554d DM |
4317 | } else { |
4318 | vmconfig_undelete_pending_option($conf, $opt); | |
4319 | delete $conf->{$opt}; | |
ffda963f | 4320 | PVE::QemuConfig->write_config($vmid, $conf); |
055d554d DM |
4321 | } |
4322 | } | |
4323 | ||
ffda963f | 4324 | $conf = PVE::QemuConfig->load_config($vmid); # update/reload |
055d554d DM |
4325 | |
4326 | foreach my $opt (keys %{$conf->{pending}}) { # add/change | |
ffda963f | 4327 | $conf = PVE::QemuConfig->load_config($vmid); # update/reload |
055d554d DM |
4328 | |
4329 | if (defined($conf->{$opt}) && ($conf->{$opt} eq $conf->{pending}->{$opt})) { | |
4330 | # skip if nothing changed | |
74479ee9 | 4331 | } elsif (is_valid_drivename($opt)) { |
055d554d DM |
4332 | vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt})) |
4333 | if defined($conf->{$opt}); | |
4334 | $conf->{$opt} = $conf->{pending}->{$opt}; | |
4335 | } else { | |
4336 | $conf->{$opt} = $conf->{pending}->{$opt}; | |
4337 | } | |
4338 | ||
4339 | delete $conf->{pending}->{$opt}; | |
ffda963f | 4340 | PVE::QemuConfig->write_config($vmid, $conf); |
055d554d DM |
4341 | } |
4342 | } | |
4343 | ||
3eec5767 DM |
4344 | my $safe_num_ne = sub { |
4345 | my ($a, $b) = @_; | |
4346 | ||
4347 | return 0 if !defined($a) && !defined($b); | |
4348 | return 1 if !defined($a); | |
4349 | return 1 if !defined($b); | |
4350 | ||
4351 | return $a != $b; | |
4352 | }; | |
4353 | ||
4354 | my $safe_string_ne = sub { | |
4355 | my ($a, $b) = @_; | |
4356 | ||
4357 | return 0 if !defined($a) && !defined($b); | |
4358 | return 1 if !defined($a); | |
4359 | return 1 if !defined($b); | |
4360 | ||
4361 | return $a ne $b; | |
4362 | }; | |
4363 | ||
4364 | sub vmconfig_update_net { | |
b3c2bdd1 | 4365 | my ($storecfg, $conf, $hotplug, $vmid, $opt, $value) = @_; |
3eec5767 DM |
4366 | |
4367 | my $newnet = parse_net($value); | |
4368 | ||
4369 | if ($conf->{$opt}) { | |
4370 | my $oldnet = parse_net($conf->{$opt}); | |
4371 | ||
4372 | if (&$safe_string_ne($oldnet->{model}, $newnet->{model}) || | |
4373 | &$safe_string_ne($oldnet->{macaddr}, $newnet->{macaddr}) || | |
4374 | &$safe_num_ne($oldnet->{queues}, $newnet->{queues}) || | |
4375 | !($newnet->{bridge} && $oldnet->{bridge})) { # bridge/nat mode change | |
4376 | ||
4377 | # for non online change, we try to hot-unplug | |
7196b757 | 4378 | die "skip\n" if !$hotplug; |
3eec5767 DM |
4379 | vm_deviceunplug($vmid, $conf, $opt); |
4380 | } else { | |
4381 | ||
4382 | die "internal error" if $opt !~ m/net(\d+)/; | |
4383 | my $iface = "tap${vmid}i$1"; | |
a1b7d579 | 4384 | |
25088687 DM |
4385 | if (&$safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) || |
4386 | &$safe_num_ne($oldnet->{tag}, $newnet->{tag}) || | |
16d08ecf | 4387 | &$safe_string_ne($oldnet->{trunks}, $newnet->{trunks}) || |
25088687 | 4388 | &$safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) { |
3eec5767 | 4389 | PVE::Network::tap_unplug($iface); |
4f4fbeb0 WB |
4390 | PVE::Network::tap_plug($iface, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate}); |
4391 | } elsif (&$safe_num_ne($oldnet->{rate}, $newnet->{rate})) { | |
4392 | # Rate can be applied on its own but any change above needs to | |
4393 | # include the rate in tap_plug since OVS resets everything. | |
4394 | PVE::Network::tap_rate_limit($iface, $newnet->{rate}); | |
3eec5767 | 4395 | } |
38c590d9 | 4396 | |
25088687 DM |
4397 | if (&$safe_string_ne($oldnet->{link_down}, $newnet->{link_down})) { |
4398 | qemu_set_link_status($vmid, $opt, !$newnet->{link_down}); | |
4399 | } | |
4400 | ||
38c590d9 | 4401 | return 1; |
3eec5767 DM |
4402 | } |
4403 | } | |
a1b7d579 | 4404 | |
7196b757 | 4405 | if ($hotplug) { |
38c590d9 DM |
4406 | vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet); |
4407 | } else { | |
4408 | die "skip\n"; | |
4409 | } | |
3eec5767 DM |
4410 | } |
4411 | ||
a05cff86 | 4412 | sub vmconfig_update_disk { |
b3c2bdd1 | 4413 | my ($storecfg, $conf, $hotplug, $vmid, $opt, $value, $force) = @_; |
a05cff86 DM |
4414 | |
4415 | # fixme: do we need force? | |
4416 | ||
4417 | my $drive = parse_drive($opt, $value); | |
4418 | ||
4419 | if ($conf->{$opt}) { | |
4420 | ||
4421 | if (my $old_drive = parse_drive($opt, $conf->{$opt})) { | |
4422 | ||
4423 | my $media = $drive->{media} || 'disk'; | |
4424 | my $oldmedia = $old_drive->{media} || 'disk'; | |
4425 | die "unable to change media type\n" if $media ne $oldmedia; | |
4426 | ||
4427 | if (!drive_is_cdrom($old_drive)) { | |
4428 | ||
a1b7d579 | 4429 | if ($drive->{file} ne $old_drive->{file}) { |
a05cff86 | 4430 | |
7196b757 | 4431 | die "skip\n" if !$hotplug; |
a05cff86 DM |
4432 | |
4433 | # unplug and register as unused | |
4434 | vm_deviceunplug($vmid, $conf, $opt); | |
4435 | vmconfig_register_unused_drive($storecfg, $vmid, $conf, $old_drive) | |
a1b7d579 | 4436 | |
a05cff86 DM |
4437 | } else { |
4438 | # update existing disk | |
4439 | ||
4440 | # skip non hotpluggable value | |
a1b7d579 | 4441 | if (&$safe_num_ne($drive->{discard}, $old_drive->{discard}) || |
22de899a | 4442 | &$safe_string_ne($drive->{iothread}, $old_drive->{iothread}) || |
6e11f143 | 4443 | &$safe_string_ne($drive->{queues}, $old_drive->{queues}) || |
a05cff86 DM |
4444 | &$safe_string_ne($drive->{cache}, $old_drive->{cache})) { |
4445 | die "skip\n"; | |
4446 | } | |
4447 | ||
4448 | # apply throttle | |
4449 | if (&$safe_num_ne($drive->{mbps}, $old_drive->{mbps}) || | |
4450 | &$safe_num_ne($drive->{mbps_rd}, $old_drive->{mbps_rd}) || | |
4451 | &$safe_num_ne($drive->{mbps_wr}, $old_drive->{mbps_wr}) || | |
4452 | &$safe_num_ne($drive->{iops}, $old_drive->{iops}) || | |
4453 | &$safe_num_ne($drive->{iops_rd}, $old_drive->{iops_rd}) || | |
4454 | &$safe_num_ne($drive->{iops_wr}, $old_drive->{iops_wr}) || | |
4455 | &$safe_num_ne($drive->{mbps_max}, $old_drive->{mbps_max}) || | |
4456 | &$safe_num_ne($drive->{mbps_rd_max}, $old_drive->{mbps_rd_max}) || | |
4457 | &$safe_num_ne($drive->{mbps_wr_max}, $old_drive->{mbps_wr_max}) || | |
4458 | &$safe_num_ne($drive->{iops_max}, $old_drive->{iops_max}) || | |
4459 | &$safe_num_ne($drive->{iops_rd_max}, $old_drive->{iops_rd_max}) || | |
4460 | &$safe_num_ne($drive->{iops_wr_max}, $old_drive->{iops_wr_max})) { | |
a1b7d579 | 4461 | |
a05cff86 DM |
4462 | qemu_block_set_io_throttle($vmid,"drive-$opt", |
4463 | ($drive->{mbps} || 0)*1024*1024, | |
4464 | ($drive->{mbps_rd} || 0)*1024*1024, | |
4465 | ($drive->{mbps_wr} || 0)*1024*1024, | |
4466 | $drive->{iops} || 0, | |
4467 | $drive->{iops_rd} || 0, | |
4468 | $drive->{iops_wr} || 0, | |
4469 | ($drive->{mbps_max} || 0)*1024*1024, | |
4470 | ($drive->{mbps_rd_max} || 0)*1024*1024, | |
4471 | ($drive->{mbps_wr_max} || 0)*1024*1024, | |
4472 | $drive->{iops_max} || 0, | |
4473 | $drive->{iops_rd_max} || 0, | |
4474 | $drive->{iops_wr_max} || 0); | |
4475 | ||
4476 | } | |
a1b7d579 | 4477 | |
a05cff86 DM |
4478 | return 1; |
4479 | } | |
4de1bb25 DM |
4480 | |
4481 | } else { # cdrom | |
a1b7d579 | 4482 | |
4de1bb25 DM |
4483 | if ($drive->{file} eq 'none') { |
4484 | vm_mon_cmd($vmid, "eject",force => JSON::true,device => "drive-$opt"); | |
4485 | } else { | |
4486 | my $path = get_iso_path($storecfg, $vmid, $drive->{file}); | |
4487 | vm_mon_cmd($vmid, "eject", force => JSON::true,device => "drive-$opt"); # force eject if locked | |
4488 | vm_mon_cmd($vmid, "change", device => "drive-$opt",target => "$path") if $path; | |
4489 | } | |
a1b7d579 | 4490 | |
34758d66 | 4491 | return 1; |
a05cff86 DM |
4492 | } |
4493 | } | |
4494 | } | |
4495 | ||
a1b7d579 | 4496 | die "skip\n" if !$hotplug || $opt =~ m/(ide|sata)(\d+)/; |
4de1bb25 | 4497 | # hotplug new disks |
f7b4356f | 4498 | PVE::Storage::activate_volumes($storecfg, [$drive->{file}]) if $drive->{file} !~ m|^/dev/.+|; |
4de1bb25 | 4499 | vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive); |
a05cff86 DM |
4500 | } |
4501 | ||
1e3baf05 | 4502 | sub vm_start { |
ba9e1000 DM |
4503 | my ($storecfg, $vmid, $statefile, $skiplock, $migratedfrom, $paused, |
4504 | $forcemachine, $spice_ticket) = @_; | |
1e3baf05 | 4505 | |
ffda963f FG |
4506 | PVE::QemuConfig->lock_config($vmid, sub { |
4507 | my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom); | |
1e3baf05 | 4508 | |
ffda963f | 4509 | die "you can't start a vm if it's a template\n" if PVE::QemuConfig->is_template($conf); |
3dcb98d5 | 4510 | |
ffda963f | 4511 | PVE::QemuConfig->check_lock($conf) if !$skiplock; |
1e3baf05 | 4512 | |
7e8dcf2c | 4513 | die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom); |
1e3baf05 | 4514 | |
055d554d | 4515 | if (!$statefile && scalar(keys %{$conf->{pending}})) { |
3a11fadb | 4516 | vmconfig_apply_pending($vmid, $conf, $storecfg); |
ffda963f | 4517 | $conf = PVE::QemuConfig->load_config($vmid); # update/reload |
055d554d DM |
4518 | } |
4519 | ||
6c47d546 DM |
4520 | my $defaults = load_defaults(); |
4521 | ||
4522 | # set environment variable useful inside network script | |
4523 | $ENV{PVE_MIGRATED_FROM} = $migratedfrom if $migratedfrom; | |
4524 | ||
67812f9c | 4525 | my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid, $conf, $defaults, $forcemachine); |
6c47d546 | 4526 | |
1e3baf05 | 4527 | my $migrate_port = 0; |
5bc1e039 | 4528 | my $migrate_uri; |
1e3baf05 DM |
4529 | if ($statefile) { |
4530 | if ($statefile eq 'tcp') { | |
5bc1e039 SP |
4531 | my $localip = "localhost"; |
4532 | my $datacenterconf = PVE::Cluster::cfs_read_file('datacenter.cfg'); | |
af0eba7e | 4533 | my $nodename = PVE::INotify::nodename(); |
5bc1e039 | 4534 | if ($datacenterconf->{migration_unsecure}) { |
5bc1e039 | 4535 | $localip = PVE::Cluster::remote_node_ip($nodename, 1); |
407e0b8b | 4536 | $localip = "[$localip]" if Net::IP::ip_is_ipv6($localip); |
5bc1e039 | 4537 | } |
af0eba7e WB |
4538 | my $pfamily = PVE::Tools::get_host_address_family($nodename); |
4539 | $migrate_port = PVE::Tools::next_migrate_port($pfamily); | |
407e0b8b | 4540 | $migrate_uri = "tcp:${localip}:${migrate_port}"; |
6c47d546 DM |
4541 | push @$cmd, '-incoming', $migrate_uri; |
4542 | push @$cmd, '-S'; | |
1e3baf05 | 4543 | } else { |
6c47d546 | 4544 | push @$cmd, '-loadstate', $statefile; |
1e3baf05 | 4545 | } |
91bd6c90 DM |
4546 | } elsif ($paused) { |
4547 | push @$cmd, '-S'; | |
1e3baf05 DM |
4548 | } |
4549 | ||
1e3baf05 | 4550 | # host pci devices |
040b06b7 DA |
4551 | for (my $i = 0; $i < $MAX_HOSTPCI_DEVICES; $i++) { |
4552 | my $d = parse_hostpci($conf->{"hostpci$i"}); | |
4553 | next if !$d; | |
b1f72af6 AD |
4554 | my $pcidevices = $d->{pciid}; |
4555 | foreach my $pcidevice (@$pcidevices) { | |
4556 | my $pciid = $pcidevice->{id}.".".$pcidevice->{function}; | |
000fc0a2 | 4557 | |
b1f72af6 AD |
4558 | my $info = pci_device_info("0000:$pciid"); |
4559 | die "IOMMU not present\n" if !check_iommu_support(); | |
4560 | die "no pci device info for device '$pciid'\n" if !$info; | |
6ea8cd3b | 4561 | die "can't unbind/bind pci group to vfio '$pciid'\n" if !pci_dev_group_bind_to_vfio($pciid); |
8f3e88af | 4562 | die "can't reset pci device '$pciid'\n" if $info->{has_fl_reset} and !pci_dev_reset($info); |
b1f72af6 | 4563 | } |
040b06b7 | 4564 | } |
1e3baf05 DM |
4565 | |
4566 | PVE::Storage::activate_volumes($storecfg, $vollist); | |
4567 | ||
2b401189 AD |
4568 | if (!check_running($vmid, 1) && -d "/sys/fs/cgroup/systemd/qemu.slice/$vmid.scope") { |
4569 | my $cmd = []; | |
4570 | push @$cmd, '/bin/systemctl', 'stop', "$vmid.scope"; | |
4571 | eval { run_command($cmd); }; | |
4572 | } | |
4573 | ||
585b6e28 DM |
4574 | eval { run_command($cmd, timeout => $statefile ? undef : 30, |
4575 | umask => 0077); }; | |
77cde36b DC |
4576 | |
4577 | if (my $err = $@) { | |
4578 | # deactivate volumes if start fails | |
4579 | eval { PVE::Storage::deactivate_volumes($storecfg, $vollist); }; | |
4580 | die "start failed: $err"; | |
4581 | } | |
1e3baf05 | 4582 | |
5bc1e039 | 4583 | print "migration listens on $migrate_uri\n" if $migrate_uri; |
afdb31d5 | 4584 | |
8c609afd | 4585 | if ($statefile && $statefile ne 'tcp') { |
95381ce0 | 4586 | eval { vm_mon_cmd_nocheck($vmid, "cont"); }; |
8c609afd | 4587 | warn $@ if $@; |
62de2cbd DM |
4588 | } |
4589 | ||
1d794448 | 4590 | if ($migratedfrom) { |
a89fded1 AD |
4591 | |
4592 | eval { | |
8e90138a | 4593 | set_migration_caps($vmid); |
a89fded1 | 4594 | }; |
1d794448 | 4595 | warn $@ if $@; |
a89fded1 | 4596 | |
1d794448 DM |
4597 | if ($spice_port) { |
4598 | print "spice listens on port $spice_port\n"; | |
4599 | if ($spice_ticket) { | |
8e90138a DM |
4600 | vm_mon_cmd_nocheck($vmid, "set_password", protocol => 'spice', password => $spice_ticket); |
4601 | vm_mon_cmd_nocheck($vmid, "expire_password", protocol => 'spice', time => "+30"); | |
95a4b4a9 AD |
4602 | } |
4603 | } | |
4604 | ||
1d794448 | 4605 | } else { |
4ec05c4c | 4606 | |
15b1fc93 | 4607 | if (!$statefile && (!defined($conf->{balloon}) || $conf->{balloon})) { |
be190583 | 4608 | vm_mon_cmd_nocheck($vmid, "balloon", value => $conf->{balloon}*1024*1024) |
4ec05c4c | 4609 | if $conf->{balloon}; |
4ec05c4c | 4610 | } |
25088687 DM |
4611 | |
4612 | foreach my $opt (keys %$conf) { | |
4613 | next if $opt !~ m/^net\d+$/; | |
4614 | my $nicconf = parse_net($conf->{$opt}); | |
4615 | qemu_set_link_status($vmid, $opt, 0) if $nicconf->{link_down}; | |
4616 | } | |
e18b0b99 | 4617 | } |
a1b7d579 | 4618 | |
eb065317 AD |
4619 | vm_mon_cmd_nocheck($vmid, 'qom-set', |
4620 | path => "machine/peripheral/balloon0", | |
4621 | property => "guest-stats-polling-interval", | |
4622 | value => 2) if (!defined($conf->{balloon}) || $conf->{balloon}); | |
4623 | ||
1e3baf05 DM |
4624 | }); |
4625 | } | |
4626 | ||
0eedc444 AD |
4627 | sub vm_mon_cmd { |
4628 | my ($vmid, $execute, %params) = @_; | |
4629 | ||
26f11676 DM |
4630 | my $cmd = { execute => $execute, arguments => \%params }; |
4631 | vm_qmp_command($vmid, $cmd); | |
0eedc444 AD |
4632 | } |
4633 | ||
4634 | sub vm_mon_cmd_nocheck { | |
4635 | my ($vmid, $execute, %params) = @_; | |
4636 | ||
26f11676 DM |
4637 | my $cmd = { execute => $execute, arguments => \%params }; |
4638 | vm_qmp_command($vmid, $cmd, 1); | |
0eedc444 AD |
4639 | } |
4640 | ||
c971c4f2 | 4641 | sub vm_qmp_command { |
c5a07de5 | 4642 | my ($vmid, $cmd, $nocheck) = @_; |
97d62eb7 | 4643 | |
c971c4f2 | 4644 | my $res; |
26f11676 | 4645 | |
14db5366 DM |
4646 | my $timeout; |
4647 | if ($cmd->{arguments} && $cmd->{arguments}->{timeout}) { | |
4648 | $timeout = $cmd->{arguments}->{timeout}; | |
4649 | delete $cmd->{arguments}->{timeout}; | |
4650 | } | |
be190583 | 4651 | |
c971c4f2 AD |
4652 | eval { |
4653 | die "VM $vmid not running\n" if !check_running($vmid, $nocheck); | |
7a6c2150 DM |
4654 | my $sname = qmp_socket($vmid); |
4655 | if (-e $sname) { # test if VM is reasonambe new and supports qmp/qga | |
c5a07de5 | 4656 | my $qmpclient = PVE::QMPClient->new(); |
dab36e1e | 4657 | |
14db5366 | 4658 | $res = $qmpclient->cmd($vmid, $cmd, $timeout); |
c5a07de5 | 4659 | } elsif (-e "${var_run_tmpdir}/$vmid.mon") { |
dab36e1e DM |
4660 | die "can't execute complex command on old monitor - stop/start your vm to fix the problem\n" |
4661 | if scalar(%{$cmd->{arguments}}); | |
4662 | vm_monitor_command($vmid, $cmd->{execute}, $nocheck); | |
4663 | } else { | |
4664 | die "unable to open monitor socket\n"; | |
4665 | } | |
c971c4f2 | 4666 | }; |
26f11676 | 4667 | if (my $err = $@) { |
c971c4f2 AD |
4668 | syslog("err", "VM $vmid qmp command failed - $err"); |
4669 | die $err; | |
4670 | } | |
4671 | ||
4672 | return $res; | |
4673 | } | |
4674 | ||
9df5cbcc DM |
4675 | sub vm_human_monitor_command { |
4676 | my ($vmid, $cmdline) = @_; | |
4677 | ||
4678 | my $res; | |
4679 | ||
f5eb281a | 4680 | my $cmd = { |
9df5cbcc DM |
4681 | execute => 'human-monitor-command', |
4682 | arguments => { 'command-line' => $cmdline}, | |
4683 | }; | |
4684 | ||
4685 | return vm_qmp_command($vmid, $cmd); | |
4686 | } | |
4687 | ||
1e3baf05 DM |
4688 | sub vm_commandline { |
4689 | my ($storecfg, $vmid) = @_; | |
4690 | ||
ffda963f | 4691 | my $conf = PVE::QemuConfig->load_config($vmid); |
1e3baf05 DM |
4692 | |
4693 | my $defaults = load_defaults(); | |
4694 | ||
6b64503e | 4695 | my $cmd = config_to_command($storecfg, $vmid, $conf, $defaults); |
1e3baf05 | 4696 | |
6b64503e | 4697 | return join(' ', @$cmd); |
1e3baf05 DM |
4698 | } |
4699 | ||
4700 | sub vm_reset { | |
4701 | my ($vmid, $skiplock) = @_; | |
4702 | ||
ffda963f | 4703 | PVE::QemuConfig->lock_config($vmid, sub { |
1e3baf05 | 4704 | |
ffda963f | 4705 | my $conf = PVE::QemuConfig->load_config($vmid); |
1e3baf05 | 4706 | |
ffda963f | 4707 | PVE::QemuConfig->check_lock($conf) if !$skiplock; |
1e3baf05 | 4708 | |
816e2c4a | 4709 | vm_mon_cmd($vmid, "system_reset"); |
ff1a2432 DM |
4710 | }); |
4711 | } | |
4712 | ||
4713 | sub get_vm_volumes { | |
4714 | my ($conf) = @_; | |
1e3baf05 | 4715 | |
ff1a2432 | 4716 | my $vollist = []; |
d5769dc2 DM |
4717 | foreach_volid($conf, sub { |
4718 | my ($volid, $is_cdrom) = @_; | |
ff1a2432 | 4719 | |
d5769dc2 | 4720 | return if $volid =~ m|^/|; |
ff1a2432 | 4721 | |
d5769dc2 DM |
4722 | my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1); |
4723 | return if !$sid; | |
ff1a2432 DM |
4724 | |
4725 | push @$vollist, $volid; | |
1e3baf05 | 4726 | }); |
ff1a2432 DM |
4727 | |
4728 | return $vollist; | |
4729 | } | |
4730 | ||
4731 | sub vm_stop_cleanup { | |
70b04821 | 4732 | my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_; |
ff1a2432 | 4733 | |
745fed70 | 4734 | eval { |
ff1a2432 | 4735 | |
254575e9 DM |
4736 | if (!$keepActive) { |
4737 | my $vollist = get_vm_volumes($conf); | |
4738 | PVE::Storage::deactivate_volumes($storecfg, $vollist); | |
4739 | } | |
a1b7d579 | 4740 | |
ab6a046f | 4741 | foreach my $ext (qw(mon qmp pid vnc qga)) { |
961bfcb2 DM |
4742 | unlink "/var/run/qemu-server/${vmid}.$ext"; |
4743 | } | |
a1b7d579 | 4744 | |
70b04821 | 4745 | vmconfig_apply_pending($vmid, $conf, $storecfg) if $apply_pending_changes; |
745fed70 DM |
4746 | }; |
4747 | warn $@ if $@; # avoid errors - just warn | |
1e3baf05 DM |
4748 | } |
4749 | ||
e6c3b671 | 4750 | # Note: use $nockeck to skip tests if VM configuration file exists. |
254575e9 DM |
4751 | # We need that when migration VMs to other nodes (files already moved) |
4752 | # Note: we set $keepActive in vzdump stop mode - volumes need to stay active | |
1e3baf05 | 4753 | sub vm_stop { |
af30308f | 4754 | my ($storecfg, $vmid, $skiplock, $nocheck, $timeout, $shutdown, $force, $keepActive, $migratedfrom) = @_; |
9269013a | 4755 | |
9269013a | 4756 | $force = 1 if !defined($force) && !$shutdown; |
1e3baf05 | 4757 | |
af30308f DM |
4758 | if ($migratedfrom){ |
4759 | my $pid = check_running($vmid, $nocheck, $migratedfrom); | |
4760 | kill 15, $pid if $pid; | |
ffda963f | 4761 | my $conf = PVE::QemuConfig->load_config($vmid, $migratedfrom); |
70b04821 | 4762 | vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 0); |
af30308f DM |
4763 | return; |
4764 | } | |
4765 | ||
ffda963f | 4766 | PVE::QemuConfig->lock_config($vmid, sub { |
1e3baf05 | 4767 | |
e6c3b671 | 4768 | my $pid = check_running($vmid, $nocheck); |
ff1a2432 | 4769 | return if !$pid; |
1e3baf05 | 4770 | |
ff1a2432 | 4771 | my $conf; |
e6c3b671 | 4772 | if (!$nocheck) { |
ffda963f FG |
4773 | $conf = PVE::QemuConfig->load_config($vmid); |
4774 | PVE::QemuConfig->check_lock($conf) if !$skiplock; | |
7f4a5b5a | 4775 | if (!defined($timeout) && $shutdown && $conf->{startup}) { |
38f7f26c | 4776 | my $opts = PVE::JSONSchema::pve_parse_startup_order($conf->{startup}); |
7f4a5b5a DM |
4777 | $timeout = $opts->{down} if $opts->{down}; |
4778 | } | |
e6c3b671 | 4779 | } |
19672434 | 4780 | |
7f4a5b5a | 4781 | $timeout = 60 if !defined($timeout); |
67fb9de6 | 4782 | |
9269013a DM |
4783 | eval { |
4784 | if ($shutdown) { | |
fbda7965 | 4785 | if (defined($conf) && $conf->{agent}) { |
2ea54503 | 4786 | vm_qmp_command($vmid, { execute => "guest-shutdown" }, $nocheck); |
1c0c1c17 | 4787 | } else { |
2ea54503 | 4788 | vm_qmp_command($vmid, { execute => "system_powerdown" }, $nocheck); |
1c0c1c17 | 4789 | } |
9269013a | 4790 | } else { |
2ea54503 | 4791 | vm_qmp_command($vmid, { execute => "quit" }, $nocheck); |
afdb31d5 | 4792 | } |
9269013a | 4793 | }; |
1e3baf05 DM |
4794 | my $err = $@; |
4795 | ||
4796 | if (!$err) { | |
1e3baf05 | 4797 | my $count = 0; |
e6c3b671 | 4798 | while (($count < $timeout) && check_running($vmid, $nocheck)) { |
1e3baf05 DM |
4799 | $count++; |
4800 | sleep 1; | |
4801 | } | |
4802 | ||
4803 | if ($count >= $timeout) { | |
9269013a DM |
4804 | if ($force) { |
4805 | warn "VM still running - terminating now with SIGTERM\n"; | |
4806 | kill 15, $pid; | |
4807 | } else { | |
4808 | die "VM quit/powerdown failed - got timeout\n"; | |
4809 | } | |
4810 | } else { | |
70b04821 | 4811 | vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 1) if $conf; |
9269013a | 4812 | return; |
1e3baf05 DM |
4813 | } |
4814 | } else { | |
9269013a DM |
4815 | if ($force) { |
4816 | warn "VM quit/powerdown failed - terminating now with SIGTERM\n"; | |
4817 | kill 15, $pid; | |
4818 | } else { | |
afdb31d5 | 4819 | die "VM quit/powerdown failed\n"; |
9269013a | 4820 | } |
1e3baf05 DM |
4821 | } |
4822 | ||
4823 | # wait again | |
ff1a2432 | 4824 | $timeout = 10; |
1e3baf05 DM |
4825 | |
4826 | my $count = 0; | |
e6c3b671 | 4827 | while (($count < $timeout) && check_running($vmid, $nocheck)) { |
1e3baf05 DM |
4828 | $count++; |
4829 | sleep 1; | |
4830 | } | |
4831 | ||
4832 | if ($count >= $timeout) { | |
ff1a2432 | 4833 | warn "VM still running - terminating now with SIGKILL\n"; |
1e3baf05 | 4834 | kill 9, $pid; |
ff1a2432 | 4835 | sleep 1; |
1e3baf05 DM |
4836 | } |
4837 | ||
70b04821 | 4838 | vm_stop_cleanup($storecfg, $vmid, $conf, $keepActive, 1) if $conf; |
ff1a2432 | 4839 | }); |
1e3baf05 DM |
4840 | } |
4841 | ||
4842 | sub vm_suspend { | |
4843 | my ($vmid, $skiplock) = @_; | |
4844 | ||
ffda963f | 4845 | PVE::QemuConfig->lock_config($vmid, sub { |
1e3baf05 | 4846 | |
ffda963f | 4847 | my $conf = PVE::QemuConfig->load_config($vmid); |
1e3baf05 | 4848 | |
e79706d4 FG |
4849 | PVE::QemuConfig->check_lock($conf) |
4850 | if !($skiplock || PVE::QemuConfig->has_lock($conf, 'backup')); | |
bcb7c9cf | 4851 | |
f77f91f3 | 4852 | vm_mon_cmd($vmid, "stop"); |
1e3baf05 DM |
4853 | }); |
4854 | } | |
4855 | ||
4856 | sub vm_resume { | |
289e0b85 | 4857 | my ($vmid, $skiplock, $nocheck) = @_; |
1e3baf05 | 4858 | |
ffda963f | 4859 | PVE::QemuConfig->lock_config($vmid, sub { |
1e3baf05 | 4860 | |
289e0b85 | 4861 | if (!$nocheck) { |
1e3baf05 | 4862 | |
ffda963f | 4863 | my $conf = PVE::QemuConfig->load_config($vmid); |
1e3baf05 | 4864 | |
e79706d4 FG |
4865 | PVE::QemuConfig->check_lock($conf) |
4866 | if !($skiplock || PVE::QemuConfig->has_lock($conf, 'backup')); | |
289e0b85 AD |
4867 | |
4868 | vm_mon_cmd($vmid, "cont"); | |
4869 | ||
4870 | } else { | |
4871 | vm_mon_cmd_nocheck($vmid, "cont"); | |
4872 | } | |
1e3baf05 DM |
4873 | }); |
4874 | } | |
4875 | ||
5fdbe4f0 DM |
4876 | sub vm_sendkey { |
4877 | my ($vmid, $skiplock, $key) = @_; | |
1e3baf05 | 4878 | |
ffda963f | 4879 | PVE::QemuConfig->lock_config($vmid, sub { |
1e3baf05 | 4880 | |
ffda963f | 4881 | my $conf = PVE::QemuConfig->load_config($vmid); |
f5eb281a | 4882 | |
7b7c6d1b DM |
4883 | # there is no qmp command, so we use the human monitor command |
4884 | vm_human_monitor_command($vmid, "sendkey $key"); | |
1e3baf05 DM |
4885 | }); |
4886 | } | |
4887 | ||
4888 | sub vm_destroy { | |
4889 | my ($storecfg, $vmid, $skiplock) = @_; | |
4890 | ||
ffda963f | 4891 | PVE::QemuConfig->lock_config($vmid, sub { |
1e3baf05 | 4892 | |
ffda963f | 4893 | my $conf = PVE::QemuConfig->load_config($vmid); |
1e3baf05 | 4894 | |
ff1a2432 | 4895 | if (!check_running($vmid)) { |
15cc8784 | 4896 | destroy_vm($storecfg, $vmid, undef, $skiplock); |
ff1a2432 DM |
4897 | } else { |
4898 | die "VM $vmid is running - destroy failed\n"; | |
1e3baf05 DM |
4899 | } |
4900 | }); | |
4901 | } | |
4902 | ||
1e3baf05 DM |
4903 | # pci helpers |
4904 | ||
4905 | sub file_write { | |
4906 | my ($filename, $buf) = @_; | |
4907 | ||
6b64503e | 4908 | my $fh = IO::File->new($filename, "w"); |
1e3baf05 DM |
4909 | return undef if !$fh; |
4910 | ||
4911 | my $res = print $fh $buf; | |
4912 | ||
4913 | $fh->close(); | |
4914 | ||
4915 | return $res; | |
4916 | } | |
4917 | ||
4918 | sub pci_device_info { | |
4919 | my ($name) = @_; | |
4920 | ||
4921 | my $res; | |
4922 | ||
4923 | return undef if $name !~ m/^([a-f0-9]{4}):([a-f0-9]{2}):([a-f0-9]{2})\.([a-f0-9])$/; | |
4924 | my ($domain, $bus, $slot, $func) = ($1, $2, $3, $4); | |
4925 | ||
4926 | my $irq = file_read_firstline("$pcisysfs/devices/$name/irq"); | |
4927 | return undef if !defined($irq) || $irq !~ m/^\d+$/; | |
4928 | ||
4929 | my $vendor = file_read_firstline("$pcisysfs/devices/$name/vendor"); | |
4930 | return undef if !defined($vendor) || $vendor !~ s/^0x//; | |
4931 | ||
4932 | my $product = file_read_firstline("$pcisysfs/devices/$name/device"); | |
4933 | return undef if !defined($product) || $product !~ s/^0x//; | |
4934 | ||
4935 | $res = { | |
4936 | name => $name, | |
4937 | vendor => $vendor, | |
4938 | product => $product, | |
4939 | domain => $domain, | |
4940 | bus => $bus, | |
4941 | slot => $slot, | |
4942 | func => $func, | |
4943 | irq => $irq, | |
4944 | has_fl_reset => -f "$pcisysfs/devices/$name/reset" || 0, | |
4945 | }; | |
4946 | ||
4947 | return $res; | |
4948 | } | |
4949 | ||
4950 | sub pci_dev_reset { | |
4951 | my ($dev) = @_; | |
4952 | ||
4953 | my $name = $dev->{name}; | |
4954 | ||
4955 | my $fn = "$pcisysfs/devices/$name/reset"; | |
4956 | ||
6b64503e | 4957 | return file_write($fn, "1"); |
1e3baf05 DM |
4958 | } |
4959 | ||
000fc0a2 SP |
4960 | sub pci_dev_bind_to_vfio { |
4961 | my ($dev) = @_; | |
4962 | ||
4963 | my $name = $dev->{name}; | |
4964 | ||
4965 | my $vfio_basedir = "$pcisysfs/drivers/vfio-pci"; | |
4966 | ||
4967 | if (!-d $vfio_basedir) { | |
4968 | system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null"); | |
4969 | } | |
4970 | die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir; | |
4971 | ||
4972 | my $testdir = "$vfio_basedir/$name"; | |
4973 | return 1 if -d $testdir; | |
4974 | ||
4975 | my $data = "$dev->{vendor} $dev->{product}"; | |
4976 | return undef if !file_write("$vfio_basedir/new_id", $data); | |
4977 | ||
4978 | my $fn = "$pcisysfs/devices/$name/driver/unbind"; | |
4979 | if (!file_write($fn, $name)) { | |
4980 | return undef if -f $fn; | |
4981 | } | |
4982 | ||
4983 | $fn = "$vfio_basedir/bind"; | |
4984 | if (! -d $testdir) { | |
4985 | return undef if !file_write($fn, $name); | |
4986 | } | |
4987 | ||
4988 | return -d $testdir; | |
4989 | } | |
4990 | ||
4991 | sub pci_dev_group_bind_to_vfio { | |
4992 | my ($pciid) = @_; | |
4993 | ||
4994 | my $vfio_basedir = "$pcisysfs/drivers/vfio-pci"; | |
4995 | ||
4996 | if (!-d $vfio_basedir) { | |
4997 | system("/sbin/modprobe vfio-pci >/dev/null 2>/dev/null"); | |
4998 | } | |
4999 | die "Cannot find vfio-pci module!\n" if !-d $vfio_basedir; | |
5000 | ||
5001 | # get IOMMU group devices | |
5002 | opendir(my $D, "$pcisysfs/devices/0000:$pciid/iommu_group/devices/") || die "Cannot open iommu_group: $!\n"; | |
5003 | my @devs = grep /^0000:/, readdir($D); | |
5004 | closedir($D); | |
5005 | ||
5006 | foreach my $pciid (@devs) { | |
5007 | $pciid =~ m/^([:\.\da-f]+)$/ or die "PCI ID $pciid not valid!\n"; | |
f8fa2ed7 SP |
5008 | |
5009 | # pci bridges, switches or root ports are not supported | |
5010 | # they have a pci_bus subdirectory so skip them | |
5011 | next if (-e "$pcisysfs/devices/$pciid/pci_bus"); | |
5012 | ||
000fc0a2 SP |
5013 | my $info = pci_device_info($1); |
5014 | pci_dev_bind_to_vfio($info) || die "Cannot bind $pciid to vfio\n"; | |
5015 | } | |
5016 | ||
5017 | return 1; | |
5018 | } | |
5019 | ||
afdb31d5 | 5020 | sub print_pci_addr { |
5bdcf937 | 5021 | my ($id, $bridges) = @_; |
6b64503e | 5022 | |
72a063e4 | 5023 | my $res = ''; |
6b64503e | 5024 | my $devices = { |
24f0d39a | 5025 | piix3 => { bus => 0, addr => 1 }, |
e5f7f8ed | 5026 | #addr2 : first videocard |
13b5a753 | 5027 | balloon0 => { bus => 0, addr => 3 }, |
0a40e8ea | 5028 | watchdog => { bus => 0, addr => 4 }, |
a1b7d579 | 5029 | scsihw0 => { bus => 0, addr => 5 }, |
6731a4cf | 5030 | 'pci.3' => { bus => 0, addr => 5 }, #can also be used for virtio-scsi-single bridge |
cdd20088 | 5031 | scsihw1 => { bus => 0, addr => 6 }, |
26ee04b6 | 5032 | ahci0 => { bus => 0, addr => 7 }, |
ab6a046f | 5033 | qga0 => { bus => 0, addr => 8 }, |
1011b570 | 5034 | spice => { bus => 0, addr => 9 }, |
6b64503e DM |
5035 | virtio0 => { bus => 0, addr => 10 }, |
5036 | virtio1 => { bus => 0, addr => 11 }, | |
5037 | virtio2 => { bus => 0, addr => 12 }, | |
5038 | virtio3 => { bus => 0, addr => 13 }, | |
5039 | virtio4 => { bus => 0, addr => 14 }, | |
5040 | virtio5 => { bus => 0, addr => 15 }, | |
b78ebef7 DA |
5041 | hostpci0 => { bus => 0, addr => 16 }, |
5042 | hostpci1 => { bus => 0, addr => 17 }, | |
f290f8d9 DA |
5043 | net0 => { bus => 0, addr => 18 }, |
5044 | net1 => { bus => 0, addr => 19 }, | |
5045 | net2 => { bus => 0, addr => 20 }, | |
5046 | net3 => { bus => 0, addr => 21 }, | |
5047 | net4 => { bus => 0, addr => 22 }, | |
5048 | net5 => { bus => 0, addr => 23 }, | |
2fa3151e AD |
5049 | vga1 => { bus => 0, addr => 24 }, |
5050 | vga2 => { bus => 0, addr => 25 }, | |
5051 | vga3 => { bus => 0, addr => 26 }, | |
5cffb2d2 AD |
5052 | hostpci2 => { bus => 0, addr => 27 }, |
5053 | hostpci3 => { bus => 0, addr => 28 }, | |
e5f7f8ed | 5054 | #addr29 : usb-host (pve-usb.cfg) |
5bdcf937 AD |
5055 | 'pci.1' => { bus => 0, addr => 30 }, |
5056 | 'pci.2' => { bus => 0, addr => 31 }, | |
5057 | 'net6' => { bus => 1, addr => 1 }, | |
5058 | 'net7' => { bus => 1, addr => 2 }, | |
5059 | 'net8' => { bus => 1, addr => 3 }, | |
5060 | 'net9' => { bus => 1, addr => 4 }, | |
5061 | 'net10' => { bus => 1, addr => 5 }, | |
5062 | 'net11' => { bus => 1, addr => 6 }, | |
5063 | 'net12' => { bus => 1, addr => 7 }, | |
5064 | 'net13' => { bus => 1, addr => 8 }, | |
5065 | 'net14' => { bus => 1, addr => 9 }, | |
5066 | 'net15' => { bus => 1, addr => 10 }, | |
5067 | 'net16' => { bus => 1, addr => 11 }, | |
5068 | 'net17' => { bus => 1, addr => 12 }, | |
5069 | 'net18' => { bus => 1, addr => 13 }, | |
5070 | 'net19' => { bus => 1, addr => 14 }, | |
5071 | 'net20' => { bus => 1, addr => 15 }, | |
5072 | 'net21' => { bus => 1, addr => 16 }, | |
5073 | 'net22' => { bus => 1, addr => 17 }, | |
5074 | 'net23' => { bus => 1, addr => 18 }, | |
5075 | 'net24' => { bus => 1, addr => 19 }, | |
5076 | 'net25' => { bus => 1, addr => 20 }, | |
5077 | 'net26' => { bus => 1, addr => 21 }, | |
5078 | 'net27' => { bus => 1, addr => 22 }, | |
5079 | 'net28' => { bus => 1, addr => 23 }, | |
5080 | 'net29' => { bus => 1, addr => 24 }, | |
5081 | 'net30' => { bus => 1, addr => 25 }, | |
5082 | 'net31' => { bus => 1, addr => 26 }, | |
da8b4189 | 5083 | 'xhci' => { bus => 1, addr => 27 }, |
5bdcf937 AD |
5084 | 'virtio6' => { bus => 2, addr => 1 }, |
5085 | 'virtio7' => { bus => 2, addr => 2 }, | |
5086 | 'virtio8' => { bus => 2, addr => 3 }, | |
5087 | 'virtio9' => { bus => 2, addr => 4 }, | |
5088 | 'virtio10' => { bus => 2, addr => 5 }, | |
5089 | 'virtio11' => { bus => 2, addr => 6 }, | |
5090 | 'virtio12' => { bus => 2, addr => 7 }, | |
5091 | 'virtio13' => { bus => 2, addr => 8 }, | |
5092 | 'virtio14' => { bus => 2, addr => 9 }, | |
5093 | 'virtio15' => { bus => 2, addr => 10 }, | |
6731a4cf AD |
5094 | 'virtioscsi0' => { bus => 3, addr => 1 }, |
5095 | 'virtioscsi1' => { bus => 3, addr => 2 }, | |
5096 | 'virtioscsi2' => { bus => 3, addr => 3 }, | |
5097 | 'virtioscsi3' => { bus => 3, addr => 4 }, | |
5098 | 'virtioscsi4' => { bus => 3, addr => 5 }, | |
5099 | 'virtioscsi5' => { bus => 3, addr => 6 }, | |
5100 | 'virtioscsi6' => { bus => 3, addr => 7 }, | |
5101 | 'virtioscsi7' => { bus => 3, addr => 8 }, | |
5102 | 'virtioscsi8' => { bus => 3, addr => 9 }, | |
5103 | 'virtioscsi9' => { bus => 3, addr => 10 }, | |
5104 | 'virtioscsi10' => { bus => 3, addr => 11 }, | |
5105 | 'virtioscsi11' => { bus => 3, addr => 12 }, | |
5106 | 'virtioscsi12' => { bus => 3, addr => 13 }, | |
5107 | 'virtioscsi13' => { bus => 3, addr => 14 }, | |
5108 | 'virtioscsi14' => { bus => 3, addr => 15 }, | |
5109 | 'virtioscsi15' => { bus => 3, addr => 16 }, | |
5110 | 'virtioscsi16' => { bus => 3, addr => 17 }, | |
5111 | 'virtioscsi17' => { bus => 3, addr => 18 }, | |
5112 | 'virtioscsi18' => { bus => 3, addr => 19 }, | |
5113 | 'virtioscsi19' => { bus => 3, addr => 20 }, | |
5114 | 'virtioscsi20' => { bus => 3, addr => 21 }, | |
5115 | 'virtioscsi21' => { bus => 3, addr => 22 }, | |
5116 | 'virtioscsi22' => { bus => 3, addr => 23 }, | |
5117 | 'virtioscsi23' => { bus => 3, addr => 24 }, | |
5118 | 'virtioscsi24' => { bus => 3, addr => 25 }, | |
5119 | 'virtioscsi25' => { bus => 3, addr => 26 }, | |
5120 | 'virtioscsi26' => { bus => 3, addr => 27 }, | |
5121 | 'virtioscsi27' => { bus => 3, addr => 28 }, | |
5122 | 'virtioscsi28' => { bus => 3, addr => 29 }, | |
5123 | 'virtioscsi29' => { bus => 3, addr => 30 }, | |
5124 | 'virtioscsi30' => { bus => 3, addr => 31 }, | |
5125 | ||
6b64503e DM |
5126 | }; |
5127 | ||
5128 | if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) { | |
72a063e4 | 5129 | my $addr = sprintf("0x%x", $devices->{$id}->{addr}); |
5bdcf937 AD |
5130 | my $bus = $devices->{$id}->{bus}; |
5131 | $res = ",bus=pci.$bus,addr=$addr"; | |
98627641 | 5132 | $bridges->{$bus} = 1 if $bridges; |
72a063e4 DA |
5133 | } |
5134 | return $res; | |
5135 | ||
5136 | } | |
5137 | ||
2e3b7e2a AD |
5138 | sub print_pcie_addr { |
5139 | my ($id) = @_; | |
5140 | ||
5141 | my $res = ''; | |
5142 | my $devices = { | |
5143 | hostpci0 => { bus => "ich9-pcie-port-1", addr => 0 }, | |
5144 | hostpci1 => { bus => "ich9-pcie-port-2", addr => 0 }, | |
5145 | hostpci2 => { bus => "ich9-pcie-port-3", addr => 0 }, | |
5146 | hostpci3 => { bus => "ich9-pcie-port-4", addr => 0 }, | |
5147 | }; | |
5148 | ||
5149 | if (defined($devices->{$id}->{bus}) && defined($devices->{$id}->{addr})) { | |
5150 | my $addr = sprintf("0x%x", $devices->{$id}->{addr}); | |
5151 | my $bus = $devices->{$id}->{bus}; | |
5152 | $res = ",bus=$bus,addr=$addr"; | |
5153 | } | |
5154 | return $res; | |
5155 | ||
5156 | } | |
5157 | ||
3e16d5fc DM |
5158 | # vzdump restore implementaion |
5159 | ||
ed221350 | 5160 | sub tar_archive_read_firstfile { |
3e16d5fc | 5161 | my $archive = shift; |
afdb31d5 | 5162 | |
3e16d5fc DM |
5163 | die "ERROR: file '$archive' does not exist\n" if ! -f $archive; |
5164 | ||
5165 | # try to detect archive type first | |
5166 | my $pid = open (TMP, "tar tf '$archive'|") || | |
5167 | die "unable to open file '$archive'\n"; | |
5168 | my $firstfile = <TMP>; | |
5169 | kill 15, $pid; | |
5170 | close TMP; | |
5171 | ||
5172 | die "ERROR: archive contaions no data\n" if !$firstfile; | |
5173 | chomp $firstfile; | |
5174 | ||
5175 | return $firstfile; | |
5176 | } | |
5177 | ||
ed221350 DM |
5178 | sub tar_restore_cleanup { |
5179 | my ($storecfg, $statfile) = @_; | |
3e16d5fc DM |
5180 | |
5181 | print STDERR "starting cleanup\n"; | |
5182 | ||
5183 | if (my $fd = IO::File->new($statfile, "r")) { | |
5184 | while (defined(my $line = <$fd>)) { | |
5185 | if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) { | |
5186 | my $volid = $2; | |
5187 | eval { | |
5188 | if ($volid =~ m|^/|) { | |
5189 | unlink $volid || die 'unlink failed\n'; | |
5190 | } else { | |
ed221350 | 5191 | PVE::Storage::vdisk_free($storecfg, $volid); |
3e16d5fc | 5192 | } |
afdb31d5 | 5193 | print STDERR "temporary volume '$volid' sucessfuly removed\n"; |
3e16d5fc DM |
5194 | }; |
5195 | print STDERR "unable to cleanup '$volid' - $@" if $@; | |
5196 | } else { | |
5197 | print STDERR "unable to parse line in statfile - $line"; | |
afdb31d5 | 5198 | } |
3e16d5fc DM |
5199 | } |
5200 | $fd->close(); | |
5201 | } | |
5202 | } | |
5203 | ||
5204 | sub restore_archive { | |
a0d1b1a2 | 5205 | my ($archive, $vmid, $user, $opts) = @_; |
3e16d5fc | 5206 | |
91bd6c90 DM |
5207 | my $format = $opts->{format}; |
5208 | my $comp; | |
5209 | ||
5210 | if ($archive =~ m/\.tgz$/ || $archive =~ m/\.tar\.gz$/) { | |
5211 | $format = 'tar' if !$format; | |
5212 | $comp = 'gzip'; | |
5213 | } elsif ($archive =~ m/\.tar$/) { | |
5214 | $format = 'tar' if !$format; | |
5215 | } elsif ($archive =~ m/.tar.lzo$/) { | |
5216 | $format = 'tar' if !$format; | |
5217 | $comp = 'lzop'; | |
5218 | } elsif ($archive =~ m/\.vma$/) { | |
5219 | $format = 'vma' if !$format; | |
5220 | } elsif ($archive =~ m/\.vma\.gz$/) { | |
5221 | $format = 'vma' if !$format; | |
5222 | $comp = 'gzip'; | |
5223 | } elsif ($archive =~ m/\.vma\.lzo$/) { | |
5224 | $format = 'vma' if !$format; | |
5225 | $comp = 'lzop'; | |
5226 | } else { | |
5227 | $format = 'vma' if !$format; # default | |
5228 | } | |
5229 | ||
5230 | # try to detect archive format | |
5231 | if ($format eq 'tar') { | |
5232 | return restore_tar_archive($archive, $vmid, $user, $opts); | |
5233 | } else { | |
5234 | return restore_vma_archive($archive, $vmid, $user, $opts, $comp); | |
5235 | } | |
5236 | } | |
5237 | ||
5238 | sub restore_update_config_line { | |
5239 | my ($outfd, $cookie, $vmid, $map, $line, $unique) = @_; | |
5240 | ||
5241 | return if $line =~ m/^\#qmdump\#/; | |
5242 | return if $line =~ m/^\#vzdump\#/; | |
5243 | return if $line =~ m/^lock:/; | |
5244 | return if $line =~ m/^unused\d+:/; | |
5245 | return if $line =~ m/^parent:/; | |
ca3e4fa4 | 5246 | return if $line =~ m/^template:/; # restored VM is never a template |
91bd6c90 DM |
5247 | |
5248 | if (($line =~ m/^(vlan(\d+)):\s*(\S+)\s*$/)) { | |
5249 | # try to convert old 1.X settings | |
5250 | my ($id, $ind, $ethcfg) = ($1, $2, $3); | |
5251 | foreach my $devconfig (PVE::Tools::split_list($ethcfg)) { | |
5252 | my ($model, $macaddr) = split(/\=/, $devconfig); | |
5253 | $macaddr = PVE::Tools::random_ether_addr() if !$macaddr || $unique; | |
5254 | my $net = { | |
5255 | model => $model, | |
5256 | bridge => "vmbr$ind", | |
5257 | macaddr => $macaddr, | |
5258 | }; | |
5259 | my $netstr = print_net($net); | |
5260 | ||
5261 | print $outfd "net$cookie->{netcount}: $netstr\n"; | |
5262 | $cookie->{netcount}++; | |
5263 | } | |
5264 | } elsif (($line =~ m/^(net\d+):\s*(\S+)\s*$/) && $unique) { | |
5265 | my ($id, $netstr) = ($1, $2); | |
5266 | my $net = parse_net($netstr); | |
5267 | $net->{macaddr} = PVE::Tools::random_ether_addr() if $net->{macaddr}; | |
5268 | $netstr = print_net($net); | |
5269 | print $outfd "$id: $netstr\n"; | |
5270 | } elsif ($line =~ m/^((ide|scsi|virtio|sata)\d+):\s*(\S+)\s*$/) { | |
5271 | my $virtdev = $1; | |
907ea891 | 5272 | my $value = $3; |
d9faf790 WB |
5273 | my $di = parse_drive($virtdev, $value); |
5274 | if (defined($di->{backup}) && !$di->{backup}) { | |
91bd6c90 | 5275 | print $outfd "#$line"; |
c0f7406e | 5276 | } elsif ($map->{$virtdev}) { |
8fd57431 | 5277 | delete $di->{format}; # format can change on restore |
91bd6c90 | 5278 | $di->{file} = $map->{$virtdev}; |
ed221350 | 5279 | $value = print_drive($vmid, $di); |
91bd6c90 DM |
5280 | print $outfd "$virtdev: $value\n"; |
5281 | } else { | |
5282 | print $outfd $line; | |
5283 | } | |
5284 | } else { | |
5285 | print $outfd $line; | |
5286 | } | |
5287 | } | |
5288 | ||
5289 | sub scan_volids { | |
5290 | my ($cfg, $vmid) = @_; | |
5291 | ||
5292 | my $info = PVE::Storage::vdisk_list($cfg, undef, $vmid); | |
5293 | ||
5294 | my $volid_hash = {}; | |
5295 | foreach my $storeid (keys %$info) { | |
5296 | foreach my $item (@{$info->{$storeid}}) { | |
5297 | next if !($item->{volid} && $item->{size}); | |
5996a936 | 5298 | $item->{path} = PVE::Storage::path($cfg, $item->{volid}); |
91bd6c90 DM |
5299 | $volid_hash->{$item->{volid}} = $item; |
5300 | } | |
5301 | } | |
5302 | ||
5303 | return $volid_hash; | |
5304 | } | |
5305 | ||
77019edf WB |
5306 | sub is_volume_in_use { |
5307 | my ($storecfg, $conf, $skip_drive, $volid) = @_; | |
a8e2f942 | 5308 | |
77019edf | 5309 | my $path = PVE::Storage::path($storecfg, $volid); |
a8e2f942 DM |
5310 | |
5311 | my $scan_config = sub { | |
5312 | my ($cref, $snapname) = @_; | |
5313 | ||
5314 | foreach my $key (keys %$cref) { | |
5315 | my $value = $cref->{$key}; | |
74479ee9 | 5316 | if (is_valid_drivename($key)) { |
a8e2f942 DM |
5317 | next if $skip_drive && $key eq $skip_drive; |
5318 | my $drive = parse_drive($key, $value); | |
5319 | next if !$drive || !$drive->{file} || drive_is_cdrom($drive); | |
77019edf | 5320 | return 1 if $volid eq $drive->{file}; |
a8e2f942 | 5321 | if ($drive->{file} =~ m!^/!) { |
77019edf | 5322 | return 1 if $drive->{file} eq $path; |
a8e2f942 DM |
5323 | } else { |
5324 | my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file}, 1); | |
5325 | next if !$storeid; | |
5326 | my $scfg = PVE::Storage::storage_config($storecfg, $storeid, 1); | |
5327 | next if !$scfg; | |
77019edf | 5328 | return 1 if $path eq PVE::Storage::path($storecfg, $drive->{file}, $snapname); |
a8e2f942 DM |
5329 | } |
5330 | } | |
5331 | } | |
77019edf WB |
5332 | |
5333 | return 0; | |
a8e2f942 DM |
5334 | }; |
5335 | ||
77019edf | 5336 | return 1 if &$scan_config($conf); |
a8e2f942 DM |
5337 | |
5338 | undef $skip_drive; | |
5339 | ||
77019edf WB |
5340 | foreach my $snapname (keys %{$conf->{snapshots}}) { |
5341 | return 1 if &$scan_config($conf->{snapshots}->{$snapname}, $snapname); | |
a8e2f942 DM |
5342 | } |
5343 | ||
77019edf | 5344 | return 0; |
a8e2f942 DM |
5345 | } |
5346 | ||
91bd6c90 DM |
5347 | sub update_disksize { |
5348 | my ($vmid, $conf, $volid_hash) = @_; | |
be190583 | 5349 | |
91bd6c90 DM |
5350 | my $changes; |
5351 | ||
5352 | my $used = {}; | |
5353 | ||
5996a936 DM |
5354 | # Note: it is allowed to define multiple storages with same path (alias), so |
5355 | # we need to check both 'volid' and real 'path' (two different volid can point | |
5356 | # to the same path). | |
5357 | ||
5358 | my $usedpath = {}; | |
be190583 | 5359 | |
91bd6c90 DM |
5360 | # update size info |
5361 | foreach my $opt (keys %$conf) { | |
74479ee9 | 5362 | if (is_valid_drivename($opt)) { |
ed221350 | 5363 | my $drive = parse_drive($opt, $conf->{$opt}); |
91bd6c90 DM |
5364 | my $volid = $drive->{file}; |
5365 | next if !$volid; | |
5366 | ||
5367 | $used->{$volid} = 1; | |
be190583 | 5368 | if ($volid_hash->{$volid} && |
5996a936 DM |
5369 | (my $path = $volid_hash->{$volid}->{path})) { |
5370 | $usedpath->{$path} = 1; | |
5371 | } | |
91bd6c90 | 5372 | |
ed221350 | 5373 | next if drive_is_cdrom($drive); |
91bd6c90 DM |
5374 | next if !$volid_hash->{$volid}; |
5375 | ||
5376 | $drive->{size} = $volid_hash->{$volid}->{size}; | |
7a907ce6 DM |
5377 | my $new = print_drive($vmid, $drive); |
5378 | if ($new ne $conf->{$opt}) { | |
5379 | $changes = 1; | |
5380 | $conf->{$opt} = $new; | |
5381 | } | |
91bd6c90 DM |
5382 | } |
5383 | } | |
5384 | ||
5996a936 DM |
5385 | # remove 'unusedX' entry if volume is used |
5386 | foreach my $opt (keys %$conf) { | |
5387 | next if $opt !~ m/^unused\d+$/; | |
5388 | my $volid = $conf->{$opt}; | |
5389 | my $path = $volid_hash->{$volid}->{path} if $volid_hash->{$volid}; | |
be190583 | 5390 | if ($used->{$volid} || ($path && $usedpath->{$path})) { |
5996a936 DM |
5391 | $changes = 1; |
5392 | delete $conf->{$opt}; | |
5393 | } | |
5394 | } | |
5395 | ||
91bd6c90 DM |
5396 | foreach my $volid (sort keys %$volid_hash) { |
5397 | next if $volid =~ m/vm-$vmid-state-/; | |
5398 | next if $used->{$volid}; | |
5996a936 DM |
5399 | my $path = $volid_hash->{$volid}->{path}; |
5400 | next if !$path; # just to be sure | |
5401 | next if $usedpath->{$path}; | |
91bd6c90 | 5402 | $changes = 1; |
8793d495 | 5403 | PVE::QemuConfig->add_unused_volume($conf, $volid); |
05937a14 | 5404 | $usedpath->{$path} = 1; # avoid to add more than once (aliases) |
91bd6c90 DM |
5405 | } |
5406 | ||
5407 | return $changes; | |
5408 | } | |
5409 | ||
5410 | sub rescan { | |
5411 | my ($vmid, $nolock) = @_; | |
5412 | ||
20519efc | 5413 | my $cfg = PVE::Storage::config(); |
91bd6c90 DM |
5414 | |
5415 | my $volid_hash = scan_volids($cfg, $vmid); | |
5416 | ||
5417 | my $updatefn = sub { | |
5418 | my ($vmid) = @_; | |
5419 | ||
ffda963f | 5420 | my $conf = PVE::QemuConfig->load_config($vmid); |
be190583 | 5421 | |
ffda963f | 5422 | PVE::QemuConfig->check_lock($conf); |
91bd6c90 | 5423 | |
03da3f0d DM |
5424 | my $vm_volids = {}; |
5425 | foreach my $volid (keys %$volid_hash) { | |
5426 | my $info = $volid_hash->{$volid}; | |
5427 | $vm_volids->{$volid} = $info if $info->{vmid} && $info->{vmid} == $vmid; | |
5428 | } | |
5429 | ||
5430 | my $changes = update_disksize($vmid, $conf, $vm_volids); | |
91bd6c90 | 5431 | |
ffda963f | 5432 | PVE::QemuConfig->write_config($vmid, $conf) if $changes; |
91bd6c90 DM |
5433 | }; |
5434 | ||
5435 | if (defined($vmid)) { | |
5436 | if ($nolock) { | |
5437 | &$updatefn($vmid); | |
5438 | } else { | |
ffda963f | 5439 | PVE::QemuConfig->lock_config($vmid, $updatefn, $vmid); |
91bd6c90 DM |
5440 | } |
5441 | } else { | |
5442 | my $vmlist = config_list(); | |
5443 | foreach my $vmid (keys %$vmlist) { | |
5444 | if ($nolock) { | |
5445 | &$updatefn($vmid); | |
5446 | } else { | |
ffda963f | 5447 | PVE::QemuConfig->lock_config($vmid, $updatefn, $vmid); |
be190583 | 5448 | } |
91bd6c90 DM |
5449 | } |
5450 | } | |
5451 | } | |
5452 | ||
5453 | sub restore_vma_archive { | |
5454 | my ($archive, $vmid, $user, $opts, $comp) = @_; | |
5455 | ||
5456 | my $input = $archive eq '-' ? "<&STDIN" : undef; | |
5457 | my $readfrom = $archive; | |
5458 | ||
5459 | my $uncomp = ''; | |
5460 | if ($comp) { | |
5461 | $readfrom = '-'; | |
5462 | my $qarchive = PVE::Tools::shellquote($archive); | |
5463 | if ($comp eq 'gzip') { | |
5464 | $uncomp = "zcat $qarchive|"; | |
5465 | } elsif ($comp eq 'lzop') { | |
5466 | $uncomp = "lzop -d -c $qarchive|"; | |
5467 | } else { | |
5468 | die "unknown compression method '$comp'\n"; | |
5469 | } | |
be190583 | 5470 | |
91bd6c90 DM |
5471 | } |
5472 | ||
5473 | my $tmpdir = "/var/tmp/vzdumptmp$$"; | |
5474 | rmtree $tmpdir; | |
5475 | ||
5476 | # disable interrupts (always do cleanups) | |
5477 | local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub { | |
5478 | warn "got interrupt - ignored\n"; | |
5479 | }; | |
5480 | ||
5481 | my $mapfifo = "/var/tmp/vzdumptmp$$.fifo"; | |
5482 | POSIX::mkfifo($mapfifo, 0600); | |
5483 | my $fifofh; | |
5484 | ||
5485 | my $openfifo = sub { | |
5486 | open($fifofh, '>', $mapfifo) || die $!; | |
5487 | }; | |
5488 | ||
5489 | my $cmd = "${uncomp}vma extract -v -r $mapfifo $readfrom $tmpdir"; | |
5490 | ||
5491 | my $oldtimeout; | |
5492 | my $timeout = 5; | |
5493 | ||
5494 | my $devinfo = {}; | |
5495 | ||
5496 | my $rpcenv = PVE::RPCEnvironment::get(); | |
5497 | ||
ffda963f | 5498 | my $conffile = PVE::QemuConfig->config_file($vmid); |
91bd6c90 DM |
5499 | my $tmpfn = "$conffile.$$.tmp"; |
5500 | ||
ed221350 | 5501 | # Note: $oldconf is undef if VM does not exists |
ffda963f FG |
5502 | my $cfs_path = PVE::QemuConfig->cfs_config_path($vmid); |
5503 | my $oldconf = PVE::Cluster::cfs_read_file($cfs_path); | |
ed221350 | 5504 | |
91bd6c90 DM |
5505 | my $print_devmap = sub { |
5506 | my $virtdev_hash = {}; | |
5507 | ||
5508 | my $cfgfn = "$tmpdir/qemu-server.conf"; | |
5509 | ||
5510 | # we can read the config - that is already extracted | |
5511 | my $fh = IO::File->new($cfgfn, "r") || | |
5512 | "unable to read qemu-server.conf - $!\n"; | |
5513 | ||
6738ab9c | 5514 | my $fwcfgfn = "$tmpdir/qemu-server.fw"; |
3457d090 WL |
5515 | if (-f $fwcfgfn) { |
5516 | my $pve_firewall_dir = '/etc/pve/firewall'; | |
5517 | mkdir $pve_firewall_dir; # make sure the dir exists | |
5518 | PVE::Tools::file_copy($fwcfgfn, "${pve_firewall_dir}/$vmid.fw"); | |
5519 | } | |
6738ab9c | 5520 | |
91bd6c90 DM |
5521 | while (defined(my $line = <$fh>)) { |
5522 | if ($line =~ m/^\#qmdump\#map:(\S+):(\S+):(\S*):(\S*):$/) { | |
5523 | my ($virtdev, $devname, $storeid, $format) = ($1, $2, $3, $4); | |
5524 | die "archive does not contain data for drive '$virtdev'\n" | |
5525 | if !$devinfo->{$devname}; | |
5526 | if (defined($opts->{storage})) { | |
5527 | $storeid = $opts->{storage} || 'local'; | |
5528 | } elsif (!$storeid) { | |
5529 | $storeid = 'local'; | |
5530 | } | |
5531 | $format = 'raw' if !$format; | |
5532 | $devinfo->{$devname}->{devname} = $devname; | |
5533 | $devinfo->{$devname}->{virtdev} = $virtdev; | |
5534 | $devinfo->{$devname}->{format} = $format; | |
5535 | $devinfo->{$devname}->{storeid} = $storeid; | |
5536 | ||
be190583 | 5537 | # check permission on storage |
91bd6c90 DM |
5538 | my $pool = $opts->{pool}; # todo: do we need that? |
5539 | if ($user ne 'root@pam') { | |
5540 | $rpcenv->check($user, "/storage/$storeid", ['Datastore.AllocateSpace']); | |
5541 | } | |
5542 | ||
5543 | $virtdev_hash->{$virtdev} = $devinfo->{$devname}; | |
5544 | } | |
5545 | } | |
5546 | ||
5547 | foreach my $devname (keys %$devinfo) { | |
be190583 DM |
5548 | die "found no device mapping information for device '$devname'\n" |
5549 | if !$devinfo->{$devname}->{virtdev}; | |
91bd6c90 DM |
5550 | } |
5551 | ||
20519efc | 5552 | my $cfg = PVE::Storage::config(); |
ed221350 DM |
5553 | |
5554 | # create empty/temp config | |
be190583 | 5555 | if ($oldconf) { |
ed221350 DM |
5556 | PVE::Tools::file_set_contents($conffile, "memory: 128\n"); |
5557 | foreach_drive($oldconf, sub { | |
5558 | my ($ds, $drive) = @_; | |
5559 | ||
5560 | return if drive_is_cdrom($drive); | |
5561 | ||
5562 | my $volid = $drive->{file}; | |
5563 | ||
5564 | return if !$volid || $volid =~ m|^/|; | |
5565 | ||
5566 | my ($path, $owner) = PVE::Storage::path($cfg, $volid); | |
5567 | return if !$path || !$owner || ($owner != $vmid); | |
5568 | ||
5569 | # Note: only delete disk we want to restore | |
5570 | # other volumes will become unused | |
5571 | if ($virtdev_hash->{$ds}) { | |
5572 | PVE::Storage::vdisk_free($cfg, $volid); | |
5573 | } | |
5574 | }); | |
381b8fae DC |
5575 | |
5576 | # delete vmstate files | |
5577 | # since after the restore we have no snapshots anymore | |
5578 | foreach my $snapname (keys %{$oldconf->{snapshots}}) { | |
5579 | my $snap = $oldconf->{snapshots}->{$snapname}; | |
5580 | if ($snap->{vmstate}) { | |
5581 | eval { PVE::Storage::vdisk_free($cfg, $snap->{vmstate}); }; | |
5582 | if (my $err = $@) { | |
5583 | warn $err; | |
5584 | } | |
5585 | } | |
5586 | } | |
ed221350 DM |
5587 | } |
5588 | ||
5589 | my $map = {}; | |
91bd6c90 DM |
5590 | foreach my $virtdev (sort keys %$virtdev_hash) { |
5591 | my $d = $virtdev_hash->{$virtdev}; | |
5592 | my $alloc_size = int(($d->{size} + 1024 - 1)/1024); | |
5593 | my $scfg = PVE::Storage::storage_config($cfg, $d->{storeid}); | |
8fd57431 DM |
5594 | |
5595 | # test if requested format is supported | |
5596 | my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($cfg, $d->{storeid}); | |
5597 | my $supported = grep { $_ eq $d->{format} } @$validFormats; | |
5598 | $d->{format} = $defFormat if !$supported; | |
5599 | ||
91bd6c90 DM |
5600 | my $volid = PVE::Storage::vdisk_alloc($cfg, $d->{storeid}, $vmid, |
5601 | $d->{format}, undef, $alloc_size); | |
5602 | print STDERR "new volume ID is '$volid'\n"; | |
5603 | $d->{volid} = $volid; | |
5604 | my $path = PVE::Storage::path($cfg, $volid); | |
5605 | ||
5f96f4df WL |
5606 | PVE::Storage::activate_volumes($cfg,[$volid]); |
5607 | ||
91bd6c90 | 5608 | my $write_zeros = 1; |
88240a83 | 5609 | if (PVE::Storage::volume_has_feature($cfg, 'sparseinit', $volid)) { |
91bd6c90 DM |
5610 | $write_zeros = 0; |
5611 | } | |
5612 | ||
3c525055 | 5613 | print $fifofh "format=$d->{format}:${write_zeros}:$d->{devname}=$path\n"; |
91bd6c90 DM |
5614 | |
5615 | print "map '$d->{devname}' to '$path' (write zeros = ${write_zeros})\n"; | |
5616 | $map->{$virtdev} = $volid; | |
5617 | } | |
5618 | ||
5619 | $fh->seek(0, 0) || die "seek failed - $!\n"; | |
5620 | ||
5621 | my $outfd = new IO::File ($tmpfn, "w") || | |
5622 | die "unable to write config for VM $vmid\n"; | |
5623 | ||
5624 | my $cookie = { netcount => 0 }; | |
5625 | while (defined(my $line = <$fh>)) { | |
be190583 | 5626 | restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique}); |
91bd6c90 DM |
5627 | } |
5628 | ||
5629 | $fh->close(); | |
5630 | $outfd->close(); | |
5631 | }; | |
5632 | ||
5633 | eval { | |
5634 | # enable interrupts | |
5635 | local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub { | |
5636 | die "interrupted by signal\n"; | |
5637 | }; | |
5638 | local $SIG{ALRM} = sub { die "got timeout\n"; }; | |
5639 | ||
5640 | $oldtimeout = alarm($timeout); | |
5641 | ||
5642 | my $parser = sub { | |
5643 | my $line = shift; | |
5644 | ||
5645 | print "$line\n"; | |
5646 | ||
5647 | if ($line =~ m/^DEV:\sdev_id=(\d+)\ssize:\s(\d+)\sdevname:\s(\S+)$/) { | |
5648 | my ($dev_id, $size, $devname) = ($1, $2, $3); | |
5649 | $devinfo->{$devname} = { size => $size, dev_id => $dev_id }; | |
5650 | } elsif ($line =~ m/^CTIME: /) { | |
46f58b5f | 5651 | # we correctly received the vma config, so we can disable |
3cf90d7a DM |
5652 | # the timeout now for disk allocation (set to 10 minutes, so |
5653 | # that we always timeout if something goes wrong) | |
5654 | alarm(600); | |
91bd6c90 DM |
5655 | &$print_devmap(); |
5656 | print $fifofh "done\n"; | |
5657 | my $tmp = $oldtimeout || 0; | |
5658 | $oldtimeout = undef; | |
5659 | alarm($tmp); | |
5660 | close($fifofh); | |
5661 | } | |
5662 | }; | |
be190583 | 5663 | |
91bd6c90 DM |
5664 | print "restore vma archive: $cmd\n"; |
5665 | run_command($cmd, input => $input, outfunc => $parser, afterfork => $openfifo); | |
5666 | }; | |
5667 | my $err = $@; | |
5668 | ||
5669 | alarm($oldtimeout) if $oldtimeout; | |
5670 | ||
5f96f4df WL |
5671 | my $vollist = []; |
5672 | foreach my $devname (keys %$devinfo) { | |
5673 | my $volid = $devinfo->{$devname}->{volid}; | |
5674 | push @$vollist, $volid if $volid; | |
5675 | } | |
5676 | ||
20519efc | 5677 | my $cfg = PVE::Storage::config(); |
5f96f4df WL |
5678 | PVE::Storage::deactivate_volumes($cfg, $vollist); |
5679 | ||
91bd6c90 DM |
5680 | unlink $mapfifo; |
5681 | ||
5682 | if ($err) { | |
5683 | rmtree $tmpdir; | |
5684 | unlink $tmpfn; | |
5685 | ||
91bd6c90 DM |
5686 | foreach my $devname (keys %$devinfo) { |
5687 | my $volid = $devinfo->{$devname}->{volid}; | |
5688 | next if !$volid; | |
5689 | eval { | |
5690 | if ($volid =~ m|^/|) { | |
5691 | unlink $volid || die 'unlink failed\n'; | |
5692 | } else { | |
5693 | PVE::Storage::vdisk_free($cfg, $volid); | |
5694 | } | |
5695 | print STDERR "temporary volume '$volid' sucessfuly removed\n"; | |
5696 | }; | |
5697 | print STDERR "unable to cleanup '$volid' - $@" if $@; | |
5698 | } | |
5699 | die $err; | |
5700 | } | |
5701 | ||
5702 | rmtree $tmpdir; | |
ed221350 DM |
5703 | |
5704 | rename($tmpfn, $conffile) || | |
91bd6c90 DM |
5705 | die "unable to commit configuration file '$conffile'\n"; |
5706 | ||
ed221350 DM |
5707 | PVE::Cluster::cfs_update(); # make sure we read new file |
5708 | ||
91bd6c90 DM |
5709 | eval { rescan($vmid, 1); }; |
5710 | warn $@ if $@; | |
5711 | } | |
5712 | ||
5713 | sub restore_tar_archive { | |
5714 | my ($archive, $vmid, $user, $opts) = @_; | |
5715 | ||
9c502e26 | 5716 | if ($archive ne '-') { |
ed221350 | 5717 | my $firstfile = tar_archive_read_firstfile($archive); |
9c502e26 DM |
5718 | die "ERROR: file '$archive' dos not lock like a QemuServer vzdump backup\n" |
5719 | if $firstfile ne 'qemu-server.conf'; | |
5720 | } | |
3e16d5fc | 5721 | |
20519efc | 5722 | my $storecfg = PVE::Storage::config(); |
ebb55558 | 5723 | |
ed221350 | 5724 | # destroy existing data - keep empty config |
ffda963f | 5725 | my $vmcfgfn = PVE::QemuConfig->config_file($vmid); |
ebb55558 | 5726 | destroy_vm($storecfg, $vmid, 1) if -f $vmcfgfn; |
ed221350 | 5727 | |
3e16d5fc DM |
5728 | my $tocmd = "/usr/lib/qemu-server/qmextract"; |
5729 | ||
2415a446 | 5730 | $tocmd .= " --storage " . PVE::Tools::shellquote($opts->{storage}) if $opts->{storage}; |
a0d1b1a2 | 5731 | $tocmd .= " --pool " . PVE::Tools::shellquote($opts->{pool}) if $opts->{pool}; |
3e16d5fc DM |
5732 | $tocmd .= ' --prealloc' if $opts->{prealloc}; |
5733 | $tocmd .= ' --info' if $opts->{info}; | |
5734 | ||
a0d1b1a2 | 5735 | # tar option "xf" does not autodetect compression when read from STDIN, |
9c502e26 | 5736 | # so we pipe to zcat |
2415a446 DM |
5737 | my $cmd = "zcat -f|tar xf " . PVE::Tools::shellquote($archive) . " " . |
5738 | PVE::Tools::shellquote("--to-command=$tocmd"); | |
3e16d5fc DM |
5739 | |
5740 | my $tmpdir = "/var/tmp/vzdumptmp$$"; | |
5741 | mkpath $tmpdir; | |
5742 | ||
5743 | local $ENV{VZDUMP_TMPDIR} = $tmpdir; | |
5744 | local $ENV{VZDUMP_VMID} = $vmid; | |
a0d1b1a2 | 5745 | local $ENV{VZDUMP_USER} = $user; |
3e16d5fc | 5746 | |
ffda963f | 5747 | my $conffile = PVE::QemuConfig->config_file($vmid); |
3e16d5fc DM |
5748 | my $tmpfn = "$conffile.$$.tmp"; |
5749 | ||
5750 | # disable interrupts (always do cleanups) | |
5751 | local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = sub { | |
5752 | print STDERR "got interrupt - ignored\n"; | |
5753 | }; | |
5754 | ||
afdb31d5 | 5755 | eval { |
3e16d5fc DM |
5756 | # enable interrupts |
5757 | local $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{PIPE} = sub { | |
5758 | die "interrupted by signal\n"; | |
5759 | }; | |
5760 | ||
9c502e26 DM |
5761 | if ($archive eq '-') { |
5762 | print "extracting archive from STDIN\n"; | |
5763 | run_command($cmd, input => "<&STDIN"); | |
5764 | } else { | |
5765 | print "extracting archive '$archive'\n"; | |
5766 | run_command($cmd); | |
5767 | } | |
3e16d5fc DM |
5768 | |
5769 | return if $opts->{info}; | |
5770 | ||
5771 | # read new mapping | |
5772 | my $map = {}; | |
5773 | my $statfile = "$tmpdir/qmrestore.stat"; | |
5774 | if (my $fd = IO::File->new($statfile, "r")) { | |
5775 | while (defined (my $line = <$fd>)) { | |
5776 | if ($line =~ m/vzdump:([^\s:]*):(\S+)$/) { | |
5777 | $map->{$1} = $2 if $1; | |
5778 | } else { | |
5779 | print STDERR "unable to parse line in statfile - $line\n"; | |
5780 | } | |
5781 | } | |
5782 | $fd->close(); | |
5783 | } | |
5784 | ||
5785 | my $confsrc = "$tmpdir/qemu-server.conf"; | |
5786 | ||
5787 | my $srcfd = new IO::File($confsrc, "r") || | |
5788 | die "unable to open file '$confsrc'\n"; | |
5789 | ||
5790 | my $outfd = new IO::File ($tmpfn, "w") || | |
5791 | die "unable to write config for VM $vmid\n"; | |
5792 | ||
91bd6c90 | 5793 | my $cookie = { netcount => 0 }; |
3e16d5fc | 5794 | while (defined (my $line = <$srcfd>)) { |
be190583 | 5795 | restore_update_config_line($outfd, $cookie, $vmid, $map, $line, $opts->{unique}); |
3e16d5fc DM |
5796 | } |
5797 | ||
5798 | $srcfd->close(); | |
5799 | $outfd->close(); | |
5800 | }; | |
5801 | my $err = $@; | |
5802 | ||
afdb31d5 | 5803 | if ($err) { |
3e16d5fc DM |
5804 | |
5805 | unlink $tmpfn; | |
5806 | ||
ed221350 | 5807 | tar_restore_cleanup($storecfg, "$tmpdir/qmrestore.stat") if !$opts->{info}; |
afdb31d5 | 5808 | |
3e16d5fc | 5809 | die $err; |
afdb31d5 | 5810 | } |
3e16d5fc DM |
5811 | |
5812 | rmtree $tmpdir; | |
5813 | ||
5814 | rename $tmpfn, $conffile || | |
5815 | die "unable to commit configuration file '$conffile'\n"; | |
91bd6c90 | 5816 | |
ed221350 DM |
5817 | PVE::Cluster::cfs_update(); # make sure we read new file |
5818 | ||
91bd6c90 DM |
5819 | eval { rescan($vmid, 1); }; |
5820 | warn $@ if $@; | |
3e16d5fc DM |
5821 | }; |
5822 | ||
18bfb361 DM |
5823 | sub foreach_writable_storage { |
5824 | my ($conf, $func) = @_; | |
5825 | ||
5826 | my $sidhash = {}; | |
5827 | ||
5828 | foreach my $ds (keys %$conf) { | |
74479ee9 | 5829 | next if !is_valid_drivename($ds); |
18bfb361 DM |
5830 | |
5831 | my $drive = parse_drive($ds, $conf->{$ds}); | |
5832 | next if !$drive; | |
5833 | next if drive_is_cdrom($drive); | |
5834 | ||
5835 | my $volid = $drive->{file}; | |
5836 | ||
5837 | my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1); | |
be190583 | 5838 | $sidhash->{$sid} = $sid if $sid; |
18bfb361 DM |
5839 | } |
5840 | ||
5841 | foreach my $sid (sort keys %$sidhash) { | |
5842 | &$func($sid); | |
5843 | } | |
5844 | } | |
5845 | ||
e5eaa028 WL |
5846 | sub do_snapshots_with_qemu { |
5847 | my ($storecfg, $volid) = @_; | |
5848 | ||
5849 | my $storage_name = PVE::Storage::parse_volume_id($volid); | |
5850 | ||
116da784 WL |
5851 | if ($qemu_snap_storage->{$storecfg->{ids}->{$storage_name}->{type}} |
5852 | && !$storecfg->{ids}->{$storage_name}->{krbd}){ | |
e5eaa028 WL |
5853 | return 1; |
5854 | } | |
5855 | ||
5856 | if ($volid =~ m/\.(qcow2|qed)$/){ | |
5857 | return 1; | |
5858 | } | |
5859 | ||
5860 | return undef; | |
5861 | } | |
5862 | ||
4dcc780c WL |
5863 | sub qga_check_running { |
5864 | my ($vmid) = @_; | |
5865 | ||
5866 | eval { vm_mon_cmd($vmid, "guest-ping", timeout => 3); }; | |
5867 | if ($@) { | |
5868 | warn "Qemu Guest Agent are not running - $@"; | |
5869 | return 0; | |
5870 | } | |
5871 | return 1; | |
5872 | } | |
5873 | ||
04a69bb4 AD |
5874 | sub template_create { |
5875 | my ($vmid, $conf, $disk) = @_; | |
5876 | ||
04a69bb4 | 5877 | my $storecfg = PVE::Storage::config(); |
04a69bb4 | 5878 | |
9cd07842 DM |
5879 | foreach_drive($conf, sub { |
5880 | my ($ds, $drive) = @_; | |
5881 | ||
5882 | return if drive_is_cdrom($drive); | |
5883 | return if $disk && $ds ne $disk; | |
5884 | ||
5885 | my $volid = $drive->{file}; | |
bbd56097 | 5886 | return if !PVE::Storage::volume_has_feature($storecfg, 'template', $volid); |
9cd07842 | 5887 | |
04a69bb4 AD |
5888 | my $voliddst = PVE::Storage::vdisk_create_base($storecfg, $volid); |
5889 | $drive->{file} = $voliddst; | |
152fe752 | 5890 | $conf->{$ds} = print_drive($vmid, $drive); |
ffda963f | 5891 | PVE::QemuConfig->write_config($vmid, $conf); |
04a69bb4 | 5892 | }); |
04a69bb4 AD |
5893 | } |
5894 | ||
5133de42 | 5895 | sub qemu_img_convert { |
988e2714 | 5896 | my ($src_volid, $dst_volid, $size, $snapname, $is_zero_initialized) = @_; |
5133de42 AD |
5897 | |
5898 | my $storecfg = PVE::Storage::config(); | |
5899 | my ($src_storeid, $src_volname) = PVE::Storage::parse_volume_id($src_volid, 1); | |
5900 | my ($dst_storeid, $dst_volname) = PVE::Storage::parse_volume_id($dst_volid, 1); | |
5901 | ||
5902 | if ($src_storeid && $dst_storeid) { | |
6bb91c17 DM |
5903 | |
5904 | PVE::Storage::activate_volumes($storecfg, [$src_volid], $snapname); | |
5905 | ||
5133de42 AD |
5906 | my $src_scfg = PVE::Storage::storage_config($storecfg, $src_storeid); |
5907 | my $dst_scfg = PVE::Storage::storage_config($storecfg, $dst_storeid); | |
5908 | ||
5909 | my $src_format = qemu_img_format($src_scfg, $src_volname); | |
5910 | my $dst_format = qemu_img_format($dst_scfg, $dst_volname); | |
5911 | ||
5912 | my $src_path = PVE::Storage::path($storecfg, $src_volid, $snapname); | |
5913 | my $dst_path = PVE::Storage::path($storecfg, $dst_volid); | |
5914 | ||
5915 | my $cmd = []; | |
71ddbff9 | 5916 | push @$cmd, '/usr/bin/qemu-img', 'convert', '-t', 'writeback', '-p', '-n'; |
5133de42 | 5917 | push @$cmd, '-s', $snapname if($snapname && $src_format eq "qcow2"); |
988e2714 WB |
5918 | push @$cmd, '-f', $src_format, '-O', $dst_format, $src_path; |
5919 | if ($is_zero_initialized) { | |
5920 | push @$cmd, "zeroinit:$dst_path"; | |
5921 | } else { | |
5922 | push @$cmd, $dst_path; | |
5923 | } | |
5133de42 AD |
5924 | |
5925 | my $parser = sub { | |
5926 | my $line = shift; | |
5927 | if($line =~ m/\((\S+)\/100\%\)/){ | |
5928 | my $percent = $1; | |
5929 | my $transferred = int($size * $percent / 100); | |
5930 | my $remaining = $size - $transferred; | |
5931 | ||
5932 | print "transferred: $transferred bytes remaining: $remaining bytes total: $size bytes progression: $percent %\n"; | |
5933 | } | |
5934 | ||
5935 | }; | |
5936 | ||
5937 | eval { run_command($cmd, timeout => undef, outfunc => $parser); }; | |
5938 | my $err = $@; | |
5939 | die "copy failed: $err" if $err; | |
5940 | } | |
5941 | } | |
5942 | ||
5943 | sub qemu_img_format { | |
5944 | my ($scfg, $volname) = @_; | |
5945 | ||
d81f0f09 | 5946 | if ($scfg->{path} && $volname =~ m/\.(raw|cow|qcow|qcow2|qed|vmdk|cloop)$/) { |
5133de42 | 5947 | return $1; |
be190583 | 5948 | } else { |
5133de42 | 5949 | return "raw"; |
5133de42 AD |
5950 | } |
5951 | } | |
5952 | ||
cfad42af | 5953 | sub qemu_drive_mirror { |
988e2714 | 5954 | my ($vmid, $drive, $dst_volid, $vmiddst, $is_zero_initialized) = @_; |
cfad42af | 5955 | |
cfad42af | 5956 | my $storecfg = PVE::Storage::config(); |
08ac653f | 5957 | my ($dst_storeid, $dst_volname) = PVE::Storage::parse_volume_id($dst_volid); |
152fe752 | 5958 | |
08ac653f | 5959 | my $dst_scfg = PVE::Storage::storage_config($storecfg, $dst_storeid); |
cfad42af | 5960 | |
d81f0f09 | 5961 | my $format = qemu_img_format($dst_scfg, $dst_volname); |
21ccdb50 | 5962 | |
08ac653f | 5963 | my $dst_path = PVE::Storage::path($storecfg, $dst_volid); |
21ccdb50 | 5964 | |
988e2714 WB |
5965 | my $qemu_target = $is_zero_initialized ? "zeroinit:$dst_path" : $dst_path; |
5966 | ||
5967 | my $opts = { timeout => 10, device => "drive-$drive", mode => "existing", sync => "full", target => $qemu_target }; | |
88383920 DM |
5968 | $opts->{format} = $format if $format; |
5969 | ||
22967505 | 5970 | print "drive mirror is starting (scanning bitmap) : this step can take some minutes/hours, depend of disk size and storage speed\n"; |
21ccdb50 | 5971 | |
2e953867 WB |
5972 | my $finish_job = sub { |
5973 | while (1) { | |
5974 | my $stats = vm_mon_cmd($vmid, "query-block-jobs"); | |
5975 | my $stat = @$stats[0]; | |
5976 | last if !$stat; | |
5977 | sleep 1; | |
5978 | } | |
5979 | }; | |
5980 | ||
08ac653f | 5981 | eval { |
22967505 | 5982 | vm_mon_cmd($vmid, "drive-mirror", %$opts); |
08ac653f DM |
5983 | while (1) { |
5984 | my $stats = vm_mon_cmd($vmid, "query-block-jobs"); | |
5985 | my $stat = @$stats[0]; | |
5986 | die "mirroring job seem to have die. Maybe do you have bad sectors?" if !$stat; | |
5987 | die "error job is not mirroring" if $stat->{type} ne "mirror"; | |
5988 | ||
08ac653f | 5989 | my $busy = $stat->{busy}; |
ad123d97 | 5990 | my $ready = $stat->{ready}; |
08ac653f | 5991 | |
6f708643 DM |
5992 | if (my $total = $stat->{len}) { |
5993 | my $transferred = $stat->{offset} || 0; | |
5994 | my $remaining = $total - $transferred; | |
5995 | my $percent = sprintf "%.2f", ($transferred * 100 / $total); | |
67fb9de6 | 5996 | |
ad123d97 | 5997 | print "transferred: $transferred bytes remaining: $remaining bytes total: $total bytes progression: $percent % busy: $busy ready: $ready \n"; |
6f708643 | 5998 | } |
f34ebd52 | 5999 | |
08ac653f | 6000 | |
ad123d97 | 6001 | if ($stat->{ready} eq 'true') { |
f34ebd52 | 6002 | |
ad123d97 | 6003 | last if $vmiddst != $vmid; |
b467f79a | 6004 | |
ad123d97 AD |
6005 | # try to switch the disk if source and destination are on the same guest |
6006 | eval { vm_mon_cmd($vmid, "block-job-complete", device => "drive-$drive") }; | |
2e953867 WB |
6007 | if (!$@) { |
6008 | &$finish_job(); | |
6009 | last; | |
6010 | } | |
ad123d97 | 6011 | die $@ if $@ !~ m/cannot be completed/; |
08ac653f | 6012 | } |
08ac653f | 6013 | sleep 1; |
cfad42af AD |
6014 | } |
6015 | ||
08ac653f DM |
6016 | |
6017 | }; | |
88383920 | 6018 | my $err = $@; |
08ac653f | 6019 | |
88383920 | 6020 | my $cancel_job = sub { |
08ac653f | 6021 | vm_mon_cmd($vmid, "block-job-cancel", device => "drive-$drive"); |
2e953867 | 6022 | &$finish_job(); |
88383920 DM |
6023 | }; |
6024 | ||
6025 | if ($err) { | |
f34ebd52 | 6026 | eval { &$cancel_job(); }; |
88383920 DM |
6027 | die "mirroring error: $err"; |
6028 | } | |
6029 | ||
6030 | if ($vmiddst != $vmid) { | |
6031 | # if we clone a disk for a new target vm, we don't switch the disk | |
6032 | &$cancel_job(); # so we call block-job-cancel | |
cfad42af AD |
6033 | } |
6034 | } | |
6035 | ||
152fe752 | 6036 | sub clone_disk { |
be190583 | 6037 | my ($storecfg, $vmid, $running, $drivename, $drive, $snapname, |
152fe752 DM |
6038 | $newvmid, $storage, $format, $full, $newvollist) = @_; |
6039 | ||
6040 | my $newvolid; | |
6041 | ||
6042 | if (!$full) { | |
6043 | print "create linked clone of drive $drivename ($drive->{file})\n"; | |
258e646c | 6044 | $newvolid = PVE::Storage::vdisk_clone($storecfg, $drive->{file}, $newvmid, $snapname); |
152fe752 DM |
6045 | push @$newvollist, $newvolid; |
6046 | } else { | |
6047 | my ($storeid, $volname) = PVE::Storage::parse_volume_id($drive->{file}); | |
6048 | $storeid = $storage if $storage; | |
6049 | ||
1377d7b0 DM |
6050 | my ($defFormat, $validFormats) = PVE::Storage::storage_default_format($storecfg, $storeid); |
6051 | if (!$format) { | |
d81f0f09 DM |
6052 | my $scfg = PVE::Storage::storage_config($storecfg, $storeid); |
6053 | $format = qemu_img_format($scfg, $volname); | |
152fe752 DM |
6054 | } |
6055 | ||
1377d7b0 DM |
6056 | # test if requested format is supported - else use default |
6057 | my $supported = grep { $_ eq $format } @$validFormats; | |
6058 | $format = $defFormat if !$supported; | |
6059 | ||
152fe752 DM |
6060 | my ($size) = PVE::Storage::volume_size_info($storecfg, $drive->{file}, 3); |
6061 | ||
6062 | print "create full clone of drive $drivename ($drive->{file})\n"; | |
6063 | $newvolid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $newvmid, $format, undef, ($size/1024)); | |
6064 | push @$newvollist, $newvolid; | |
6065 | ||
1dbd6d30 WL |
6066 | PVE::Storage::activate_volumes($storecfg, $newvollist); |
6067 | ||
988e2714 | 6068 | my $sparseinit = PVE::Storage::volume_has_feature($storecfg, 'sparseinit', $newvolid); |
152fe752 | 6069 | if (!$running || $snapname) { |
988e2714 | 6070 | qemu_img_convert($drive->{file}, $newvolid, $size, $snapname, $sparseinit); |
152fe752 | 6071 | } else { |
988e2714 | 6072 | qemu_drive_mirror($vmid, $drivename, $newvolid, $newvmid, $sparseinit); |
be190583 | 6073 | } |
152fe752 DM |
6074 | } |
6075 | ||
6076 | my ($size) = PVE::Storage::volume_size_info($storecfg, $newvolid, 3); | |
6077 | ||
6078 | my $disk = $drive; | |
6079 | $disk->{format} = undef; | |
6080 | $disk->{file} = $newvolid; | |
6081 | $disk->{size} = $size; | |
6082 | ||
6083 | return $disk; | |
6084 | } | |
6085 | ||
ff556cf2 DM |
6086 | # this only works if VM is running |
6087 | sub get_current_qemu_machine { | |
6088 | my ($vmid) = @_; | |
6089 | ||
6090 | my $cmd = { execute => 'query-machines', arguments => {} }; | |
8e90138a | 6091 | my $res = vm_qmp_command($vmid, $cmd); |
ff556cf2 DM |
6092 | |
6093 | my ($current, $default); | |
6094 | foreach my $e (@$res) { | |
6095 | $default = $e->{name} if $e->{'is-default'}; | |
6096 | $current = $e->{name} if $e->{'is-current'}; | |
6097 | } | |
6098 | ||
6099 | # fallback to the default machine if current is not supported by qemu | |
6100 | return $current || $default || 'pc'; | |
6101 | } | |
6102 | ||
23f73120 AD |
6103 | sub qemu_machine_feature_enabled { |
6104 | my ($machine, $kvmver, $version_major, $version_minor) = @_; | |
6105 | ||
6106 | my $current_major; | |
6107 | my $current_minor; | |
6108 | ||
6109 | if ($machine && $machine =~ m/^(pc(-i440fx|-q35)?-(\d+)\.(\d+))/) { | |
6110 | ||
6111 | $current_major = $3; | |
6112 | $current_minor = $4; | |
6113 | ||
6114 | } elsif ($kvmver =~ m/^(\d+)\.(\d+)/) { | |
6115 | ||
6116 | $current_major = $1; | |
6117 | $current_minor = $2; | |
6118 | } | |
6119 | ||
6120 | return 1 if $current_major >= $version_major && $current_minor >= $version_minor; | |
6121 | ||
6122 | ||
6123 | } | |
6124 | ||
42dbd2ee AD |
6125 | sub qemu_machine_pxe { |
6126 | my ($vmid, $conf, $machine) = @_; | |
6127 | ||
6128 | $machine = PVE::QemuServer::get_current_qemu_machine($vmid) if !$machine; | |
6129 | ||
6130 | foreach my $opt (keys %$conf) { | |
6131 | next if $opt !~ m/^net(\d+)$/; | |
6132 | my $net = PVE::QemuServer::parse_net($conf->{$opt}); | |
6133 | next if !$net; | |
6134 | my $romfile = PVE::QemuServer::vm_mon_cmd_nocheck($vmid, 'qom-get', path => $opt, property => 'romfile'); | |
6135 | return $machine.".pxe" if $romfile =~ m/pxe/; | |
6136 | last; | |
6137 | } | |
6138 | ||
d1363934 | 6139 | return $machine; |
42dbd2ee AD |
6140 | } |
6141 | ||
249c4a6c AD |
6142 | sub qemu_use_old_bios_files { |
6143 | my ($machine_type) = @_; | |
6144 | ||
6145 | return if !$machine_type; | |
6146 | ||
6147 | my $use_old_bios_files = undef; | |
6148 | ||
6149 | if ($machine_type =~ m/^(\S+)\.pxe$/) { | |
6150 | $machine_type = $1; | |
6151 | $use_old_bios_files = 1; | |
6152 | } else { | |
74cc511f | 6153 | my $kvmver = kvm_user_version(); |
249c4a6c AD |
6154 | # Note: kvm version < 2.4 use non-efi pxe files, and have problems when we |
6155 | # load new efi bios files on migration. So this hack is required to allow | |
6156 | # live migration from qemu-2.2 to qemu-2.4, which is sometimes used when | |
6157 | # updrading from proxmox-ve-3.X to proxmox-ve 4.0 | |
74cc511f | 6158 | $use_old_bios_files = !qemu_machine_feature_enabled ($machine_type, $kvmver, 2, 4); |
249c4a6c AD |
6159 | } |
6160 | ||
6161 | return ($use_old_bios_files, $machine_type); | |
6162 | } | |
6163 | ||
4543ecf0 AD |
6164 | sub lspci { |
6165 | ||
6166 | my $devices = {}; | |
6167 | ||
6168 | dir_glob_foreach("$pcisysfs/devices", '[a-f0-9]{4}:([a-f0-9]{2}:[a-f0-9]{2})\.([0-9])', sub { | |
6169 | my (undef, $id, $function) = @_; | |
6170 | my $res = { id => $id, function => $function}; | |
6171 | push @{$devices->{$id}}, $res; | |
6172 | }); | |
6173 | ||
6174 | return $devices; | |
6175 | } | |
6176 | ||
22de899a AD |
6177 | sub vm_iothreads_list { |
6178 | my ($vmid) = @_; | |
6179 | ||
6180 | my $res = vm_mon_cmd($vmid, 'query-iothreads'); | |
6181 | ||
6182 | my $iothreads = {}; | |
6183 | foreach my $iothread (@$res) { | |
6184 | $iothreads->{ $iothread->{id} } = $iothread->{"thread-id"}; | |
6185 | } | |
6186 | ||
6187 | return $iothreads; | |
6188 | } | |
6189 | ||
ee034f5c AD |
6190 | sub scsihw_infos { |
6191 | my ($conf, $drive) = @_; | |
6192 | ||
6193 | my $maxdev = 0; | |
6194 | ||
6195 | if ($conf->{scsihw} && ($conf->{scsihw} =~ m/^lsi/)) { | |
6196 | $maxdev = 7; | |
a1511b3c | 6197 | } elsif ($conf->{scsihw} && ($conf->{scsihw} eq 'virtio-scsi-single')) { |
ee034f5c AD |
6198 | $maxdev = 1; |
6199 | } else { | |
6200 | $maxdev = 256; | |
6201 | } | |
6202 | ||
6203 | my $controller = int($drive->{index} / $maxdev); | |
a1511b3c | 6204 | my $controller_prefix = ($conf->{scsihw} && $conf->{scsihw} eq 'virtio-scsi-single') ? "virtioscsi" : "scsihw"; |
ee034f5c AD |
6205 | |
6206 | return ($maxdev, $controller, $controller_prefix); | |
6207 | } | |
a1511b3c | 6208 | |
65e866e5 DM |
6209 | # bash completion helper |
6210 | ||
6211 | sub complete_backup_archives { | |
6212 | my ($cmdname, $pname, $cvalue) = @_; | |
6213 | ||
6214 | my $cfg = PVE::Storage::config(); | |
6215 | ||
6216 | my $storeid; | |
6217 | ||
6218 | if ($cvalue =~ m/^([^:]+):/) { | |
6219 | $storeid = $1; | |
6220 | } | |
6221 | ||
6222 | my $data = PVE::Storage::template_list($cfg, $storeid, 'backup'); | |
6223 | ||
6224 | my $res = []; | |
6225 | foreach my $id (keys %$data) { | |
6226 | foreach my $item (@{$data->{$id}}) { | |
6227 | next if $item->{format} !~ m/^vma\.(gz|lzo)$/; | |
6228 | push @$res, $item->{volid} if defined($item->{volid}); | |
6229 | } | |
6230 | } | |
6231 | ||
6232 | return $res; | |
6233 | } | |
6234 | ||
6235 | my $complete_vmid_full = sub { | |
6236 | my ($running) = @_; | |
6237 | ||
6238 | my $idlist = vmstatus(); | |
6239 | ||
6240 | my $res = []; | |
6241 | ||
6242 | foreach my $id (keys %$idlist) { | |
6243 | my $d = $idlist->{$id}; | |
6244 | if (defined($running)) { | |
6245 | next if $d->{template}; | |
6246 | next if $running && $d->{status} ne 'running'; | |
6247 | next if !$running && $d->{status} eq 'running'; | |
6248 | } | |
6249 | push @$res, $id; | |
6250 | ||
6251 | } | |
6252 | return $res; | |
6253 | }; | |
6254 | ||
6255 | sub complete_vmid { | |
6256 | return &$complete_vmid_full(); | |
6257 | } | |
6258 | ||
6259 | sub complete_vmid_stopped { | |
6260 | return &$complete_vmid_full(0); | |
6261 | } | |
6262 | ||
6263 | sub complete_vmid_running { | |
6264 | return &$complete_vmid_full(1); | |
6265 | } | |
6266 | ||
335af808 DM |
6267 | sub complete_storage { |
6268 | ||
6269 | my $cfg = PVE::Storage::config(); | |
6270 | my $ids = $cfg->{ids}; | |
6271 | ||
6272 | my $res = []; | |
6273 | foreach my $sid (keys %$ids) { | |
6274 | next if !PVE::Storage::storage_check_enabled($cfg, $sid, undef, 1); | |
c4c844ef | 6275 | next if !$ids->{$sid}->{content}->{images}; |
335af808 DM |
6276 | push @$res, $sid; |
6277 | } | |
6278 | ||
6279 | return $res; | |
6280 | } | |
6281 | ||
1e3baf05 | 6282 | 1; |