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