]>
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; |
7f8c8087 | 15 | use PVE::QemuServer::Memory qw(get_current_memory); |
b2c9558d FG |
16 | use PVE::Storage; |
17 | use PVE::Tools; | |
969eb0b8 | 18 | use PVE::Format qw(render_bytes render_duration); |
b2c9558d | 19 | |
ffda963f FG |
20 | use base qw(PVE::AbstractConfig); |
21 | ||
22 | my $nodename = PVE::INotify::nodename(); | |
23 | ||
24 | mkdir "/etc/pve/nodes/$nodename"; | |
d036e418 | 25 | mkdir "/etc/pve/nodes/$nodename/qemu-server"; |
ffda963f FG |
26 | |
27 | my $lock_dir = "/var/lock/qemu-server"; | |
28 | mkdir $lock_dir; | |
29 | ||
babf613a SR |
30 | sub assert_config_exists_on_node { |
31 | my ($vmid, $node) = @_; | |
32 | ||
33 | $node //= $nodename; | |
34 | ||
35 | my $filename = __PACKAGE__->config_file($vmid, $node); | |
36 | my $exists = -f $filename; | |
37 | ||
38 | my $type = guest_type(); | |
39 | die "unable to find configuration file for $type $vmid on node '$node'\n" | |
40 | if !$exists; | |
41 | } | |
42 | ||
ffda963f FG |
43 | # BEGIN implemented abstract methods from PVE::AbstractConfig |
44 | ||
45 | sub guest_type { | |
46 | return "VM"; | |
47 | } | |
48 | ||
49 | sub __config_max_unused_disks { | |
3aa44d3b | 50 | my ($class) = @_; |
ffda963f | 51 | |
a3597c94 | 52 | return $PVE::QemuServer::Drive::MAX_UNUSED_DISKS; |
ffda963f FG |
53 | } |
54 | ||
55 | sub config_file_lock { | |
56 | my ($class, $vmid) = @_; | |
57 | ||
58 | return "$lock_dir/lock-$vmid.conf"; | |
59 | } | |
60 | ||
61 | sub cfs_config_path { | |
62 | my ($class, $vmid, $node) = @_; | |
63 | ||
64 | $node = $nodename if !$node; | |
65 | return "nodes/$node/qemu-server/$vmid.conf"; | |
66 | } | |
67 | ||
b2c9558d FG |
68 | sub has_feature { |
69 | my ($class, $feature, $conf, $storecfg, $snapname, $running, $backup_only) = @_; | |
70 | ||
71 | my $err; | |
912792e2 | 72 | $class->foreach_volume($conf, sub { |
b2c9558d FG |
73 | my ($ds, $drive) = @_; |
74 | ||
75 | return if PVE::QemuServer::drive_is_cdrom($drive); | |
5282865b | 76 | return if $backup_only && defined($drive->{backup}) && !$drive->{backup}; |
b2c9558d FG |
77 | my $volid = $drive->{file}; |
78 | $err = 1 if !PVE::Storage::volume_has_feature($storecfg, $feature, $volid, $snapname, $running); | |
79 | }); | |
80 | ||
81 | return $err ? 0 : 1; | |
82 | } | |
83 | ||
beed0e3b FE |
84 | sub valid_volume_keys { |
85 | my ($class, $reverse) = @_; | |
86 | ||
87 | my @keys = PVE::QemuServer::Drive::valid_drive_names(); | |
88 | ||
89 | return $reverse ? reverse @keys : @keys; | |
90 | } | |
91 | ||
92 | # FIXME: adapt parse_drive to use $noerr for better error messages | |
93 | sub parse_volume { | |
94 | my ($class, $key, $volume_string, $noerr) = @_; | |
95 | ||
09253883 FE |
96 | my $volume; |
97 | if ($key eq 'vmstate') { | |
98 | eval { PVE::JSONSchema::check_format('pve-volume-id', $volume_string) }; | |
99 | if (my $err = $@) { | |
d1c1af4b | 100 | return if $noerr; |
09253883 FE |
101 | die $err; |
102 | } | |
103 | $volume = { 'file' => $volume_string }; | |
104 | } else { | |
105 | $volume = PVE::QemuServer::Drive::parse_drive($key, $volume_string); | |
106 | } | |
beed0e3b FE |
107 | |
108 | die "unable to parse volume\n" if !defined($volume) && !$noerr; | |
109 | ||
110 | return $volume; | |
111 | } | |
112 | ||
113 | sub print_volume { | |
114 | my ($class, $key, $volume) = @_; | |
115 | ||
116 | return PVE::QemuServer::Drive::print_drive($volume); | |
117 | } | |
118 | ||
119 | sub volid_key { | |
120 | my ($class) = @_; | |
121 | ||
122 | return 'file'; | |
123 | } | |
124 | ||
3aa44d3b | 125 | sub get_replicatable_volumes { |
c78f43b9 | 126 | my ($class, $storecfg, $vmid, $conf, $cleanup, $noerr) = @_; |
3aa44d3b DM |
127 | |
128 | my $volhash = {}; | |
129 | ||
130 | my $test_volid = sub { | |
f949eb77 | 131 | my ($volid, $attr) = @_; |
3aa44d3b | 132 | |
a6cb40f7 DM |
133 | return if $attr->{cdrom}; |
134 | ||
135 | return if !$cleanup && !$attr->{replicate}; | |
136 | ||
4ab3bcc8 DM |
137 | if ($volid =~ m|^/|) { |
138 | return if !$attr->{replicate}; | |
a6cb40f7 | 139 | return if $cleanup || $noerr; |
4ab3bcc8 DM |
140 | die "unable to replicate local file/device '$volid'\n"; |
141 | } | |
142 | ||
a722d4ff DM |
143 | my ($storeid, $volname) = PVE::Storage::parse_volume_id($volid, $noerr); |
144 | return if !$storeid; | |
145 | ||
6f249d94 | 146 | my $scfg = PVE::Storage::storage_config($storecfg, $storeid); |
a722d4ff DM |
147 | return if $scfg->{shared}; |
148 | ||
f7e7767f DM |
149 | my ($path, $owner, $vtype) = PVE::Storage::path($storecfg, $volid); |
150 | return if !$owner || ($owner != $vmid); | |
151 | ||
a6cb40f7 DM |
152 | if ($vtype ne 'images') { |
153 | return if $cleanup || $noerr; | |
154 | die "unable to replicate volume '$volid', type '$vtype'\n"; | |
155 | } | |
3aa44d3b DM |
156 | |
157 | if (!PVE::Storage::volume_has_feature($storecfg, 'replicate', $volid)) { | |
e5857ca8 | 158 | return if $cleanup || $noerr; |
3aa44d3b DM |
159 | die "missing replicate feature on volume '$volid'\n"; |
160 | } | |
161 | ||
162 | $volhash->{$volid} = 1; | |
163 | }; | |
164 | ||
0b7a0b78 | 165 | PVE::QemuServer::foreach_volid($conf, $test_volid); |
3aa44d3b DM |
166 | |
167 | return $volhash; | |
168 | } | |
169 | ||
185df962 AL |
170 | sub get_backup_volumes { |
171 | my ($class, $conf) = @_; | |
172 | ||
173 | my $return_volumes = []; | |
174 | ||
175 | my $test_volume = sub { | |
176 | my ($key, $drive) = @_; | |
177 | ||
178 | return if PVE::QemuServer::drive_is_cdrom($drive); | |
179 | ||
180 | my $included = $drive->{backup} // 1; | |
181 | my $reason = "backup="; | |
182 | $reason .= defined($drive->{backup}) ? 'no' : 'yes'; | |
183 | ||
184 | if ($key =~ m/^efidisk/ && (!defined($conf->{bios}) || $conf->{bios} ne 'ovmf')) { | |
185 | $included = 0; | |
186 | $reason = "efidisk but no OMVF BIOS"; | |
187 | } | |
188 | ||
189 | push @$return_volumes, { | |
190 | key => $key, | |
191 | included => $included, | |
192 | reason => $reason, | |
193 | volume_config => $drive, | |
194 | }; | |
195 | }; | |
196 | ||
197 | PVE::QemuConfig->foreach_volume($conf, $test_volume); | |
198 | ||
199 | return $return_volumes; | |
200 | } | |
201 | ||
b2c9558d | 202 | sub __snapshot_save_vmstate { |
48b4cdc2 | 203 | my ($class, $vmid, $conf, $snapname, $storecfg, $statestorage, $suspend) = @_; |
b2c9558d | 204 | |
66cebc46 DC |
205 | # use given storage or search for one from the config |
206 | my $target = $statestorage; | |
b2c9558d FG |
207 | |
208 | if (!$target) { | |
66cebc46 | 209 | $target = PVE::QemuServer::find_vmstate_storage($conf, $storecfg); |
2eeb0c93 | 210 | } |
b2c9558d | 211 | |
7f8c8087 | 212 | my $mem_size = get_current_memory($conf->{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}; | |
b2c9558d | 409 | |
c1b2092d | 410 | PVE::QemuServer::qemu_volume_snapshot_delete($vmid, $storecfg, $volid, $snapname); |
b2c9558d FG |
411 | |
412 | push @$unused, $volid; | |
413 | } | |
414 | ||
58b1a8d7 DC |
415 | sub __snapshot_rollback_hook { |
416 | my ($class, $vmid, $conf, $snap, $prepare, $data) = @_; | |
417 | ||
418 | if ($prepare) { | |
419 | # we save the machine of the current config | |
420 | $data->{oldmachine} = $conf->{machine}; | |
421 | } else { | |
e6d35c71 TL |
422 | # if we have a 'runningmachine' entry in the snapshot we use that |
423 | # for the forcemachine parameter, else we use the old logic | |
c6737ef1 DC |
424 | if (defined($conf->{runningmachine})) { |
425 | $data->{forcemachine} = $conf->{runningmachine}; | |
426 | delete $conf->{runningmachine}; | |
ea1c2110 SR |
427 | |
428 | # runningcpu is newer than runningmachine, so assume it only exists | |
429 | # here, if at all | |
430 | $data->{forcecpu} = delete $conf->{runningcpu} | |
431 | if defined($conf->{runningcpu}); | |
c6737ef1 DC |
432 | } else { |
433 | # Note: old code did not store 'machine', so we try to be smart | |
434 | # and guess the snapshot was generated with kvm 1.4 (pc-i440fx-1.4). | |
8082eb8c MF |
435 | my $machine_conf = PVE::QemuServer::Machine::parse_machine($conf->{machine}); |
436 | $data->{forcemachine} = $machine_conf->{type} || 'pc-i440fx-1.4'; | |
c6737ef1 DC |
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; | |
7f8c8087 AD |
552 | } elsif ($name eq 'max-memory') { # current usage maximum, not maximum hotpluggable |
553 | return get_current_memory($conf->{memory}) * 1024 * 1024; | |
3dd16f4a FE |
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; |