diff options
Diffstat (limited to 'roles/storage/zfs/syncoid')
-rw-r--r-- | roles/storage/zfs/syncoid/defaults/main.yml | 2 | ||||
-rw-r--r-- | roles/storage/zfs/syncoid/tasks/main.yml | 34 | ||||
-rw-r--r-- | roles/storage/zfs/syncoid/templates/autosuspend.py.j2 | 92 | ||||
-rw-r--r-- | roles/storage/zfs/syncoid/templates/autosuspend.service.j2 | 18 | ||||
-rw-r--r-- | roles/storage/zfs/syncoid/templates/autosuspend.timer.j2 | 8 | ||||
-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 |
7 files changed, 150 insertions, 5 deletions
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 |