summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--inventory/host_vars/ch-epimetheus.yml1
-rw-r--r--roles/storage/zfs/syncoid/defaults/main.yml2
-rw-r--r--roles/storage/zfs/syncoid/tasks/main.yml34
-rw-r--r--roles/storage/zfs/syncoid/templates/autosuspend.py.j292
-rw-r--r--roles/storage/zfs/syncoid/templates/autosuspend.service.j218
-rw-r--r--roles/storage/zfs/syncoid/templates/autosuspend.timer.j28
-rw-r--r--roles/storage/zfs/syncoid/templates/run.service.j2 (renamed from roles/storage/zfs/syncoid/templates/systemd.service.j2)0
-rw-r--r--roles/storage/zfs/syncoid/templates/run.timer.j2 (renamed from roles/storage/zfs/syncoid/templates/systemd.timer.j2)1
8 files changed, 151 insertions, 5 deletions
diff --git a/inventory/host_vars/ch-epimetheus.yml b/inventory/host_vars/ch-epimetheus.yml
index bc56c22e..4668f67c 100644
--- a/inventory/host_vars/ch-epimetheus.yml
+++ b/inventory/host_vars/ch-epimetheus.yml
@@ -71,6 +71,7 @@ zfs_sanoid_modules:
process_children_only: yes
+zfs_syncoid_autosuspend: yes
zfs_syncoid_target_pool: backup
zfs_syncoid_sources:
'ch-prometheus':
diff --git a/roles/storage/zfs/syncoid/defaults/main.yml b/roles/storage/zfs/syncoid/defaults/main.yml
index 6e874daf..1161423b 100644
--- a/roles/storage/zfs/syncoid/defaults/main.yml
+++ b/roles/storage/zfs/syncoid/defaults/main.yml
@@ -1,4 +1,6 @@
---
+zfs_syncoid_autosuspend: no
+
# zfs_syncoid_target_pool: target
# zfs_syncoid_sources:
diff --git a/roles/storage/zfs/syncoid/tasks/main.yml b/roles/storage/zfs/syncoid/tasks/main.yml
index a01900b1..7c489baa 100644
--- a/roles/storage/zfs/syncoid/tasks/main.yml
+++ b/roles/storage/zfs/syncoid/tasks/main.yml
@@ -43,8 +43,8 @@
loop_control:
label: "{{ item.key }}"
template:
- src: systemd.service.j2
- dest: "/etc/systemd/system/syncoid-{{ item.key }}.service"
+ src: run.service.j2
+ dest: "/etc/systemd/system/syncoid-run-{{ item.key }}.service"
- name: create systemd timer units for periodic backups
loop: "{{ zfs_syncoid_sources | dict2items }}"
@@ -52,8 +52,8 @@
label: "{{ item.key }}"
when: "'periodic' in item.value"
template:
- src: systemd.timer.j2
- dest: "/etc/systemd/system/syncoid-{{ item.key }}.timer"
+ src: run.timer.j2
+ dest: "/etc/systemd/system/syncoid-run-{{ item.key }}.timer"
- name: make sure systemd timer units for periodic backups are enabled and started
loop: "{{ zfs_syncoid_sources | dict2items }}"
@@ -62,6 +62,30 @@
when: "'periodic' in item.value"
systemd:
daemon_reload: yes
- name: "syncoid-{{ item.key }}.timer"
+ name: "syncoid-run-{{ item.key }}.timer"
state: started
enabled: yes
+
+- name: enable autosuspend
+ when: zfs_syncoid_autosuspend
+ block:
+ - name: install autosuspend script
+ template:
+ src: autosuspend.py.j2
+ dest: /usr/local/bin/syncoid-autosuspend.py
+ mode: 0755
+
+ - name: install autosuspend systemd units
+ loop:
+ - service
+ - timer
+ template:
+ src: "autosuspend.{{ item }}.j2"
+ dest: "/etc/systemd/system/syncoid-autosuspend.{{ item }}"
+
+ - name: make sure autosuspend timer unit is enabled and started
+ systemd:
+ daemon_reload: yes
+ name: syncoid-autosuspend.timer
+ enabled: yes
+ state: started
diff --git a/roles/storage/zfs/syncoid/templates/autosuspend.py.j2 b/roles/storage/zfs/syncoid/templates/autosuspend.py.j2
new file mode 100644
index 00000000..e54ddd1b
--- /dev/null
+++ b/roles/storage/zfs/syncoid/templates/autosuspend.py.j2
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+
+import sys
+import dbus
+import re
+import datetime
+#import pprint
+
+
+class AutoSuspender(object):
+
+ def __init__(self):
+ self.__bus = dbus.SystemBus()
+ self.__timer_re = re.compile('^syncoid-run-.*\.timer$')
+ #self.__pp = pprint.PrettyPrinter(indent=2)
+
+ def _get_interface(self, dest, object, interface):
+ try:
+ obj = self.__bus.get_object(dest, object)
+ return dbus.Interface(obj, interface)
+ except dbus.exceptions.DBusException as error:
+ print(error)
+ sys.exit(1)
+
+ def _get_logind_manager_interface(self):
+ return self._get_interface("org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager")
+
+ def _get_systemd_manager_interface(self):
+ return self._get_interface("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager")
+
+ def _get_systemd_timer_properties(self, path):
+ return self._get_interface("org.freedesktop.systemd1", path, "org.freedesktop.DBus.Properties").GetAll("org.freedesktop.systemd1.Timer")
+
+ def _get_users_logged_in(self):
+ manager = self._get_logind_manager_interface()
+ users = []
+ for user in manager.ListUsers():
+ uid = int(user[0])
+ name = str(user[1])
+ users.append((name, uid))
+ return users
+
+ def _get_syncoid_timers(self):
+ manager = self._get_systemd_manager_interface()
+ timers = []
+ for unit in manager.ListUnits():
+ name = str(unit[0])
+ path = str(unit[6])
+ if not self.__timer_re.match(name):
+ continue
+ timers.append((name, path))
+ return timers
+
+ def _get_timer_next_elapse(self, path):
+ props = self._get_systemd_timer_properties(path)
+ try:
+ return datetime.datetime.fromtimestamp(int(props['NextElapseUSecRealtime']) / 1000000)
+ except ValueError:
+ return datetime.datetime.now()
+
+ def check(self):
+ result = True
+
+ users = self._get_users_logged_in()
+ if(len(users) > 0):
+ print("%d users logged in: %s" % (len(users), ", ".join([user[0] for user in users])))
+ result = False
+
+ timers = self._get_syncoid_timers()
+ for timer in timers:
+ next = self._get_timer_next_elapse(timer[1])
+ until = next - datetime.datetime.now()
+ if(until < datetime.timedelta(minutes=10)):
+ print("Timer %s elapses in less then 10 Minutes -> %s (%s)" % (timer[0], next, until))
+ result = False
+
+ return result
+
+ def suspend(self):
+ manager = self._get_logind_manager_interface()
+ if not manager.CanSuspend():
+ print("suspending is not possible!")
+ return
+
+ print("suspending system")
+ manager.Suspend(False)
+
+
+if __name__ == "__main__":
+ s = AutoSuspender()
+ if s.check():
+ s.suspend()
diff --git a/roles/storage/zfs/syncoid/templates/autosuspend.service.j2 b/roles/storage/zfs/syncoid/templates/autosuspend.service.j2
new file mode 100644
index 00000000..f1e13b72
--- /dev/null
+++ b/roles/storage/zfs/syncoid/templates/autosuspend.service.j2
@@ -0,0 +1,18 @@
+[Unit]
+Description=automatic system suspender for syncoid-based backups
+
+[Service]
+Type=oneshot
+ExecStart=/usr/local/bin/syncoid-autosuspend.py
+NoNewPrivileges=yes
+PrivateTmp=yes
+PrivateDevices=yes
+ProtectSystem=strict
+ProtectHome=yes
+ProtectKernelTunables=yes
+ProtectControlGroups=yes
+RestrictRealtime=yes
+RestrictAddressFamilies=AF_UNIX
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/storage/zfs/syncoid/templates/autosuspend.timer.j2 b/roles/storage/zfs/syncoid/templates/autosuspend.timer.j2
new file mode 100644
index 00000000..5c0b3329
--- /dev/null
+++ b/roles/storage/zfs/syncoid/templates/autosuspend.timer.j2
@@ -0,0 +1,8 @@
+[Unit]
+Description=automatic system suspender for syncoid-based backups
+
+[Timer]
+OnCalendar=*-*-* *-03/10-00
+
+[Install]
+WantedBy=timers.target
diff --git a/roles/storage/zfs/syncoid/templates/systemd.service.j2 b/roles/storage/zfs/syncoid/templates/run.service.j2
index 0d214595..0d214595 100644
--- a/roles/storage/zfs/syncoid/templates/systemd.service.j2
+++ b/roles/storage/zfs/syncoid/templates/run.service.j2
diff --git a/roles/storage/zfs/syncoid/templates/systemd.timer.j2 b/roles/storage/zfs/syncoid/templates/run.timer.j2
index 260a8938..cc252854 100644
--- a/roles/storage/zfs/syncoid/templates/systemd.timer.j2
+++ b/roles/storage/zfs/syncoid/templates/run.timer.j2
@@ -3,6 +3,7 @@ Description=syncoid-based backup for {{ item.key }}
[Timer]
OnCalendar={{ item.value.periodic.schedule }}
+WakeSystem={{ zfs_syncoid_autosuspend | ternary('true', 'false') }}
[Install]
WantedBy=timers.target