[pmg-devel] [RFC pmg-api 08/12] PBSTools: add systemd-timer helpers
Stoiko Ivanov
s.ivanov at proxmox.com
Mon Oct 19 21:02:05 CEST 2020
add helper methods to create systemd-timer units, which run backups to a
PBS-Remote regularly.
Unit-file handling taken from pve-storage/PVE/API2/Disks/Directory.pm
Signed-off-by: Stoiko Ivanov <s.ivanov at proxmox.com>
---
debian/pmg-pbsbackup at .service | 6 ++
debian/rules | 1 +
src/Makefile | 2 +-
src/PMG/PBSTools.pm | 165 ++++++++++++++++++++++++++++++++++
4 files changed, 173 insertions(+), 1 deletion(-)
create mode 100644 debian/pmg-pbsbackup at .service
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 7f9726b..a460048 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})) debian/pmg-pbsbackup at .service
TIMER_UNITS = $(addprefix debian/, $(addsuffix .timer, ${CRONSCRIPTS} pmgspamreport pmgreport))
CLI_BINARIES = $(addprefix bin/, ${CLITOOLS} ${CLISCRIPTS} ${CRONSCRIPTS})
diff --git a/src/PMG/PBSTools.pm b/src/PMG/PBSTools.pm
index 9efb2ce..fcef74a 100644
--- a/src/PMG/PBSTools.pm
+++ b/src/PMG/PBSTools.pm
@@ -298,4 +298,169 @@ sub status {
return ($total, $free, $used, $active);
}
+# systemd timer
+my $read_ini = sub {
+ my ($filename) = @_;
+
+ my $content = file_get_contents($filename);
+ my @lines = split /\n/, $content;
+
+ my $result = {};
+ my $section;
+
+ foreach my $line (@lines) {
+ $line = trim($line);
+ if ($line =~ m/^\[([^\]]+)\]/) {
+ $section = $1;
+ if (!defined($result->{$section})) {
+ $result->{$section} = {};
+ }
+ } elsif ($line =~ m/^(.*?)=(.*)$/) {
+ my ($key, $val) = ($1, $2);
+ if (!$section) {
+ warn "key value pair found without section, skipping\n";
+ next;
+ }
+
+ if ($result->{$section}->{$key}) {
+ # make duplicate properties to arrays to keep the order
+ my $prop = $result->{$section}->{$key};
+ if (ref($prop) eq 'ARRAY') {
+ push @$prop, $val;
+ } else {
+ $result->{$section}->{$key} = [$prop, $val];
+ }
+ } else {
+ $result->{$section}->{$key} = $val;
+ }
+ }
+ # ignore everything else
+ }
+
+ return $result;
+};
+
+my $write_ini = sub {
+ my ($ini, $filename) = @_;
+
+ my $content = "";
+
+ foreach my $sname (sort keys %$ini) {
+ my $section = $ini->{$sname};
+
+ $content .= "[$sname]\n";
+
+ foreach my $pname (sort keys %$section) {
+ my $prop = $section->{$pname};
+
+ if (!ref($prop)) {
+ $content .= "$pname=$prop\n";
+ } elsif (ref($prop) eq 'ARRAY') {
+ foreach my $val (@$prop) {
+ $content .= "$pname=$val\n";
+ }
+ } else {
+ die "invalid property '$pname'\n";
+ }
+ }
+ $content .= "\n";
+ }
+
+ file_set_contents($filename, $content);
+};
+
+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 = $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 {
+ $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