]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Backup.pm
api: backup/vzdump: add get_storage_param helper
[pve-manager.git] / PVE / API2 / Backup.pm
CommitLineData
ac27b58d
DM
1package PVE::API2::Backup;
2
3use strict;
4use warnings;
52878b0a 5use Digest::SHA;
1c87d344 6use UUID qw(uuid);
ac27b58d
DM
7
8use PVE::SafeSyslog;
9use PVE::Tools qw(extract_param);
2424074e 10use PVE::Cluster qw(cfs_lock_file cfs_read_file cfs_write_file);
ac27b58d
DM
11use PVE::RESTHandler;
12use PVE::RPCEnvironment;
13use PVE::JSONSchema;
14use PVE::Storage;
15use PVE::Exception qw(raise_param_exc);
16use PVE::VZDump;
2424074e 17use PVE::VZDump::Common;
6f2e57c0 18use PVE::VZDump::JobBase;
305921b1 19use PVE::Jobs; # for VZDump Jobs
9ee99910 20use Proxmox::RS::CalendarEvent;
ac27b58d
DM
21
22use base qw(PVE::RESTHandler);
23
85b9ba88
DC
24use constant ALL_DAYS => 'mon,tue,wed,thu,fri,sat,sun';
25
ac27b58d
DM
26PVE::JSONSchema::register_format('pve-day-of-week', \&verify_day_of_week);
27sub verify_day_of_week {
28 my ($value, $noerr) = @_;
29
30 return $value if $value =~ m/^(mon|tue|wed|thu|fri|sat|sun)$/;
31
32 return undef if $noerr;
33
34 die "invalid day '$value'\n";
35}
36
43b2494b
SR
37my $vzdump_job_id_prop = {
38 type => 'string',
39 description => "The job ID.",
40 maxLength => 50
41};
ac27b58d 42
2b233ecc
FE
43# NOTE: also used by the vzdump API call.
44sub assert_param_permission_common {
45 my ($rpcenv, $user, $param) = @_;
2617768f
TL
46 return if $user eq 'root@pam'; # always OK
47
48 for my $key (qw(tmpdir dumpdir script)) {
49 raise_param_exc({ $key => "Only root may set this option."}) if exists $param->{$key};
50 }
2b233ecc
FE
51
52 if (defined($param->{bwlimit}) || defined($param->{ionice}) || defined($param->{performance})) {
53 $rpcenv->check($user, "/", [ 'Sys.Modify' ]);
54 }
55}
2617768f 56
b6e56130
FE
57my sub assert_param_permission_create {
58 my ($rpcenv, $user, $param) = @_;
59 return if $user eq 'root@pam'; # always OK
60
61 assert_param_permission_common($rpcenv, $user, $param);
62
43f83ad9 63 if (my $storeid = PVE::VZDump::get_storage_param($param)) {
b6e56130 64 $rpcenv->check($user, "/storage/$storeid", [ 'Datastore.Allocate' ]);
43f83ad9 65 }
b6e56130
FE
66}
67
9f65a584 68my sub assert_param_permission_update {
b6e56130 69 my ($rpcenv, $user, $update, $delete, $current) = @_;
9f65a584
FE
70 return if $user eq 'root@pam'; # always OK
71
72 assert_param_permission_common($rpcenv, $user, $update);
73 assert_param_permission_common($rpcenv, $user, $delete);
b6e56130
FE
74
75 if ($update->{storage}) {
76 $rpcenv->check($user, "/storage/$update->{storage}", [ 'Datastore.Allocate' ])
77 } elsif ($delete->{storage}) {
78 $rpcenv->check($user, "/storage/local", [ 'Datastore.Allocate' ]);
79 }
80
81 return if !$current; # early check done
82
83 if ($current->{dumpdir}) {
84 die "only root\@pam may edit jobs with a 'dumpdir' option.";
85 } else {
43f83ad9
FE
86 if (my $storeid = PVE::VZDump::get_storage_param($current)) {
87 $rpcenv->check($user, "/storage/$storeid", [ 'Datastore.Allocate' ]);
88 }
b6e56130 89 }
9f65a584
FE
90}
91
305921b1
DC
92my $convert_to_schedule = sub {
93 my ($job) = @_;
94
95 my $starttime = $job->{starttime};
de92b4d7 96
5cc0c3a0 97 return "$starttime" if !$job->{dow}; # dow is restrictive, so none means all days
305921b1 98
5cc0c3a0
TL
99 # normalize as it could be a null-separated list previously
100 my $dow = join(',', PVE::Tools::split_list($job->{dow}));
305921b1 101
5cc0c3a0 102 return $dow eq ALL_DAYS ? "$starttime" : "$dow $starttime";
305921b1
DC
103};
104
105my $schedule_param_check = sub {
81ba0803 106 my ($param, $required) = @_;
305921b1
DC
107 if (defined($param->{schedule})) {
108 if (defined($param->{starttime})) {
109 raise_param_exc({ starttime => "'starttime' and 'schedule' cannot both be set" });
110 }
111 } elsif (!defined($param->{starttime})) {
81ba0803
DC
112 raise_param_exc({ schedule => "neither 'starttime' nor 'schedule' were set" })
113 if $required;
305921b1
DC
114 } else {
115 $param->{schedule} = $convert_to_schedule->($param);
116 }
117
118 delete $param->{starttime};
119 delete $param->{dow};
120};
121
ac27b58d 122__PACKAGE__->register_method({
60e049c2
TM
123 name => 'index',
124 path => '',
ac27b58d
DM
125 method => 'GET',
126 description => "List vzdump backup schedule.",
937515d6
DM
127 permissions => {
128 check => ['perm', '/', ['Sys.Audit']],
129 },
ac27b58d
DM
130 parameters => {
131 additionalProperties => 0,
132 properties => {},
133 },
134 returns => {
135 type => 'array',
136 items => {
137 type => "object",
138 properties => {
43b2494b 139 id => $vzdump_job_id_prop
ac27b58d
DM
140 },
141 },
142 links => [ { rel => 'child', href => "{id}" } ],
143 },
144 code => sub {
145 my ($param) = @_;
146
147 my $rpcenv = PVE::RPCEnvironment::get();
148 my $user = $rpcenv->get_user();
149
b0905e3a 150 my $data = cfs_read_file('vzdump.cron');
305921b1
DC
151 my $jobs_data = cfs_read_file('jobs.cfg');
152 my $order = $jobs_data->{order};
153 my $jobs = $jobs_data->{ids};
ac27b58d
DM
154
155 my $res = $data->{jobs} || [];
305921b1
DC
156 foreach my $job (@$res) {
157 $job->{schedule} = $convert_to_schedule->($job);
158 }
159
160 foreach my $jobid (sort { $order->{$a} <=> $order->{$b} } keys %$jobs) {
161 my $job = $jobs->{$jobid};
162 next if $job->{type} ne 'vzdump';
c048ad30
DC
163
164 if (my $schedule = $job->{schedule}) {
165 # vzdump jobs are cluster wide, there maybe was no local run
166 # so simply calculate from now
167 my $last_run = time();
9ee99910 168 my $calspec = Proxmox::RS::CalendarEvent->new($schedule);
c048ad30
DC
169 my $next_run = $calspec->compute_next_event($last_run);
170 $job->{'next-run'} = $next_run if defined($next_run);
171 }
172
082e0297
FE
173 # FIXME remove in PVE 8.0?
174 # backwards compat: before moving the job registry to pve-common, id was auto-injected
175 $job->{id} = $jobid;
176
305921b1
DC
177 push @$res, $job;
178 }
ac27b58d
DM
179
180 return $res;
181 }});
182
183__PACKAGE__->register_method({
60e049c2
TM
184 name => 'create_job',
185 path => '',
ac27b58d
DM
186 method => 'POST',
187 protected => 1,
188 description => "Create new vzdump backup job.",
937515d6
DM
189 permissions => {
190 check => ['perm', '/', ['Sys.Modify']],
f0bbc084 191 description => "The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to the 'root\@pam' user.",
937515d6 192 },
ac27b58d
DM
193 parameters => {
194 additionalProperties => 0,
2424074e 195 properties => PVE::VZDump::Common::json_config_properties({
305921b1
DC
196 id => {
197 type => 'string',
198 description => "Job ID (will be autogenerated).",
199 format => 'pve-configid',
200 optional => 1, # FIXME: make required on 8.0
201 },
202 schedule => {
203 description => "Backup schedule. The format is a subset of `systemd` calendar events.",
204 type => 'string', format => 'pve-calendar-event',
205 maxLength => 128,
206 optional => 1,
207 },
7625ea19
DM
208 starttime => {
209 type => 'string',
210 description => "Job Start time.",
211 pattern => '\d{1,2}:\d{1,2}',
212 typetext => 'HH:MM',
305921b1 213 optional => 1,
ac27b58d
DM
214 },
215 dow => {
216 type => 'string', format => 'pve-day-of-week-list',
217 optional => 1,
218 description => "Day of week selection.",
305921b1 219 requires => 'starttime',
85b9ba88 220 default => ALL_DAYS,
ac27b58d 221 },
4341db1d
TL
222 enabled => {
223 type => 'boolean',
224 optional => 1,
225 description => "Enable or disable the job.",
226 default => '1',
227 },
c61c192e
DC
228 'repeat-missed' => {
229 optional => 1,
230 type => 'boolean',
231 description => "If true, the job will be run as soon as possible if it was missed".
232 " while the scheduler was not running.",
233 default => 0,
234 },
998b61fb
DC
235 comment => {
236 optional => 1,
237 type => 'string',
238 description => "Description for the Job.",
239 maxLength => 512,
240 },
ac27b58d
DM
241 }),
242 },
243 returns => { type => 'null' },
244 code => sub {
245 my ($param) = @_;
246
247 my $rpcenv = PVE::RPCEnvironment::get();
248 my $user = $rpcenv->get_user();
249
b6e56130 250 assert_param_permission_create($rpcenv, $user, $param);
f0bbc084 251
c92c54d5
TL
252 if (my $pool = $param->{pool}) {
253 $rpcenv->check_pool_exist($pool);
254 $rpcenv->check($user, "/pool/$pool", ['VM.Backup']);
255 }
256
81ba0803 257 $schedule_param_check->($param, 1);
c92c54d5 258
305921b1
DC
259 $param->{enabled} = 1 if !defined($param->{enabled});
260
261 # autogenerate id for api compatibility FIXME remove with 8.0
ca62f164 262 my $id = extract_param($param, 'id') // UUID::uuid();
305921b1
DC
263
264 cfs_lock_file('jobs.cfg', undef, sub {
265 my $data = cfs_read_file('jobs.cfg');
266
267 die "Job '$id' already exists\n"
268 if $data->{ids}->{$id};
ac27b58d 269
200cef80 270 PVE::VZDump::verify_vzdump_parameters($param, 1);
6f2e57c0 271 my $opts = PVE::VZDump::JobBase->check_config($id, $param, 1, 1);
ac27b58d 272
305921b1 273 $data->{ids}->{$id} = $opts;
ac27b58d 274
2cf7706e 275 PVE::Jobs::create_job($id, 'vzdump', $opts);
305921b1
DC
276
277 cfs_write_file('jobs.cfg', $data);
278 });
200cef80 279 die "$@" if ($@);
ac27b58d
DM
280
281 return undef;
282 }});
283
284__PACKAGE__->register_method({
60e049c2
TM
285 name => 'read_job',
286 path => '{id}',
ac27b58d
DM
287 method => 'GET',
288 description => "Read vzdump backup job definition.",
937515d6
DM
289 permissions => {
290 check => ['perm', '/', ['Sys.Audit']],
291 },
ac27b58d
DM
292 parameters => {
293 additionalProperties => 0,
294 properties => {
43b2494b 295 id => $vzdump_job_id_prop
ac27b58d
DM
296 },
297 },
298 returns => {
299 type => 'object',
300 },
301 code => sub {
302 my ($param) = @_;
303
304 my $rpcenv = PVE::RPCEnvironment::get();
305 my $user = $rpcenv->get_user();
306
b0905e3a 307 my $data = cfs_read_file('vzdump.cron');
ac27b58d
DM
308
309 my $jobs = $data->{jobs} || [];
310
311 foreach my $job (@$jobs) {
305921b1
DC
312 if ($job->{id} eq $param->{id}) {
313 $job->{schedule} = $convert_to_schedule->($job);
314 return $job;
315 }
ac27b58d
DM
316 }
317
305921b1
DC
318 my $jobs_data = cfs_read_file('jobs.cfg');
319 my $job = $jobs_data->{ids}->{$param->{id}};
082e0297
FE
320 if ($job && $job->{type} eq 'vzdump') {
321 # FIXME remove in PVE 8.0?
322 # backwards compat: before moving the job registry to pve-common, id was auto-injected
323 $job->{id} = $param->{id};
324 return $job;
325 }
305921b1 326
ac27b58d
DM
327 raise_param_exc({ id => "No such job '$param->{id}'" });
328
329 }});
330
331__PACKAGE__->register_method({
60e049c2
TM
332 name => 'delete_job',
333 path => '{id}',
ac27b58d
DM
334 method => 'DELETE',
335 description => "Delete vzdump backup job definition.",
937515d6
DM
336 permissions => {
337 check => ['perm', '/', ['Sys.Modify']],
338 },
ac27b58d
DM
339 protected => 1,
340 parameters => {
341 additionalProperties => 0,
342 properties => {
43b2494b 343 id => $vzdump_job_id_prop
ac27b58d
DM
344 },
345 },
346 returns => { type => 'null' },
347 code => sub {
348 my ($param) = @_;
349
350 my $rpcenv = PVE::RPCEnvironment::get();
351 my $user = $rpcenv->get_user();
352
305921b1
DC
353 my $id = $param->{id};
354
200cef80
CE
355 my $delete_job = sub {
356 my $data = cfs_read_file('vzdump.cron');
ac27b58d 357
200cef80
CE
358 my $jobs = $data->{jobs} || [];
359 my $newjobs = [];
ac27b58d 360
200cef80
CE
361 my $found;
362 foreach my $job (@$jobs) {
305921b1 363 if ($job->{id} eq $id) {
200cef80
CE
364 $found = 1;
365 } else {
366 push @$newjobs, $job;
367 }
ac27b58d 368 }
ac27b58d 369
305921b1
DC
370 if (!$found) {
371 cfs_lock_file('jobs.cfg', undef, sub {
372 my $jobs_data = cfs_read_file('jobs.cfg');
373
374 if (!defined($jobs_data->{ids}->{$id})) {
375 raise_param_exc({ id => "No such job '$id'" });
376 }
377 delete $jobs_data->{ids}->{$id};
378
379 PVE::Jobs::remove_job($id, 'vzdump');
ac27b58d 380
305921b1
DC
381 cfs_write_file('jobs.cfg', $jobs_data);
382 });
383 die "$@" if $@;
384 } else {
385 $data->{jobs} = $newjobs;
ac27b58d 386
305921b1
DC
387 cfs_write_file('vzdump.cron', $data);
388 }
200cef80
CE
389 };
390 cfs_lock_file('vzdump.cron', undef, $delete_job);
391 die "$@" if ($@);
ac27b58d
DM
392
393 return undef;
394 }});
395
396__PACKAGE__->register_method({
60e049c2
TM
397 name => 'update_job',
398 path => '{id}',
ac27b58d
DM
399 method => 'PUT',
400 protected => 1,
401 description => "Update vzdump backup job definition.",
937515d6
DM
402 permissions => {
403 check => ['perm', '/', ['Sys.Modify']],
e6d963ca 404 description => "The 'tmpdir', 'dumpdir' and 'script' parameters are additionally restricted to the 'root\@pam' user.",
937515d6 405 },
ac27b58d
DM
406 parameters => {
407 additionalProperties => 0,
2424074e 408 properties => PVE::VZDump::Common::json_config_properties({
43b2494b 409 id => $vzdump_job_id_prop,
305921b1
DC
410 schedule => {
411 description => "Backup schedule. The format is a subset of `systemd` calendar events.",
412 type => 'string', format => 'pve-calendar-event',
413 maxLength => 128,
414 optional => 1,
415 },
7625ea19
DM
416 starttime => {
417 type => 'string',
418 description => "Job Start time.",
419 pattern => '\d{1,2}:\d{1,2}',
420 typetext => 'HH:MM',
305921b1 421 optional => 1,
ac27b58d
DM
422 },
423 dow => {
424 type => 'string', format => 'pve-day-of-week-list',
425 optional => 1,
305921b1 426 requires => 'starttime',
ac27b58d
DM
427 description => "Day of week selection.",
428 },
53c6bb6c
DM
429 delete => {
430 type => 'string', format => 'pve-configid-list',
431 description => "A list of settings you want to delete.",
432 optional => 1,
433 },
4341db1d
TL
434 enabled => {
435 type => 'boolean',
436 optional => 1,
437 description => "Enable or disable the job.",
438 default => '1',
439 },
c61c192e
DC
440 'repeat-missed' => {
441 optional => 1,
442 type => 'boolean',
443 description => "If true, the job will be run as soon as possible if it was missed".
444 " while the scheduler was not running.",
445 default => 0,
446 },
998b61fb
DC
447 comment => {
448 optional => 1,
449 type => 'string',
450 description => "Description for the Job.",
451 maxLength => 512,
452 },
ac27b58d
DM
453 }),
454 },
455 returns => { type => 'null' },
456 code => sub {
457 my ($param) = @_;
458
459 my $rpcenv = PVE::RPCEnvironment::get();
460 my $user = $rpcenv->get_user();
461
16f5b283
TL
462 if (my $pool = $param->{pool}) {
463 $rpcenv->check_pool_exist($pool);
464 $rpcenv->check($user, "/pool/$pool", ['VM.Backup']);
465 }
466
305921b1
DC
467 $schedule_param_check->($param);
468
469 my $id = extract_param($param, 'id');
470 my $delete = extract_param($param, 'delete');
bda3f2aa 471 $delete = { map { $_ => 1 } PVE::Tools::split_list($delete) } if $delete;
305921b1 472
9f65a584
FE
473 assert_param_permission_update($rpcenv, $user, $param, $delete);
474
200cef80
CE
475 my $update_job = sub {
476 my $data = cfs_read_file('vzdump.cron');
305921b1 477 my $jobs_data = cfs_read_file('jobs.cfg');
ac27b58d 478
200cef80 479 my $jobs = $data->{jobs} || [];
ac27b58d 480
659032f4 481 die "no options specified\n" if !scalar(keys $param->%*) && !scalar(keys $delete->%*);
53c6bb6c 482
200cef80 483 PVE::VZDump::verify_vzdump_parameters($param);
6f2e57c0 484 my $opts = PVE::VZDump::JobBase->check_config($id, $param, 0, 1);
305921b1
DC
485
486 # try to find it in old vzdump.cron and convert it to a job
487 my ($idx) = grep { $jobs->[$_]->{id} eq $id } (0 .. scalar(@$jobs) - 1);
488
489 my $job;
490 if (defined($idx)) {
491 $job = splice @$jobs, $idx, 1;
492 $job->{schedule} = $convert_to_schedule->($job);
493 delete $job->{starttime};
494 delete $job->{dow};
495 delete $job->{id};
496 $job->{type} = 'vzdump';
497 $jobs_data->{ids}->{$id} = $job;
498 } else {
499 $job = $jobs_data->{ids}->{$id};
500 die "no such vzdump job\n" if !$job || $job->{type} ne 'vzdump';
501 }
ac27b58d 502
b6e56130
FE
503 assert_param_permission_update($rpcenv, $user, $param, $delete, $job);
504
c61c192e
DC
505 my $deletable = {
506 comment => 1,
507 'repeat-missed' => 1,
508 };
509
bda3f2aa 510 for my $k (keys $delete->%*) {
c61c192e 511 if (!PVE::VZDump::option_exists($k) && !$deletable->{$k}) {
305921b1
DC
512 raise_param_exc({ delete => "unknown option '$k'" });
513 }
ac27b58d 514
305921b1
DC
515 delete $job->{$k};
516 }
53c6bb6c 517
305921b1
DC
518 foreach my $k (keys %$param) {
519 $job->{$k} = $param->{$k};
520 }
ac27b58d 521
305921b1
DC
522 $job->{all} = 1 if (defined($job->{exclude}) && !defined($job->{pool}));
523
524 if (defined($param->{vmid})) {
525 delete $job->{all};
526 delete $job->{exclude};
527 delete $job->{pool};
528 } elsif ($param->{all}) {
529 delete $job->{vmid};
530 delete $job->{pool};
531 } elsif ($job->{pool}) {
532 delete $job->{vmid};
533 delete $job->{all};
534 delete $job->{exclude};
535 }
ac27b58d 536
305921b1 537 PVE::VZDump::verify_vzdump_parameters($job, 1);
ac27b58d 538
305921b1
DC
539 if (defined($idx)) {
540 cfs_write_file('vzdump.cron', $data);
ac27b58d 541 }
305921b1 542 cfs_write_file('jobs.cfg', $jobs_data);
2cf7706e
DC
543
544 PVE::Jobs::detect_changed_runtime_props($id, 'vzdump', $job);
545
305921b1 546 return;
200cef80 547 };
305921b1
DC
548 cfs_lock_file('vzdump.cron', undef, sub {
549 cfs_lock_file('jobs.cfg', undef, $update_job);
550 die "$@" if ($@);
551 });
200cef80 552 die "$@" if ($@);
ac27b58d
DM
553 }});
554
ac0fe8b6
AL
555__PACKAGE__->register_method({
556 name => 'get_volume_backup_included',
557 path => '{id}/included_volumes',
558 method => 'GET',
559 protected => 1,
560 description => "Returns included guests and the backup status of their disks. Optimized to be used in ExtJS tree views.",
561 permissions => {
562 check => ['perm', '/', ['Sys.Audit']],
563 },
564 parameters => {
565 additionalProperties => 0,
566 properties => {
567 id => $vzdump_job_id_prop
568 },
569 },
570 returns => {
571 type => 'object',
572 description => 'Root node of the tree object. Children represent guests, grandchildren represent volumes of that guest.',
573 properties => {
574 children => {
575 type => 'array',
576 items => {
577 type => 'object',
578 properties => {
579 id => {
580 type => 'integer',
581 description => 'VMID of the guest.',
582 },
583 name => {
584 type => 'string',
585 description => 'Name of the guest',
586 optional => 1,
587 },
588 type => {
589 type => 'string',
590 description => 'Type of the guest, VM, CT or unknown for removed but not purged guests.',
591 enum => ['qemu', 'lxc', 'unknown'],
592 },
593 children => {
594 type => 'array',
595 optional => 1,
596 description => 'The volumes of the guest with the information if they will be included in backups.',
597 items => {
598 type => 'object',
599 properties => {
600 id => {
601 type => 'string',
602 description => 'Configuration key of the volume.',
603 },
604 name => {
605 type => 'string',
606 description => 'Name of the volume.',
607 },
608 included => {
609 type => 'boolean',
610 description => 'Whether the volume is included in the backup or not.',
611 },
612 reason => {
613 type => 'string',
614 description => 'The reason why the volume is included (or excluded).',
615 },
616 },
617 },
618 },
619 },
620 },
621 },
622 },
623 },
624 code => sub {
625 my ($param) = @_;
626
627 my $rpcenv = PVE::RPCEnvironment::get();
628
629 my $user = $rpcenv->get_user();
630
631 my $vzconf = cfs_read_file('vzdump.cron');
632 my $all_jobs = $vzconf->{jobs} || [];
633 my $job;
634 my $rrd = PVE::Cluster::rrd_dump();
635
636 for my $j (@$all_jobs) {
637 if ($j->{id} eq $param->{id}) {
638 $job = $j;
639 last;
640 }
641 }
305921b1
DC
642 if (!$job) {
643 my $jobs_data = cfs_read_file('jobs.cfg');
644 my $j = $jobs_data->{ids}->{$param->{id}};
645 if ($j && $j->{type} eq 'vzdump') {
646 $job = $j;
647 }
648 }
ac0fe8b6
AL
649 raise_param_exc({ id => "No such job '$param->{id}'" }) if !$job;
650
651 my $vmlist = PVE::Cluster::get_vmlist();
652
653 my @job_vmids;
654
655 my $included_guests = PVE::VZDump::get_included_guests($job);
656
657 for my $node (keys %{$included_guests}) {
658 my $node_vmids = $included_guests->{$node};
659 push(@job_vmids, @{$node_vmids});
660 }
661
662 # remove VMIDs to which the user has no permission to not leak infos
663 # like the guest name
664 my @allowed_vmids = grep {
665 $rpcenv->check($user, "/vms/$_", [ 'VM.Audit' ], 1);
666 } @job_vmids;
667
668 my $result = {
669 children => [],
670 };
671
672 for my $vmid (@allowed_vmids) {
673
674 my $children = [];
675
676 # It's possible that a job has VMIDs configured that are not in
677 # vmlist. This could be because a guest was removed but not purged.
678 # Since there is no more data available we can only deliver the VMID
679 # and no volumes.
680 if (!defined $vmlist->{ids}->{$vmid}) {
681 push(@{$result->{children}}, {
682 id => int($vmid),
683 type => 'unknown',
684 leaf => 1,
685 });
686 next;
687 }
688
689 my $type = $vmlist->{ids}->{$vmid}->{type};
690 my $node = $vmlist->{ids}->{$vmid}->{node};
691
692 my $conf;
693 my $volumes;
694 my $name = "";
695
696 if ($type eq 'qemu') {
697 $conf = PVE::QemuConfig->load_config($vmid, $node);
698 $volumes = PVE::QemuConfig->get_backup_volumes($conf);
699 $name = $conf->{name};
700 } elsif ($type eq 'lxc') {
701 $conf = PVE::LXC::Config->load_config($vmid, $node);
702 $volumes = PVE::LXC::Config->get_backup_volumes($conf);
703 $name = $conf->{hostname};
704 } else {
705 die "VMID $vmid is neither Qemu nor LXC guest\n";
706 }
707
708 foreach my $volume (@$volumes) {
709 my $disk = {
710 # id field must be unique for ExtJS tree view
711 id => "$vmid:$volume->{key}",
712 name => $volume->{volume_config}->{file} // $volume->{volume_config}->{volume},
713 included=> $volume->{included},
714 reason => $volume->{reason},
715 leaf => 1,
716 };
717 push(@{$children}, $disk);
718 }
719
720 my $leaf = 0;
721 # it's possible for a guest to have no volumes configured
722 $leaf = 1 if !@{$children};
723
724 push(@{$result->{children}}, {
725 id => int($vmid),
726 type => $type,
727 name => $name,
728 children => $children,
729 leaf => $leaf,
730 });
731 }
732
733 return $result;
734 }});
735
ac27b58d 7361;