[pmg-devel] [PATCH pmg-api v2 6/7] add scheduled backup to PBS remotes
Stoiko Ivanov
s.ivanov at proxmox.com
Mon Nov 2 19:45:34 CET 2020
PMG::PBSSchedule contains methods for creating/deleting systemd-timer units,
which will run a backup to a configured PBS remote.
Signed-off-by: Stoiko Ivanov <s.ivanov at proxmox.com>
---
debian/pmg-pbsbackup at .service | 6 ++
debian/rules | 1 +
src/Makefile | 3 +-
src/PMG/API2/PBS/Job.pm | 130 ++++++++++++++++++++++++++++++++++
src/PMG/CLI/pmgbackup.pm | 6 ++
src/PMG/PBSSchedule.pm | 104 +++++++++++++++++++++++++++
6 files changed, 249 insertions(+), 1 deletion(-)
create mode 100644 debian/pmg-pbsbackup at .service
create mode 100644 src/PMG/PBSSchedule.pm
diff --git a/debian/pmg-pbsbackup at .service b/debian/pmg-pbsbackup at .service
new file mode 100644
index 0000000..37aa23b
--- /dev/null
+++ b/debian/pmg-pbsbackup at .service
@@ -0,0 +1,6 @@
+[Unit]
+Description=Backup to PBS remote %I
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/pmgbackup pbsjob run %I
diff --git a/debian/rules b/debian/rules
index bab4d98..5a2cf7a 100755
--- a/debian/rules
+++ b/debian/rules
@@ -20,6 +20,7 @@ override_dh_installinit:
dh_systemd_enable --name=pmgspamreport pmgspamreport.service
dh_systemd_enable --name=pmgreport pmgreport.service
dh_systemd_enable --name=pmgsync pmgsync.service
+ dh_systemd_enable --no-enable --name=pmg-pbsbackup@ pmg-pbsbackup at .service
override_dh_systemd_start:
dh_systemd_start pmg-hourly.timer pmg-daily.timer pmgspamreport.timer pmgreport.timer
diff --git a/src/Makefile b/src/Makefile
index fb42f21..9d5c335 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -15,7 +15,7 @@ CRONSCRIPTS = pmg-hourly pmg-daily
CLI_CLASSES = $(addprefix PMG/CLI/, $(addsuffix .pm, ${CLITOOLS}))
SERVICE_CLASSES = $(addprefix PMG/Service/, $(addsuffix .pm, ${SERVICES}))
-SERVICE_UNITS = $(addprefix debian/, $(addsuffix .service, ${SERVICES}))
+SERVICE_UNITS = $(addprefix debian/, $(addsuffix .service, ${SERVICES} pmg-pbsbackup@))
TIMER_UNITS = $(addprefix debian/, $(addsuffix .timer, ${CRONSCRIPTS} pmgspamreport pmgreport))
CLI_BINARIES = $(addprefix bin/, ${CLITOOLS} ${CLISCRIPTS} ${CRONSCRIPTS})
@@ -67,6 +67,7 @@ LIBSOURCES = \
PMG/Unpack.pm \
PMG/Backup.pm \
PMG/PBSConfig.pm \
+ PMG/PBSSchedule.pm \
PMG/RuleCache.pm \
PMG/Statistic.pm \
PMG/UserConfig.pm \
diff --git a/src/PMG/API2/PBS/Job.pm b/src/PMG/API2/PBS/Job.pm
index dee1754..4b686ec 100644
--- a/src/PMG/API2/PBS/Job.pm
+++ b/src/PMG/API2/PBS/Job.pm
@@ -14,6 +14,7 @@ use PVE::PBSClient;
use PMG::RESTEnvironment;
use PMG::Backup;
use PMG::PBSConfig;
+use PMG::PBSSchedule;
use base qw(PVE::RESTHandler);
@@ -368,4 +369,133 @@ __PACKAGE__->register_method ({
return $rpcenv->fork_worker('pbs_restore', undef, $authuser, $worker);
}});
+__PACKAGE__->register_method ({
+ name => 'create_timer',
+ path => '{remote}/timer',
+ method => 'POST',
+ description => "Create backup schedule",
+ proxyto => 'node',
+ protected => 1,
+ permissions => { check => [ 'admin', 'audit' ] },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ remote => {
+ description => "Proxmox Backup Server ID.",
+ type => 'string', format => 'pve-configid',
+ },
+ schedule => {
+ description => "Schedule for the backup (OnCalendar setting of the systemd.timer)",
+ type => 'string', pattern => '[0-9a-zA-Z*.:,\-/ ]+',
+ default => 'daily', optional => 1,
+ },
+ delay => {
+ description => "Randomized delay to add to the starttime (RandomizedDelaySec setting of the systemd.timer)",
+ type => 'string', pattern => '[0-9a-zA-Z. ]+',
+ default => 'daily', optional => 1,
+ },
+ },
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my $remote = $param->{remote};
+ my $schedule = $param->{schedule} // 'daily';
+ my $delay = $param->{delay} // '5min';
+
+ my $conf = PMG::PBSConfig->new();
+
+ my $remote_config = $conf->{ids}->{$remote};
+ die "PBS remote '$remote' does not exist\n" if !$remote_config;
+ die "PBS remote '$remote' is disabled\n" if $remote_config->{disable};
+
+ PMG::PBSSchedule::create_schedule($remote, $schedule, $delay);
+
+ }});
+
+__PACKAGE__->register_method ({
+ name => 'delete_timer',
+ path => '{remote}/timer',
+ method => 'DELETE',
+ description => "Delete backup schedule",
+ proxyto => 'node',
+ protected => 1,
+ permissions => { check => [ 'admin', 'audit' ] },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ remote => {
+ description => "Proxmox Backup Server ID.",
+ type => 'string', format => 'pve-configid',
+ },
+ },
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my $remote = $param->{remote};
+
+ PMG::PBSSchedule::delete_schedule($remote);
+
+ }});
+
+__PACKAGE__->register_method ({
+ name => 'list_timer',
+ path => '{remote}/timer',
+ method => 'GET',
+ description => "Get timer specification",
+ proxyto => 'node',
+ protected => 1,
+ permissions => { check => [ 'admin', 'audit' ] },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ remote => {
+ description => "Proxmox Backup Server ID.",
+ type => 'string', format => 'pve-configid',
+ },
+ },
+ },
+ returns => { type => 'object', properties => {
+ remote => {
+ description => "Proxmox Backup Server ID.",
+ type => 'string', format => 'pve-configid',
+ optional => 1,
+ },
+ schedule => {
+ description => "Schedule for the backup (OnCalendar setting of the systemd.timer)",
+ type => 'string', pattern => '[0-9a-zA-Z*.:,\-/ ]+',
+ default => 'daily', optional => 1,
+ },
+ delay => {
+ description => "Randomized delay to add to the starttime (RandomizedDelaySec setting of the systemd.timer)",
+ type => 'string', pattern => '[0-9a-zA-Z. ]+',
+ default => 'daily', optional => 1,
+ },
+ unitfile => {
+ description => "unit file for the systemd.timer unit",
+ type => 'string', optional => 1,
+ },
+ }},
+ code => sub {
+ my ($param) = @_;
+
+ my $remote = $param->{remote};
+
+ my $schedules = PMG::PBSSchedule::get_schedules();
+ my @data = grep {$_->{remote} eq $remote} @$schedules;
+
+ my $res = {};
+ if (scalar(@data) == 1) {
+ $res = $data[0];
+ }
+
+ return $res
+ }});
+
1;
diff --git a/src/PMG/CLI/pmgbackup.pm b/src/PMG/CLI/pmgbackup.pm
index c422927..950f6e4 100644
--- a/src/PMG/CLI/pmgbackup.pm
+++ b/src/PMG/CLI/pmgbackup.pm
@@ -54,6 +54,12 @@ our $cmddef = {
forget => ['PMG::API2::PBS::Job', 'forget_snapshot', ['remote', 'time'], { node => $nodename} ],
run => ['PMG::API2::PBS::Job', 'run_backup', ['remote'], { node => $nodename} ],
restore => ['PMG::API2::PBS::Job', 'restore', ['remote'], { node => $nodename} ],
+ create => ['PMG::API2::PBS::Job', 'create_timer', ['remote'], { node => $nodename }],
+ delete => ['PMG::API2::PBS::Job', 'delete_timer', ['remote'], { node => $nodename }],
+ schedule => ['PMG::API2::PBS::Job', 'list_timer', ['remote'], { node => $nodename }, sub {
+ my ($data, $schema, $options) = @_;
+ PVE::CLIFormatter::print_api_result($data, $schema, ['remote', 'schedule', 'delay'], $options);
+ }, $PVE::RESTHandler::standard_output_options ],
},
};
diff --git a/src/PMG/PBSSchedule.pm b/src/PMG/PBSSchedule.pm
new file mode 100644
index 0000000..6663f55
--- /dev/null
+++ b/src/PMG/PBSSchedule.pm
@@ -0,0 +1,104 @@
+package PMG::PBSSchedule;
+
+use strict;
+use warnings;
+
+use PVE::Tools qw(run_command file_set_contents file_get_contents trim dir_glob_foreach);
+use PVE::Systemd;
+
+# systemd timer
+sub get_schedules {
+ my ($param) = @_;
+
+ my $result = [];
+
+ my $systemd_dir = '/etc/systemd/system';
+
+ dir_glob_foreach($systemd_dir, '^pmg-pbsbackup at .+\.timer$', sub {
+ my ($filename) = @_;
+ my $remote;
+ if ($filename =~ /^pmg-pbsbackup\@(.+)\.timer$/) {
+ $remote = PVE::Systemd::unescape_unit($1);
+ } else {
+ die 'Unrecognized timer name!\n';
+ }
+
+ my $unitfile = "$systemd_dir/$filename";
+ my $unit = PVE::Systemd::read_ini($unitfile);
+
+ push @$result, {
+ unitfile => $unitfile,
+ remote => $remote,
+ schedule => $unit->{'Timer'}->{'OnCalendar'},
+ delay => $unit->{'Timer'}->{'RandomizedDelaySec'},
+ };
+ });
+
+ return $result;
+
+}
+
+sub create_schedule {
+ my ($remote, $schedule, $delay) = @_;
+
+ my $unit_name = 'pmg-pbsbackup@' . PVE::Systemd::escape_unit($remote);
+ #my $service_unit = $unit_name . '.service';
+ my $timer_unit = $unit_name . '.timer';
+ my $timer_unit_path = "/etc/systemd/system/$timer_unit";
+
+ # create systemd timer
+ run_command(['systemd-analyze', 'calendar', $schedule], errmsg => "Invalid schedule specification", outfunc => sub {});
+ run_command(['systemd-analyze', 'timespan', $delay], errmsg => "Invalid delay specification", outfunc => sub {});
+ my $timer = {
+ 'Unit' => {
+ 'Description' => "Timer for PBS Backup to remote $remote",
+ },
+ 'Timer' => {
+ 'OnCalendar' => $schedule,
+ 'RandomizedDelaySec' => $delay,
+ },
+ 'Install' => {
+ 'WantedBy' => 'timers.target',
+ },
+ };
+
+ eval {
+ PVE::Systemd::write_ini($timer, $timer_unit_path);
+ run_command(['systemctl', 'daemon-reload']);
+ run_command(['systemctl', 'enable', $timer_unit]);
+ run_command(['systemctl', 'start', $timer_unit]);
+
+ };
+ if (my $err = $@) {
+ die "Creating backup schedule for $remote failed: $err\n";
+ }
+
+ return;
+}
+
+sub delete_schedule {
+ my ($remote) = @_;
+
+ my $schedules = get_schedules();
+
+ die "Schedule for $remote not found!\n" if !grep {$_->{remote} eq $remote} @$schedules;
+
+ my $unit_name = 'pmg-pbsbackup@' . PVE::Systemd::escape_unit($remote);
+ my $service_unit = $unit_name . '.service';
+ my $timer_unit = $unit_name . '.timer';
+ my $timer_unit_path = "/etc/systemd/system/$timer_unit";
+
+ eval {
+ run_command(['systemctl', 'disable', $timer_unit]);
+ unlink($timer_unit_path) || die "delete '$timer_unit_path' failed - $!\n";
+ run_command(['systemctl', 'daemon-reload']);
+
+ };
+ if (my $err = $@) {
+ die "Removing backup schedule for $remote failed: $err\n";
+ }
+
+ return;
+}
+
+1;
--
2.20.1
More information about the pmg-devel
mailing list