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