]>
Commit | Line | Data |
---|---|---|
ffda963f FG |
1 | package PVE::QemuConfig; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
b2c9558d FG |
6 | use PVE::AbstractConfig; |
7 | use PVE::INotify; | |
09253883 | 8 | use PVE::JSONSchema; |
ea1c2110 | 9 | use PVE::QemuServer::CPUConfig; |
a3597c94 | 10 | use PVE::QemuServer::Drive; |
d036e418 | 11 | use PVE::QemuServer::Helpers; |
0a13e08e | 12 | use PVE::QemuServer::Monitor qw(mon_cmd); |
b2c9558d | 13 | use PVE::QemuServer; |
3392d6ca | 14 | use PVE::QemuServer::Machine; |
b2c9558d FG |
15 | use PVE::Storage; |
16 | use PVE::Tools; | |
969eb0b8 | 17 | use PVE::Format qw(render_bytes render_duration); |
b2c9558d | 18 | |
ffda963f FG |
19 | use base qw(PVE::AbstractConfig); |
20 | ||
21 | my $nodename = PVE::INotify::nodename(); | |
22 | ||
23 | mkdir "/etc/pve/nodes/$nodename"; | |
d036e418 | 24 | mkdir "/etc/pve/nodes/$nodename/qemu-server"; |
ffda963f FG |
25 | |
26 | my $lock_dir = "/var/lock/qemu-server"; | |
27 | mkdir $lock_dir; | |
28 | ||
babf613a SR |
29 | sub assert_config_exists_on_node { |
30 | my ($vmid, $node) = @_; | |
31 | ||
32 | $node //= $nodename; | |
33 | ||
34 | my $filename = __PACKAGE__->config_file($vmid, $node); | |
35 | my $exists = -f $filename; | |
36 | ||
37 | my $type = guest_type(); | |
38 | die "unable to find configuration file for $type $vmid on node '$node'\n" | |
39 | if !$exists; | |
40 | } | |
41 | ||
ffda963f FG |
42 | # BEGIN implemented abstract methods from PVE::AbstractConfig |
43 | ||
44 | sub guest_type { | |
45 | return "VM"; | |
46 | } | |
47 | ||
48 | sub __config_max_unused_disks { | |
3aa44d3b | 49 | my ($class) = @_; |
ffda963f | 50 | |
a3597c94 | 51 | return $PVE::QemuServer::Drive::MAX_UNUSED_DISKS; |
ffda963f FG |
52 | } |
53 | ||
54 | sub config_file_lock { | |
55 | my ($class, $vmid) = @_; | |
56 | ||
57 | return "$lock_dir/lock-$vmid.conf"; | |
58 | } | |
59 | ||
60 | sub cfs_config_path { | |
61 | my ($class, $vmid, $node) = @_; | |
62 | ||
63 | $node = $nodename if !$node; | |
64 | return "nodes/$node/qemu-server/$vmid.conf"; | |
65 | } | |
66 | ||
b2c9558d FG |
67 | sub has_feature { |
68 | my ($class, $feature, $conf, $storecfg, $snapname, $running, $backup_only) = @_; | |
69 | ||
70 | my $err; | |
912792e2 | 71 | $class->foreach_volume($conf, sub { |
b2c9558d FG |
72 | my ($ds, $drive) = @_; |
73 | ||
74 | return if PVE::QemuServer::drive_is_cdrom($drive); | |
5282865b | 75 | return if $backup_only && defined($drive->{backup}) && !$drive->{backup}; |
b2c9558d FG |
76 | my $volid = $drive->{file}; |
77 | $err = 1 if !PVE::Storage::volume_has_feature($storecfg, $feature, $volid, $snapname, $running); | |
78 | }); | |
79 | ||
80 | return $err ? 0 : 1; | |
81 | } | |
82 | ||
beed0e3b FE |
83 | sub valid_volume_keys { |
84 | my ($class, $reverse) = @_; | |
85 | ||
86 | my @keys = PVE::QemuServer::Drive::valid_drive_names(); | |
87 | ||
88 | return $reverse ? reverse @keys : @keys; | |
89 | } | |
90 | ||
91 | # FIXME: adapt parse_drive to use $noerr for better error messages | |
92 | sub parse_volume { | |
93 | my ($class, $key, $volume_string, $noerr) = @_; | |
94 | ||
09253883 FE |
95 | my $volume; |
96 | if ($key eq 'vmstate') { | |
97 | eval { PVE::JSONSchema::check_format('pve-volume-id', $volume_string) }; | |
98 | if (my $err = $@) { | |
d1c1af4b | 99 | return if $noerr; |
09253883 FE |
100 | die $err; |
101 | } | |
102 | $volume = { 'file' => $volume_string }; | |
103 | } else { | |
104 | $volume = PVE::QemuServer::Drive::parse_drive($key, $volume_string); | |
105 | } | |
beed0e3b FE |
106 | |
107 | die "unable to parse volume\n" if !defined($volume) && !$noerr; | |
108 | ||
109 | return $volume; | |
110 | } | |
111 | ||
112 | sub print_volume { | |
113 | my ($class, $key, $volume) = @_; | |
114 | ||
115 | return PVE::QemuServer::Drive::print_drive($volume); | |
116 | } | |
117 | ||
118 | sub volid_key { | |
119 | my ($class) = @_; | |
120 | ||
121 | return 'file'; | |
122 | } | |
123 | ||
3aa44d3b | 124 | sub get_replicatable_volumes { |
c78f43b9 | 125 | my ($class, $storecfg, $vmid, $conf, $cleanup, $noerr) = @_; |
3aa44d3b DM |
126 | |
127 | my $volhash = {}; | |
128 | ||
129 | my $test_volid = sub { | |
f949eb77 | 130 | my ($volid, $attr) = @_; |
3aa44d3b | 131 | |
a6cb40f7 DM |
132 | return if $attr->{cdrom}; |
133 | ||
134 | return if !$cleanup && !$attr->{replicate}; | |
135 | ||
4ab3bcc8 DM |
136 | if ($volid =~ m|^/|) { |
137 | return if !$attr->{replicate}; | |
a6cb40f7 | 138 | return if $cleanup || $noerr; |
4ab3bcc8 DM |
139 | die "unable to replicate local file/device '$volid'\n"; |
140 | } | |
141 | ||
a722d4ff DM |
142 | my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, $noerr); |
143 | return if !$storeid; | |
144 | ||
6f249d94 | 145 | my $scfg = PVE::Storage::storage_config($storecfg, $storeid); |
a722d4ff DM |
146 | return if $scfg->{shared}; |
147 | ||
f7e7767f DM |
148 | my ($path, $owner, $vtype) = PVE::Storage::path($storecfg, $volid); |
149 | return if !$owner || ($owner != $vmid); | |
150 | ||
a6cb40f7 DM |
151 | if ($vtype ne 'images') { |
152 | return if $cleanup || $noerr; | |
153 | die "unable to replicate volume '$volid', type '$vtype'\n"; | |
154 | } | |
3aa44d3b DM |
155 | |
156 | if (!PVE::Storage::volume_has_feature($storecfg, 'replicate', $volid)) { | |
e5857ca8 | 157 | return if $cleanup || $noerr; |
3aa44d3b DM |
158 | die "missing replicate feature on volume '$volid'\n"; |
159 | } | |
160 | ||
161 | $volhash->{$volid} = 1; | |
162 | }; | |
163 | ||
f949eb77 | 164 | PVE::QemuServer::foreach_volid($conf, $test_volid); |
3aa44d3b DM |
165 | |
166 | return $volhash; | |
167 | } | |
168 | ||
185df962 AL |
169 | sub get_backup_volumes { |
170 | my ($class, $conf) = @_; | |
171 | ||
172 | my $return_volumes = []; | |
173 | ||
174 | my $test_volume = sub { | |
175 | my ($key, $drive) = @_; | |
176 | ||
177 | return if PVE::QemuServer::drive_is_cdrom($drive); | |
178 | ||
179 | my $included = $drive->{backup} // 1; | |
180 | my $reason = "backup="; | |
181 | $reason .= defined($drive->{backup}) ? 'no' : 'yes'; | |
182 | ||
183 | if ($key =~ m/^efidisk/ && (!defined($conf->{bios}) || $conf->{bios} ne 'ovmf')) { | |
184 | $included = 0; | |
185 | $reason = "efidisk but no OMVF BIOS"; | |
186 | } | |
187 | ||
188 | push @$return_volumes, { | |
189 | key => $key, | |
190 | included => $included, | |
191 | reason => $reason, | |
192 | volume_config => $drive, | |
193 | }; | |
194 | }; | |
195 | ||
196 | PVE::QemuConfig->foreach_volume($conf, $test_volume); | |
197 | ||
198 | return $return_volumes; | |
199 | } | |
200 | ||
b2c9558d | 201 | sub __snapshot_save_vmstate { |
48b4cdc2 | 202 | my ($class, $vmid, $conf, $snapname, $storecfg, $statestorage, $suspend) = @_; |
b2c9558d | 203 | |
66cebc46 DC |
204 | # use given storage or search for one from the config |
205 | my $target = $statestorage; | |
b2c9558d FG |
206 | |
207 | if (!$target) { | |
66cebc46 | 208 | $target = PVE::QemuServer::find_vmstate_storage($conf, $storecfg); |
2eeb0c93 | 209 | } |
b2c9558d | 210 | |
22ea69ca AA |
211 | my $defaults = PVE::QemuServer::load_defaults(); |
212 | my $mem_size = $conf->{memory} // $defaults->{memory}; | |
566caaa4 | 213 | my $driver_state_size = 500; # assume 500MB is enough to safe all driver state; |
b3983fa1 DC |
214 | # our savevm-start does live-save of the memory until the space left in the |
215 | # volume is just enough for the remaining memory content + internal state | |
216 | # then it stops the vm and copies the rest so we reserve twice the | |
217 | # memory content + state to minimize vm downtime | |
22ea69ca | 218 | my $size = $mem_size*2 + $driver_state_size; |
159719e5 | 219 | my $scfg = PVE::Storage::storage_config($storecfg, $target); |
b2c9558d FG |
220 | |
221 | my $name = "vm-$vmid-state-$snapname"; | |
b2c9558d | 222 | $name .= ".raw" if $scfg->{path}; # add filename extension for file base storage |
159719e5 DC |
223 | |
224 | my $statefile = PVE::Storage::vdisk_alloc($storecfg, $target, $vmid, 'raw', $name, $size*1024); | |
3392d6ca | 225 | my $runningmachine = PVE::QemuServer::Machine::get_current_qemu_machine($vmid); |
159719e5 | 226 | |
ea1c2110 SR |
227 | # get current QEMU -cpu argument to ensure consistency of custom CPU models |
228 | my $runningcpu; | |
229 | if (my $pid = PVE::QemuServer::check_running($vmid)) { | |
230 | $runningcpu = PVE::QemuServer::CPUConfig::get_cpu_from_running_vm($pid); | |
231 | } | |
232 | ||
233 | if (!$suspend) { | |
234 | $conf = $conf->{snapshots}->{$snapname}; | |
159719e5 DC |
235 | } |
236 | ||
ea1c2110 SR |
237 | $conf->{vmstate} = $statefile; |
238 | $conf->{runningmachine} = $runningmachine; | |
239 | $conf->{runningcpu} = $runningcpu; | |
240 | ||
159719e5 | 241 | return $statefile; |
b2c9558d FG |
242 | } |
243 | ||
fe2c5069 FE |
244 | sub __snapshot_activate_storages { |
245 | my ($class, $conf, $include_vmstate) = @_; | |
246 | ||
247 | my $storecfg = PVE::Storage::config(); | |
248 | my $opts = $include_vmstate ? { 'extra_keys' => ['vmstate'] } : {}; | |
249 | my $storage_hash = {}; | |
250 | ||
251 | $class->foreach_volume_full($conf, $opts, sub { | |
252 | my ($key, $drive) = @_; | |
253 | ||
254 | return if PVE::QemuServer::drive_is_cdrom($drive); | |
255 | ||
256 | my ($storeid) = PVE::Storage::parse_volume_id($drive->{file}); | |
257 | $storage_hash->{$storeid} = 1; | |
258 | }); | |
259 | ||
260 | PVE::Storage::activate_storage_list($storecfg, [ sort keys $storage_hash->%* ]); | |
261 | } | |
262 | ||
b2c9558d FG |
263 | sub __snapshot_check_running { |
264 | my ($class, $vmid) = @_; | |
babf613a | 265 | return PVE::QemuServer::Helpers::vm_running_locally($vmid); |
b2c9558d FG |
266 | } |
267 | ||
268 | sub __snapshot_check_freeze_needed { | |
269 | my ($class, $vmid, $config, $save_vmstate) = @_; | |
270 | ||
271 | my $running = $class->__snapshot_check_running($vmid); | |
278e2c9d | 272 | if (!$save_vmstate) { |
9d66b397 | 273 | return ($running, $running && PVE::QemuServer::parse_guest_agent($config)->{enabled} && PVE::QemuServer::qga_check_running($vmid)); |
b2c9558d FG |
274 | } else { |
275 | return ($running, 0); | |
276 | } | |
277 | } | |
278 | ||
279 | sub __snapshot_freeze { | |
280 | my ($class, $vmid, $unfreeze) = @_; | |
281 | ||
282 | if ($unfreeze) { | |
0a13e08e | 283 | eval { mon_cmd($vmid, "guest-fsfreeze-thaw"); }; |
b2c9558d FG |
284 | warn "guest-fsfreeze-thaw problems - $@" if $@; |
285 | } else { | |
0a13e08e | 286 | eval { mon_cmd($vmid, "guest-fsfreeze-freeze"); }; |
b2c9558d FG |
287 | warn "guest-fsfreeze-freeze problems - $@" if $@; |
288 | } | |
289 | } | |
290 | ||
291 | sub __snapshot_create_vol_snapshots_hook { | |
292 | my ($class, $vmid, $snap, $running, $hook) = @_; | |
293 | ||
294 | if ($running) { | |
3a8deb55 AD |
295 | my $storecfg = PVE::Storage::config(); |
296 | ||
b2c9558d FG |
297 | if ($hook eq "before") { |
298 | if ($snap->{vmstate}) { | |
b2c9558d | 299 | my $path = PVE::Storage::path($storecfg, $snap->{vmstate}); |
3a8deb55 | 300 | PVE::Storage::activate_volumes($storecfg, [$snap->{vmstate}]); |
f97224b1 | 301 | my $state_storage_id = PVE::Storage::parse_volume_id($snap->{vmstate}); |
3a8deb55 | 302 | |
27a5be53 | 303 | PVE::QemuServer::set_migration_caps($vmid, 1); |
0a13e08e | 304 | mon_cmd($vmid, "savevm-start", statefile => $path); |
f97224b1 | 305 | print "saving VM state and RAM using storage '$state_storage_id'\n"; |
969eb0b8 SR |
306 | my $render_state = sub { |
307 | my ($stat) = @_; | |
308 | my $b = render_bytes($stat->{bytes}); | |
309 | my $t = render_duration($stat->{'total-time'} / 1000); | |
310 | return ($b, $t); | |
311 | }; | |
98308873 | 312 | my $round = 0; |
b2c9558d | 313 | for(;;) { |
98308873 | 314 | $round++; |
0a13e08e | 315 | my $stat = mon_cmd($vmid, "query-savevm"); |
b2c9558d FG |
316 | if (!$stat->{status}) { |
317 | die "savevm not active\n"; | |
318 | } elsif ($stat->{status} eq 'active') { | |
98308873 TL |
319 | if ($round < 60 || $round % 10 == 0) { |
320 | my ($b, $t) = $render_state->($stat); | |
321 | print "$b in $t\n"; | |
322 | } | |
323 | print "reducing reporting rate to every 10s\n" if $round == 60; | |
b2c9558d FG |
324 | sleep(1); |
325 | next; | |
326 | } elsif ($stat->{status} eq 'completed') { | |
969eb0b8 | 327 | my ($b, $t) = $render_state->($stat); |
5c3f7825 | 328 | print "completed saving the VM state in $t, saved $b\n"; |
b2c9558d | 329 | last; |
d064764a FE |
330 | } elsif ($stat->{status} eq 'failed') { |
331 | my $err = $stat->{error} || 'unknown error'; | |
332 | die "unable to save VM state and RAM - $err\n"; | |
b2c9558d | 333 | } else { |
5c3f7825 | 334 | die "query-savevm returned unexpected status '$stat->{status}'\n"; |
b2c9558d FG |
335 | } |
336 | } | |
337 | } else { | |
0a13e08e | 338 | mon_cmd($vmid, "savevm-start"); |
b2c9558d FG |
339 | } |
340 | } elsif ($hook eq "after") { | |
a022e3fd | 341 | eval { |
0a13e08e | 342 | mon_cmd($vmid, "savevm-end"); |
3a8deb55 AD |
343 | PVE::Storage::deactivate_volumes($storecfg, [$snap->{vmstate}]) if $snap->{vmstate}; |
344 | }; | |
b2c9558d FG |
345 | warn $@ if $@; |
346 | } elsif ($hook eq "after-freeze") { | |
347 | # savevm-end is async, we need to wait | |
348 | for (;;) { | |
0a13e08e | 349 | my $stat = mon_cmd($vmid, "query-savevm"); |
b2c9558d FG |
350 | if (!$stat->{bytes}) { |
351 | last; | |
352 | } else { | |
353 | print "savevm not yet finished\n"; | |
354 | sleep(1); | |
355 | next; | |
356 | } | |
357 | } | |
358 | } | |
359 | } | |
360 | } | |
361 | ||
362 | sub __snapshot_create_vol_snapshot { | |
363 | my ($class, $vmid, $ds, $drive, $snapname) = @_; | |
364 | ||
365 | return if PVE::QemuServer::drive_is_cdrom($drive); | |
366 | ||
367 | my $volid = $drive->{file}; | |
368 | my $device = "drive-$ds"; | |
369 | my $storecfg = PVE::Storage::config(); | |
370 | ||
8828460b SR |
371 | print "snapshotting '$device' ($drive->{file})\n"; |
372 | ||
b2c9558d FG |
373 | PVE::QemuServer::qemu_volume_snapshot($vmid, $device, $storecfg, $volid, $snapname); |
374 | } | |
375 | ||
376 | sub __snapshot_delete_remove_drive { | |
377 | my ($class, $snap, $remove_drive) = @_; | |
378 | ||
379 | if ($remove_drive eq 'vmstate') { | |
380 | delete $snap->{$remove_drive}; | |
381 | } else { | |
382 | my $drive = PVE::QemuServer::parse_drive($remove_drive, $snap->{$remove_drive}); | |
383 | return if PVE::QemuServer::drive_is_cdrom($drive); | |
384 | ||
385 | my $volid = $drive->{file}; | |
386 | delete $snap->{$remove_drive}; | |
387 | $class->add_unused_volume($snap, $volid); | |
388 | } | |
389 | } | |
390 | ||
391 | sub __snapshot_delete_vmstate_file { | |
392 | my ($class, $snap, $force) = @_; | |
393 | ||
394 | my $storecfg = PVE::Storage::config(); | |
395 | ||
396 | eval { PVE::Storage::vdisk_free($storecfg, $snap->{vmstate}); }; | |
397 | if (my $err = $@) { | |
398 | die $err if !$force; | |
399 | warn $err; | |
400 | } | |
401 | } | |
402 | ||
403 | sub __snapshot_delete_vol_snapshot { | |
404 | my ($class, $vmid, $ds, $drive, $snapname, $unused) = @_; | |
405 | ||
406 | return if PVE::QemuServer::drive_is_cdrom($drive); | |
407 | my $storecfg = PVE::Storage::config(); | |
408 | my $volid = $drive->{file}; | |
409 | my $device = "drive-$ds"; | |
410 | ||
411 | PVE::QemuServer::qemu_volume_snapshot_delete($vmid, $device, $storecfg, $volid, $snapname); | |
412 | ||
413 | push @$unused, $volid; | |
414 | } | |
415 | ||
58b1a8d7 DC |
416 | sub __snapshot_rollback_hook { |
417 | my ($class, $vmid, $conf, $snap, $prepare, $data) = @_; | |
418 | ||
419 | if ($prepare) { | |
420 | # we save the machine of the current config | |
421 | $data->{oldmachine} = $conf->{machine}; | |
422 | } else { | |
e6d35c71 TL |
423 | # if we have a 'runningmachine' entry in the snapshot we use that |
424 | # for the forcemachine parameter, else we use the old logic | |
c6737ef1 DC |
425 | if (defined($conf->{runningmachine})) { |
426 | $data->{forcemachine} = $conf->{runningmachine}; | |
427 | delete $conf->{runningmachine}; | |
ea1c2110 SR |
428 | |
429 | # runningcpu is newer than runningmachine, so assume it only exists | |
430 | # here, if at all | |
431 | $data->{forcecpu} = delete $conf->{runningcpu} | |
432 | if defined($conf->{runningcpu}); | |
c6737ef1 DC |
433 | } else { |
434 | # Note: old code did not store 'machine', so we try to be smart | |
435 | # and guess the snapshot was generated with kvm 1.4 (pc-i440fx-1.4). | |
436 | $data->{forcemachine} = $conf->{machine} || 'pc-i440fx-1.4'; | |
437 | ||
438 | # we remove the 'machine' configuration if not explicitly specified | |
439 | # in the original config. | |
440 | delete $conf->{machine} if $snap->{vmstate} && !defined($data->{oldmachine}); | |
441 | } | |
6ee499ff DC |
442 | |
443 | if ($conf->{vmgenid}) { | |
4f4d9772 TL |
444 | # tell the VM that it's another generation, so it can react |
445 | # appropriately, e.g. dirty-mark copies of distributed databases or | |
446 | # re-initializing its random number generator | |
6ee499ff DC |
447 | $conf->{vmgenid} = PVE::QemuServer::generate_uuid(); |
448 | } | |
58b1a8d7 DC |
449 | } |
450 | ||
451 | return; | |
452 | } | |
453 | ||
b2c9558d | 454 | sub __snapshot_rollback_vol_possible { |
7a16336d | 455 | my ($class, $drive, $snapname, $blockers) = @_; |
b2c9558d FG |
456 | |
457 | return if PVE::QemuServer::drive_is_cdrom($drive); | |
458 | ||
459 | my $storecfg = PVE::Storage::config(); | |
460 | my $volid = $drive->{file}; | |
461 | ||
7a16336d | 462 | PVE::Storage::volume_rollback_is_possible($storecfg, $volid, $snapname, $blockers); |
b2c9558d FG |
463 | } |
464 | ||
465 | sub __snapshot_rollback_vol_rollback { | |
466 | my ($class, $drive, $snapname) = @_; | |
467 | ||
468 | return if PVE::QemuServer::drive_is_cdrom($drive); | |
469 | ||
470 | my $storecfg = PVE::Storage::config(); | |
471 | PVE::Storage::volume_snapshot_rollback($storecfg, $drive->{file}, $snapname); | |
472 | } | |
473 | ||
474 | sub __snapshot_rollback_vm_stop { | |
475 | my ($class, $vmid) = @_; | |
476 | ||
477 | my $storecfg = PVE::Storage::config(); | |
478 | PVE::QemuServer::vm_stop($storecfg, $vmid, undef, undef, 5, undef, undef); | |
479 | } | |
480 | ||
481 | sub __snapshot_rollback_vm_start { | |
58b1a8d7 | 482 | my ($class, $vmid, $vmstate, $data) = @_; |
b2c9558d FG |
483 | |
484 | my $storecfg = PVE::Storage::config(); | |
0c498cca FG |
485 | my $params = { |
486 | statefile => $vmstate, | |
487 | forcemachine => $data->{forcemachine}, | |
ea1c2110 | 488 | forcecpu => $data->{forcecpu}, |
0c498cca FG |
489 | }; |
490 | PVE::QemuServer::vm_start($storecfg, $vmid, $params); | |
b2c9558d FG |
491 | } |
492 | ||
c4a54ed5 FG |
493 | sub __snapshot_rollback_get_unused { |
494 | my ($class, $conf, $snap) = @_; | |
495 | ||
496 | my $unused = []; | |
497 | ||
beed0e3b | 498 | $class->foreach_volume($conf, sub { |
c4a54ed5 FG |
499 | my ($vs, $volume) = @_; |
500 | ||
f9408af4 | 501 | return if PVE::QemuServer::drive_is_cdrom($volume, 1); |
c4a54ed5 FG |
502 | |
503 | my $found = 0; | |
504 | my $volid = $volume->{file}; | |
505 | ||
beed0e3b | 506 | $class->foreach_volume($snap, sub { |
c4a54ed5 FG |
507 | my ($ds, $drive) = @_; |
508 | ||
509 | return if $found; | |
f9408af4 | 510 | return if PVE::QemuServer::drive_is_cdrom($drive, 1); |
c4a54ed5 FG |
511 | |
512 | $found = 1 | |
513 | if ($drive->{file} && $drive->{file} eq $volid); | |
514 | }); | |
515 | ||
516 | push @$unused, $volid if !$found; | |
517 | }); | |
518 | ||
519 | return $unused; | |
520 | } | |
521 | ||
f9408af4 ML |
522 | sub add_unused_volume { |
523 | my ($class, $config, $volid) = @_; | |
524 | ||
525 | if ($volid =~ m/vm-\d+-cloudinit/) { | |
526 | print "found unused cloudinit disk '$volid', removing it\n"; | |
527 | my $storecfg = PVE::Storage::config(); | |
528 | PVE::Storage::vdisk_free($storecfg, $volid); | |
529 | return undef; | |
530 | } else { | |
531 | return $class->SUPER::add_unused_volume($config, $volid); | |
532 | } | |
533 | } | |
534 | ||
ae37db24 AD |
535 | sub load_current_config { |
536 | my ($class, $vmid, $current) = @_; | |
537 | ||
538 | my $conf = $class->SUPER::load_current_config($vmid, $current); | |
539 | delete $conf->{cloudinit}; | |
540 | return $conf; | |
541 | } | |
542 | ||
3dd16f4a FE |
543 | sub get_derived_property { |
544 | my ($class, $conf, $name) = @_; | |
545 | ||
546 | my $defaults = PVE::QemuServer::load_defaults(); | |
547 | ||
548 | if ($name eq 'max-cpu') { | |
549 | my $cpus = | |
550 | ($conf->{sockets} || $defaults->{sockets}) * ($conf->{cores} || $defaults->{cores}); | |
551 | return $conf->{vcpus} || $cpus; | |
552 | } elsif ($name eq 'max-memory') { | |
553 | return ($conf->{memory} || $defaults->{memory}) * 1024 * 1024; | |
554 | } else { | |
555 | die "unknown derived property - $name\n"; | |
556 | } | |
557 | } | |
558 | ||
ffda963f FG |
559 | # END implemented abstract methods from PVE::AbstractConfig |
560 | ||
39b56b16 FE |
561 | sub has_cloudinit { |
562 | my ($class, $conf, $skip) = @_; | |
563 | ||
564 | my $found; | |
565 | ||
566 | $class->foreach_volume($conf, sub { | |
567 | my ($key, $volume) = @_; | |
568 | ||
569 | return if ($skip && $skip eq $key) || $found; | |
570 | $found = $key if PVE::QemuServer::Drive::drive_is_cloudinit($volume); | |
571 | }); | |
572 | ||
573 | return $found; | |
574 | } | |
575 | ||
ffda963f | 576 | 1; |