]>
Commit | Line | Data |
---|---|---|
a0208dbd SI |
1 | package PMG::API2::PBS::Job; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use POSIX qw(strftime); | |
7 | ||
8 | use PVE::JSONSchema qw(get_standard_option); | |
9 | use PVE::RESTHandler; | |
10 | use PVE::SafeSyslog; | |
11 | use PVE::Tools qw(extract_param); | |
12 | use PVE::PBSClient; | |
13 | ||
14 | use PMG::RESTEnvironment; | |
15 | use PMG::Backup; | |
16 | use PMG::PBSConfig; | |
7251cc51 | 17 | use PMG::PBSSchedule; |
a0208dbd SI |
18 | |
19 | use base qw(PVE::RESTHandler); | |
20 | ||
21 | __PACKAGE__->register_method ({ | |
22 | name => 'list', | |
23 | path => '', | |
24 | method => 'GET', | |
25 | description => "List all configured Proxmox Backup Server jobs.", | |
26 | permissions => { check => [ 'admin', 'audit' ] }, | |
27 | proxyto => 'node', | |
28 | protected => 1, | |
29 | parameters => { | |
30 | additionalProperties => 0, | |
31 | properties => { | |
32 | node => get_standard_option('pve-node'), | |
33 | }, | |
34 | }, | |
35 | returns => { | |
2ea79c2a TL |
36 | type => "array", |
37 | items => PMG::PBSConfig->createSchema(1), | |
38 | links => [ { rel => 'child', href => "{remote}" } ], | |
a0208dbd SI |
39 | }, |
40 | code => sub { | |
41 | my ($param) = @_; | |
42 | ||
43 | my $res = []; | |
44 | ||
45 | my $conf = PMG::PBSConfig->new(); | |
2ea79c2a TL |
46 | return $res if !defined($conf); |
47 | ||
48 | foreach my $remote (keys %{$conf->{ids}}) { | |
49 | my $d = $conf->{ids}->{$remote}; | |
50 | push @$res, { | |
51 | remote => $remote, | |
52 | server => $d->{server}, | |
53 | datastore => $d->{datastore}, | |
54 | }; | |
a0208dbd SI |
55 | } |
56 | ||
57 | return $res; | |
58 | }}); | |
59 | ||
60 | __PACKAGE__->register_method ({ | |
61 | name => 'remote_index', | |
62 | path => '{remote}', | |
63 | method => 'GET', | |
64 | description => "Backup Job index.", | |
65 | parameters => { | |
66 | additionalProperties => 0, | |
67 | properties => { | |
68 | node => get_standard_option('pve-node'), | |
69 | remote => { | |
70 | description => "Proxmox Backup Server ID.", | |
71 | type => 'string', format => 'pve-configid', | |
72 | }, | |
73 | }, | |
74 | }, | |
75 | returns => { | |
76 | type => 'array', | |
77 | items => { | |
78 | type => "object", | |
79 | properties => { section => { type => 'string'} }, | |
80 | }, | |
81 | links => [ { rel => 'child', href => "{section}" } ], | |
82 | }, | |
83 | code => sub { | |
84 | my ($param) = @_; | |
85 | ||
86 | my $result = [ | |
0e368d37 | 87 | { section => 'snapshot' }, |
a0208dbd SI |
88 | { section => 'timer' }, |
89 | ]; | |
90 | return $result; | |
91 | }}); | |
92 | ||
0e368d37 TL |
93 | |
94 | my sub get_snapshots { | |
95 | my ($remote, $group) = @_; | |
96 | ||
97 | my $conf = PMG::PBSConfig->new(); | |
98 | ||
99 | my $remote_config = $conf->{ids}->{$remote}; | |
100 | die "PBS remote '$remote' does not exist\n" if !$remote_config; | |
101 | ||
102 | my $res = []; | |
103 | return $res if $remote_config->{disable}; | |
104 | ||
105 | my $pbs = PVE::PBSClient->new($remote_config, $remote, $conf->{secret_dir}); | |
106 | ||
107 | my $snapshots = $pbs->get_snapshots($group); | |
108 | foreach my $item (@$snapshots) { | |
109 | my ($type, $id, $time) = $item->@{qw(backup-type backup-id backup-time)}; | |
110 | next if $type ne 'host'; | |
111 | ||
112 | my @pxar = grep { $_->{filename} eq 'pmgbackup.pxar.didx' } @{$item->{files}}; | |
113 | next if (scalar(@pxar) != 1); | |
114 | ||
115 | my $time_rfc3339 = strftime("%FT%TZ", gmtime($time)); | |
116 | ||
117 | push @$res, { | |
118 | 'backup-id' => $id, | |
119 | 'backup-time' => $time_rfc3339, | |
120 | ctime => $time, | |
121 | size => $item->{size} // 1, | |
122 | }; | |
123 | } | |
124 | return $res; | |
125 | } | |
126 | ||
a0208dbd SI |
127 | __PACKAGE__->register_method ({ |
128 | name => 'get_snapshots', | |
0e368d37 | 129 | path => '{remote}/snapshot', |
a0208dbd SI |
130 | method => 'GET', |
131 | description => "Get snapshots stored on remote.", | |
132 | proxyto => 'node', | |
133 | protected => 1, | |
134 | permissions => { check => [ 'admin', 'audit' ] }, | |
135 | parameters => { | |
136 | additionalProperties => 0, | |
137 | properties => { | |
138 | node => get_standard_option('pve-node'), | |
139 | remote => { | |
140 | description => "Proxmox Backup Server ID.", | |
141 | type => 'string', format => 'pve-configid', | |
142 | }, | |
143 | }, | |
144 | }, | |
145 | returns => { | |
146 | type => 'array', | |
147 | items => { | |
148 | type => "object", | |
149 | properties => { | |
0e368d37 TL |
150 | 'backup-time' => { type => 'string'}, |
151 | 'backup-id' => { type => 'string'}, | |
a0208dbd SI |
152 | ctime => { type => 'string'}, |
153 | size => { type => 'integer'}, | |
154 | }, | |
155 | }, | |
0e368d37 | 156 | links => [ { rel => 'child', href => "{backup-id}" } ], |
a0208dbd SI |
157 | }, |
158 | code => sub { | |
159 | my ($param) = @_; | |
160 | ||
0e368d37 TL |
161 | return get_snapshots($param->{remote}); |
162 | }}); | |
a0208dbd | 163 | |
0e368d37 TL |
164 | __PACKAGE__->register_method ({ |
165 | name => 'get_group_snapshots', | |
166 | path => '{remote}/snapshot/{backup-id}', | |
167 | method => 'GET', | |
168 | description => "Get snapshots from a specific ID stored on remote.", | |
169 | proxyto => 'node', | |
170 | protected => 1, | |
171 | permissions => { check => [ 'admin', 'audit' ] }, | |
172 | parameters => { | |
173 | additionalProperties => 0, | |
174 | properties => { | |
175 | node => get_standard_option('pve-node'), | |
176 | remote => { | |
177 | description => "Proxmox Backup Server ID.", | |
178 | type => 'string', format => 'pve-configid', | |
179 | }, | |
180 | 'backup-id' => { | |
181 | description => "ID (hostname) of backup snapshot", | |
182 | type => 'string', | |
183 | }, | |
184 | }, | |
185 | }, | |
186 | returns => { | |
187 | type => 'array', | |
188 | items => { | |
189 | type => "object", | |
190 | properties => { | |
191 | 'backup-time' => { type => 'string'}, | |
192 | 'backup-id' => { type => 'string'}, | |
193 | ctime => { type => 'string'}, | |
194 | size => { type => 'integer'}, | |
195 | }, | |
196 | }, | |
197 | links => [ { rel => 'child', href => "{backup-time}" } ], | |
198 | }, | |
199 | code => sub { | |
200 | my ($param) = @_; | |
a0208dbd | 201 | |
0e368d37 | 202 | return get_snapshots($param->{remote}, "host/$param->{node}"); |
a0208dbd SI |
203 | }}); |
204 | ||
205 | __PACKAGE__->register_method ({ | |
206 | name => 'forget_snapshot', | |
0e368d37 | 207 | path => '{remote}/snapshot/{backup-id}/{backup-time}', |
a0208dbd SI |
208 | method => 'DELETE', |
209 | description => "Forget a snapshot", | |
210 | proxyto => 'node', | |
211 | protected => 1, | |
212 | permissions => { check => [ 'admin', 'audit' ] }, | |
213 | parameters => { | |
214 | additionalProperties => 0, | |
215 | properties => { | |
216 | node => get_standard_option('pve-node'), | |
217 | remote => { | |
218 | description => "Proxmox Backup Server ID.", | |
219 | type => 'string', format => 'pve-configid', | |
220 | }, | |
0e368d37 TL |
221 | 'backup-id' => { |
222 | description => "ID (hostname) of backup snapshot", | |
223 | type => 'string', | |
224 | }, | |
225 | 'backup-time' => { | |
a6fd44eb | 226 | description => "Backup time in RFC 3339 format", |
a0208dbd SI |
227 | type => 'string', |
228 | }, | |
229 | }, | |
230 | }, | |
231 | returns => {type => 'null' }, | |
232 | code => sub { | |
233 | my ($param) = @_; | |
234 | ||
235 | my $remote = $param->{remote}; | |
0e368d37 TL |
236 | my $id = $param->{'backup-id'}; |
237 | my $time = $param->{'backup-time'}; | |
a0208dbd SI |
238 | |
239 | my $conf = PMG::PBSConfig->new(); | |
a0208dbd SI |
240 | my $remote_config = $conf->{ids}->{$remote}; |
241 | die "PBS remote '$remote' does not exist\n" if !$remote_config; | |
242 | die "PBS remote '$remote' is disabled\n" if $remote_config->{disable}; | |
243 | ||
244 | my $pbs = PVE::PBSClient->new($remote_config, $remote, $conf->{secret_dir}); | |
245 | ||
0e368d37 | 246 | eval { $pbs->forget_snapshot("host/$id/$time") }; |
a0208dbd SI |
247 | die "Forgetting backup failed: $@" if $@; |
248 | ||
249 | return; | |
250 | ||
251 | }}); | |
252 | ||
253 | __PACKAGE__->register_method ({ | |
254 | name => 'run_backup', | |
0e368d37 | 255 | path => '{remote}/snapshot', |
a0208dbd | 256 | method => 'POST', |
0e368d37 | 257 | description => "Create a new backup and prune the backup group afterwards, if configured.", |
a0208dbd SI |
258 | proxyto => 'node', |
259 | protected => 1, | |
260 | permissions => { check => [ 'admin', 'audit' ] }, | |
261 | parameters => { | |
262 | additionalProperties => 0, | |
263 | properties => { | |
264 | node => get_standard_option('pve-node'), | |
265 | remote => { | |
266 | description => "Proxmox Backup Server ID.", | |
267 | type => 'string', format => 'pve-configid', | |
268 | }, | |
269 | }, | |
270 | }, | |
271 | returns => { type => "string" }, | |
272 | code => sub { | |
273 | my ($param) = @_; | |
274 | ||
275 | my $rpcenv = PMG::RESTEnvironment->get(); | |
276 | my $authuser = $rpcenv->get_user(); | |
277 | ||
278 | my $remote = $param->{remote}; | |
279 | my $node = $param->{node}; | |
280 | ||
281 | my $conf = PMG::PBSConfig->new(); | |
282 | ||
283 | my $remote_config = $conf->{ids}->{$remote}; | |
284 | die "PBS remote '$remote' does not exist\n" if !$remote_config; | |
285 | die "PBS remote '$remote' is disabled\n" if $remote_config->{disable}; | |
286 | ||
287 | my $pbs = PVE::PBSClient->new($remote_config, $remote, $conf->{secret_dir}); | |
288 | my $backup_dir = "/var/lib/pmg/backup/current"; | |
289 | ||
290 | my $worker = sub { | |
291 | my $upid = shift; | |
292 | ||
293 | print "starting update of current backup state\n"; | |
294 | ||
295 | -d $backup_dir || mkdir $backup_dir; | |
296 | PMG::Backup::pmg_backup($backup_dir, $param->{statistic}); | |
a0208dbd | 297 | |
0339f810 | 298 | $pbs->backup_fs_tree($backup_dir, $node, 'pmgbackup'); |
a0208dbd SI |
299 | |
300 | print "backup finished\n"; | |
301 | ||
302 | my $group = "host/$node"; | |
d423685c TL |
303 | if (defined(my $prune_opts = $conf->prune_options($remote))) { |
304 | print "starting prune of $group\n"; | |
305 | my $res = $pbs->prune_group(undef, $prune_opts, $group); | |
306 | ||
307 | foreach my $pruned (@$res){ | |
308 | my $time = strftime("%FT%TZ", gmtime($pruned->{'backup-time'})); | |
309 | my $snap = $pruned->{'backup-type'} . '/' . $pruned->{'backup-id'} . '/' . $time; | |
310 | print "pruned snapshot: $snap\n"; | |
311 | } | |
312 | print "prune finished\n"; | |
a0208dbd SI |
313 | } |
314 | ||
a0208dbd SI |
315 | return; |
316 | }; | |
317 | ||
318 | return $rpcenv->fork_worker('pbs_backup', undef, $authuser, $worker); | |
319 | ||
320 | }}); | |
321 | ||
322 | __PACKAGE__->register_method ({ | |
323 | name => 'restore', | |
0e368d37 | 324 | path => '{remote}/snapshot/{backup-id}/{backup-time}', |
a0208dbd SI |
325 | method => 'POST', |
326 | description => "Restore the system configuration.", | |
327 | permissions => { check => [ 'admin' ] }, | |
328 | proxyto => 'node', | |
329 | protected => 1, | |
330 | parameters => { | |
331 | additionalProperties => 0, | |
332 | properties => { | |
333 | PMG::Backup::get_restore_options(), | |
334 | remote => { | |
335 | description => "Proxmox Backup Server ID.", | |
336 | type => 'string', format => 'pve-configid', | |
337 | }, | |
0e368d37 TL |
338 | 'backup-time' => { |
339 | description=> "backup-time to restore", | |
340 | type => 'string' | |
a0208dbd | 341 | }, |
0e368d37 TL |
342 | 'backup-id' => { |
343 | description => "backup-id (hostname) of backup snapshot", | |
344 | type => 'string' | |
a0208dbd SI |
345 | }, |
346 | }, | |
347 | }, | |
348 | returns => { type => "string" }, | |
349 | code => sub { | |
350 | my ($param) = @_; | |
351 | ||
352 | my $rpcenv = PMG::RESTEnvironment->get(); | |
353 | my $authuser = $rpcenv->get_user(); | |
354 | ||
355 | my $remote = $param->{remote}; | |
0e368d37 TL |
356 | my $id = $param->{'backup-id'}; |
357 | my $time = $param->{'backup-time'}; | |
358 | my $snapshot = "host/$id/$time"; | |
a0208dbd SI |
359 | |
360 | my $conf = PMG::PBSConfig->new(); | |
361 | ||
362 | my $remote_config = $conf->{ids}->{$remote}; | |
363 | die "PBS remote '$remote' does not exist\n" if !$remote_config; | |
364 | die "PBS remote '$remote' is disabled\n" if $remote_config->{disable}; | |
365 | ||
366 | my $pbs = PVE::PBSClient->new($remote_config, $remote, $conf->{secret_dir}); | |
367 | ||
ffd8e031 TL |
368 | my $now = time; |
369 | my $dirname = "/tmp/proxrestore_$$.$now"; | |
a0208dbd SI |
370 | |
371 | $param->{database} //= 1; | |
372 | ||
373 | die "nothing selected - please select what you want to restore (config or database?)\n" | |
374 | if !($param->{database} || $param->{config}); | |
375 | ||
a0208dbd | 376 | my $worker = sub { |
a0208dbd SI |
377 | print "starting restore of $snapshot from $remote\n"; |
378 | ||
dc4b50da | 379 | $pbs->restore_pxar($snapshot, 'pmgbackup', $dirname); |
a0208dbd | 380 | print "starting restore of PMG config\n"; |
0e368d37 TL |
381 | PMG::Backup::pmg_restore( |
382 | $dirname, | |
383 | $param->{database}, | |
384 | $param->{config}, | |
385 | $param->{statistic} | |
386 | ); | |
a0208dbd | 387 | print "restore finished\n"; |
a0208dbd SI |
388 | }; |
389 | ||
390 | return $rpcenv->fork_worker('pbs_restore', undef, $authuser, $worker); | |
391 | }}); | |
392 | ||
7251cc51 SI |
393 | __PACKAGE__->register_method ({ |
394 | name => 'create_timer', | |
395 | path => '{remote}/timer', | |
396 | method => 'POST', | |
397 | description => "Create backup schedule", | |
398 | proxyto => 'node', | |
399 | protected => 1, | |
400 | permissions => { check => [ 'admin', 'audit' ] }, | |
401 | parameters => { | |
402 | additionalProperties => 0, | |
403 | properties => { | |
404 | node => get_standard_option('pve-node'), | |
405 | remote => { | |
406 | description => "Proxmox Backup Server ID.", | |
407 | type => 'string', format => 'pve-configid', | |
408 | }, | |
409 | schedule => { | |
410 | description => "Schedule for the backup (OnCalendar setting of the systemd.timer)", | |
411 | type => 'string', pattern => '[0-9a-zA-Z*.:,\-/ ]+', | |
412 | default => 'daily', optional => 1, | |
413 | }, | |
414 | delay => { | |
415 | description => "Randomized delay to add to the starttime (RandomizedDelaySec setting of the systemd.timer)", | |
416 | type => 'string', pattern => '[0-9a-zA-Z. ]+', | |
a6fd44eb | 417 | default => '5min', optional => 1, |
7251cc51 SI |
418 | }, |
419 | }, | |
420 | }, | |
421 | returns => { type => 'null' }, | |
422 | code => sub { | |
423 | my ($param) = @_; | |
424 | ||
425 | my $remote = $param->{remote}; | |
426 | my $schedule = $param->{schedule} // 'daily'; | |
427 | my $delay = $param->{delay} // '5min'; | |
428 | ||
429 | my $conf = PMG::PBSConfig->new(); | |
430 | ||
431 | my $remote_config = $conf->{ids}->{$remote}; | |
432 | die "PBS remote '$remote' does not exist\n" if !$remote_config; | |
433 | die "PBS remote '$remote' is disabled\n" if $remote_config->{disable}; | |
434 | ||
435 | PMG::PBSSchedule::create_schedule($remote, $schedule, $delay); | |
436 | ||
437 | }}); | |
438 | ||
439 | __PACKAGE__->register_method ({ | |
440 | name => 'delete_timer', | |
441 | path => '{remote}/timer', | |
442 | method => 'DELETE', | |
443 | description => "Delete backup schedule", | |
444 | proxyto => 'node', | |
445 | protected => 1, | |
446 | permissions => { check => [ 'admin', 'audit' ] }, | |
447 | parameters => { | |
448 | additionalProperties => 0, | |
449 | properties => { | |
450 | node => get_standard_option('pve-node'), | |
451 | remote => { | |
452 | description => "Proxmox Backup Server ID.", | |
453 | type => 'string', format => 'pve-configid', | |
454 | }, | |
455 | }, | |
456 | }, | |
457 | returns => { type => 'null' }, | |
458 | code => sub { | |
459 | my ($param) = @_; | |
460 | ||
461 | my $remote = $param->{remote}; | |
462 | ||
463 | PMG::PBSSchedule::delete_schedule($remote); | |
464 | ||
465 | }}); | |
466 | ||
467 | __PACKAGE__->register_method ({ | |
468 | name => 'list_timer', | |
469 | path => '{remote}/timer', | |
470 | method => 'GET', | |
471 | description => "Get timer specification", | |
472 | proxyto => 'node', | |
473 | protected => 1, | |
474 | permissions => { check => [ 'admin', 'audit' ] }, | |
475 | parameters => { | |
476 | additionalProperties => 0, | |
477 | properties => { | |
478 | node => get_standard_option('pve-node'), | |
479 | remote => { | |
480 | description => "Proxmox Backup Server ID.", | |
481 | type => 'string', format => 'pve-configid', | |
482 | }, | |
483 | }, | |
484 | }, | |
edce8f57 TL |
485 | returns => { |
486 | type => 'object', | |
487 | properties => { | |
7251cc51 | 488 | remote => { |
edce8f57 | 489 | description => "Proxmox Backup Server remote ID.", |
7251cc51 SI |
490 | type => 'string', format => 'pve-configid', |
491 | optional => 1, | |
492 | }, | |
493 | schedule => { | |
494 | description => "Schedule for the backup (OnCalendar setting of the systemd.timer)", | |
495 | type => 'string', pattern => '[0-9a-zA-Z*.:,\-/ ]+', | |
496 | default => 'daily', optional => 1, | |
497 | }, | |
498 | delay => { | |
499 | description => "Randomized delay to add to the starttime (RandomizedDelaySec setting of the systemd.timer)", | |
500 | type => 'string', pattern => '[0-9a-zA-Z. ]+', | |
a6fd44eb | 501 | default => '5min', optional => 1, |
7251cc51 | 502 | }, |
8be5c4b2 TL |
503 | 'next-run' => { |
504 | description => "The date time of the next run, in server locale.", | |
505 | type => 'string', | |
506 | optional => 1, | |
507 | }, | |
7251cc51 SI |
508 | unitfile => { |
509 | description => "unit file for the systemd.timer unit", | |
510 | type => 'string', optional => 1, | |
511 | }, | |
edce8f57 TL |
512 | } |
513 | }, | |
7251cc51 SI |
514 | code => sub { |
515 | my ($param) = @_; | |
516 | ||
517 | my $remote = $param->{remote}; | |
518 | ||
4fa076e5 | 519 | my $schedules = PMG::PBSSchedule::get_schedules($remote); |
7251cc51 SI |
520 | |
521 | my $res = {}; | |
4fa076e5 TL |
522 | if (scalar(@$schedules) >= 1) { |
523 | $res = $schedules->[0]; | |
7251cc51 SI |
524 | } |
525 | ||
526 | return $res | |
527 | }}); | |
528 | ||
a0208dbd | 529 | 1; |