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