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