public inbox for pmg-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Stoiko Ivanov <s.ivanov@proxmox.com>
To: pmg-devel@lists.proxmox.com
Subject: [pmg-devel] [RFC pmg-api 08/12] PBSTools: add systemd-timer helpers
Date: Mon, 19 Oct 2020 21:02:05 +0200	[thread overview]
Message-ID: <20201019190209.11495-9-s.ivanov@proxmox.com> (raw)
In-Reply-To: <20201019190209.11495-1-s.ivanov@proxmox.com>

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@proxmox.com>
---
 debian/pmg-pbsbackup@.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@.service

diff --git a/debian/pmg-pbsbackup@.service b/debian/pmg-pbsbackup@.service
new file mode 100644
index 0000000..37aa23b
--- /dev/null
+++ b/debian/pmg-pbsbackup@.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@.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@.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@.+\.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





  parent reply	other threads:[~2020-10-19 19:02 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-19 19:01 [pmg-devel] [RFC pmg-api 00/12] POC PBS integration Stoiko Ivanov
2020-10-19 19:01 ` [pmg-devel] [RFC pmg-api 01/12] drop left-over commented out code Stoiko Ivanov
2020-10-19 19:01 ` [pmg-devel] [RFC pmg-api 02/12] Backup: split backup creation and creating tar Stoiko Ivanov
2020-10-20  5:43   ` Dietmar Maurer
2020-10-19 19:02 ` [pmg-devel] [RFC pmg-api 03/12] Restore: optionally restore from directory Stoiko Ivanov
2020-10-19 19:02 ` [pmg-devel] [RFC pmg-api 04/12] Backup: push restore options to PMG::Backup Stoiko Ivanov
2020-10-19 19:02 ` [pmg-devel] [RFC pmg-api 05/12] debian: add dependency on proxmox-backup-client Stoiko Ivanov
2020-10-19 19:02 ` [pmg-devel] [RFC pmg-api 06/12] add helper module for handling PBS Integration Stoiko Ivanov
2020-10-19 19:02 ` [pmg-devel] [RFC pmg-api 07/12] PBSTools: add methods for managing backups Stoiko Ivanov
2020-10-19 19:02 ` Stoiko Ivanov [this message]
2020-10-19 19:02 ` [pmg-devel] [RFC pmg-api 09/12] add initial SectionConfig for pbs Stoiko Ivanov
2020-10-19 19:02 ` [pmg-devel] [RFC pmg-api 10/12] Add API2 module for PBS configuration Stoiko Ivanov
2020-10-19 19:02 ` [pmg-devel] [RFC pmg-api 11/12] Add API2 module for per-node backups to PBS Stoiko Ivanov
2020-10-19 19:02 ` [pmg-devel] [RFC pmg-api 12/12] pbs-integration: add CLI calls to pmgbackup Stoiko Ivanov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201019190209.11495-9-s.ivanov@proxmox.com \
    --to=s.ivanov@proxmox.com \
    --cc=pmg-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal