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