From 26214e3f7c5343fa30d2ff1ae71a6cf7197b6f3e Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Wed, 19 May 2021 16:52:36 +0200 Subject: initial version of syncoid autosuspender --- .../zfs/syncoid/templates/autosuspend.py.j2 | 92 ++++++++++++++++++++++ .../zfs/syncoid/templates/autosuspend.service.j2 | 18 +++++ .../zfs/syncoid/templates/autosuspend.timer.j2 | 8 ++ roles/storage/zfs/syncoid/templates/run.service.j2 | 19 +++++ roles/storage/zfs/syncoid/templates/run.timer.j2 | 9 +++ .../zfs/syncoid/templates/systemd.service.j2 | 19 ----- .../storage/zfs/syncoid/templates/systemd.timer.j2 | 8 -- 7 files changed, 146 insertions(+), 27 deletions(-) create mode 100644 roles/storage/zfs/syncoid/templates/autosuspend.py.j2 create mode 100644 roles/storage/zfs/syncoid/templates/autosuspend.service.j2 create mode 100644 roles/storage/zfs/syncoid/templates/autosuspend.timer.j2 create mode 100644 roles/storage/zfs/syncoid/templates/run.service.j2 create mode 100644 roles/storage/zfs/syncoid/templates/run.timer.j2 delete mode 100644 roles/storage/zfs/syncoid/templates/systemd.service.j2 delete mode 100644 roles/storage/zfs/syncoid/templates/systemd.timer.j2 (limited to 'roles/storage/zfs/syncoid/templates') 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/run.service.j2 b/roles/storage/zfs/syncoid/templates/run.service.j2 new file mode 100644 index 00000000..0d214595 --- /dev/null +++ b/roles/storage/zfs/syncoid/templates/run.service.j2 @@ -0,0 +1,19 @@ +[Unit] +Description=syncoid-based backup for {{ item.key }} + +[Service] +Type=oneshot +{% for path,config in item.value.paths.items() %} +ExecStart=/var/lib/syncoid/syncoid_wrapper --dumpsnaps --quiet {{ config.recursive | default(false) | ternary('-r ', '') }}{{ config.skip_parent | default(false) | ternary('--skip-parent ', '') }}{% for re in config.exclude | default([]) %}--exclude='{{ re }}' {% endfor %}{% if 'ssh_port' in item.value %}--sshport {{ item.value.ssh_port }} {% endif %}root@{{ item.value.ssh_hostname }}:{{ path }} {{ zfs_syncoid_target_pool }}/{{ item.key }}/{{ path }} +{% endfor %} +{% if 'periodic' in item.value %} +TimeoutStartSec={{ item.value.periodic.timeout }} +{% endif %} +PrivateTmp=yes +ProtectHome=yes +ProtectKernelTunables=yes +ProtectControlGroups=yes +RestrictRealtime=yes + +[Install] +WantedBy=multi-user.target diff --git a/roles/storage/zfs/syncoid/templates/run.timer.j2 b/roles/storage/zfs/syncoid/templates/run.timer.j2 new file mode 100644 index 00000000..cc252854 --- /dev/null +++ b/roles/storage/zfs/syncoid/templates/run.timer.j2 @@ -0,0 +1,9 @@ +[Unit] +Description=syncoid-based backup for {{ item.key }} + +[Timer] +OnCalendar={{ item.value.periodic.schedule }} +WakeSystem={{ zfs_syncoid_autosuspend | ternary('true', 'false') }} + +[Install] +WantedBy=timers.target diff --git a/roles/storage/zfs/syncoid/templates/systemd.service.j2 b/roles/storage/zfs/syncoid/templates/systemd.service.j2 deleted file mode 100644 index 0d214595..00000000 --- a/roles/storage/zfs/syncoid/templates/systemd.service.j2 +++ /dev/null @@ -1,19 +0,0 @@ -[Unit] -Description=syncoid-based backup for {{ item.key }} - -[Service] -Type=oneshot -{% for path,config in item.value.paths.items() %} -ExecStart=/var/lib/syncoid/syncoid_wrapper --dumpsnaps --quiet {{ config.recursive | default(false) | ternary('-r ', '') }}{{ config.skip_parent | default(false) | ternary('--skip-parent ', '') }}{% for re in config.exclude | default([]) %}--exclude='{{ re }}' {% endfor %}{% if 'ssh_port' in item.value %}--sshport {{ item.value.ssh_port }} {% endif %}root@{{ item.value.ssh_hostname }}:{{ path }} {{ zfs_syncoid_target_pool }}/{{ item.key }}/{{ path }} -{% endfor %} -{% if 'periodic' in item.value %} -TimeoutStartSec={{ item.value.periodic.timeout }} -{% endif %} -PrivateTmp=yes -ProtectHome=yes -ProtectKernelTunables=yes -ProtectControlGroups=yes -RestrictRealtime=yes - -[Install] -WantedBy=multi-user.target diff --git a/roles/storage/zfs/syncoid/templates/systemd.timer.j2 b/roles/storage/zfs/syncoid/templates/systemd.timer.j2 deleted file mode 100644 index 260a8938..00000000 --- a/roles/storage/zfs/syncoid/templates/systemd.timer.j2 +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=syncoid-based backup for {{ item.key }} - -[Timer] -OnCalendar={{ item.value.periodic.schedule }} - -[Install] -WantedBy=timers.target -- cgit v1.2.3 From a23e7f8055818a1d95b005998f094a249f71b490 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Wed, 19 May 2021 18:55:24 +0200 Subject: syncoid: autosuspend cleanup and cooldown --- roles/storage/zfs/syncoid/tasks/main.yml | 15 +++++++++++++++ roles/storage/zfs/syncoid/templates/run.service.j2 | 3 +++ 2 files changed, 18 insertions(+) (limited to 'roles/storage/zfs/syncoid/templates') diff --git a/roles/storage/zfs/syncoid/tasks/main.yml b/roles/storage/zfs/syncoid/tasks/main.yml index 7c489baa..bab61a89 100644 --- a/roles/storage/zfs/syncoid/tasks/main.yml +++ b/roles/storage/zfs/syncoid/tasks/main.yml @@ -69,6 +69,21 @@ - name: enable autosuspend when: zfs_syncoid_autosuspend block: + - name: generate syncoid post script + copy: + content: | + #!/bin/bash + + echo "running sanoid --prune to cleanup old snapshots" + sleep 5 + systemctl start --wait sanoid-prune.service + + echo "wait 60s (autosuspend cooldown period)" + sleep 60 + echo "done." + dest: /var/lib/syncoid/syncoid_post + mode: 0755 + - name: install autosuspend script template: src: autosuspend.py.j2 diff --git a/roles/storage/zfs/syncoid/templates/run.service.j2 b/roles/storage/zfs/syncoid/templates/run.service.j2 index 0d214595..33283576 100644 --- a/roles/storage/zfs/syncoid/templates/run.service.j2 +++ b/roles/storage/zfs/syncoid/templates/run.service.j2 @@ -6,6 +6,9 @@ Type=oneshot {% for path,config in item.value.paths.items() %} ExecStart=/var/lib/syncoid/syncoid_wrapper --dumpsnaps --quiet {{ config.recursive | default(false) | ternary('-r ', '') }}{{ config.skip_parent | default(false) | ternary('--skip-parent ', '') }}{% for re in config.exclude | default([]) %}--exclude='{{ re }}' {% endfor %}{% if 'ssh_port' in item.value %}--sshport {{ item.value.ssh_port }} {% endif %}root@{{ item.value.ssh_hostname }}:{{ path }} {{ zfs_syncoid_target_pool }}/{{ item.key }}/{{ path }} {% endfor %} +{% if zfs_syncoid_autosuspend %} +ExecStart=/var/lib/syncoid/syncoid_post +{% endif %} {% if 'periodic' in item.value %} TimeoutStartSec={{ item.value.periodic.timeout }} {% endif %} -- cgit v1.2.3 From 389f7be86f07a08a6282e02913a676aeef328db5 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Wed, 19 May 2021 23:39:10 +0200 Subject: syncoid: run vs pull and fix autosuspend timer --- roles/storage/zfs/syncoid/tasks/main.yml | 15 ++++++++++----- .../zfs/syncoid/templates/autosuspend.timer.j2 | 2 +- .../storage/zfs/syncoid/templates/pull.service.j2 | 22 ++++++++++++++++++++++ roles/storage/zfs/syncoid/templates/pull.timer.j2 | 9 +++++++++ roles/storage/zfs/syncoid/templates/run.service.j2 | 22 ---------------------- roles/storage/zfs/syncoid/templates/run.timer.j2 | 9 --------- 6 files changed, 42 insertions(+), 37 deletions(-) create mode 100644 roles/storage/zfs/syncoid/templates/pull.service.j2 create mode 100644 roles/storage/zfs/syncoid/templates/pull.timer.j2 delete mode 100644 roles/storage/zfs/syncoid/templates/run.service.j2 delete mode 100644 roles/storage/zfs/syncoid/templates/run.timer.j2 (limited to 'roles/storage/zfs/syncoid/templates') diff --git a/roles/storage/zfs/syncoid/tasks/main.yml b/roles/storage/zfs/syncoid/tasks/main.yml index bab61a89..6232eea7 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: run.service.j2 - dest: "/etc/systemd/system/syncoid-run-{{ item.key }}.service" + src: pull.service.j2 + dest: "/etc/systemd/system/syncoid-pull-{{ 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: run.timer.j2 - dest: "/etc/systemd/system/syncoid-run-{{ item.key }}.timer" + src: pull.timer.j2 + dest: "/etc/systemd/system/syncoid-pull-{{ item.key }}.timer" - name: make sure systemd timer units for periodic backups are enabled and started loop: "{{ zfs_syncoid_sources | dict2items }}" @@ -62,7 +62,7 @@ when: "'periodic' in item.value" systemd: daemon_reload: yes - name: "syncoid-run-{{ item.key }}.timer" + name: "syncoid-pull-{{ item.key }}.timer" state: started enabled: yes @@ -84,6 +84,11 @@ dest: /var/lib/syncoid/syncoid_post mode: 0755 + - name: install python deps + apt: + name: "{{ python_basename }}-dbus" + state: present + - name: install autosuspend script template: src: autosuspend.py.j2 diff --git a/roles/storage/zfs/syncoid/templates/autosuspend.timer.j2 b/roles/storage/zfs/syncoid/templates/autosuspend.timer.j2 index 5c0b3329..40852739 100644 --- a/roles/storage/zfs/syncoid/templates/autosuspend.timer.j2 +++ b/roles/storage/zfs/syncoid/templates/autosuspend.timer.j2 @@ -2,7 +2,7 @@ Description=automatic system suspender for syncoid-based backups [Timer] -OnCalendar=*-*-* *-03/10-00 +OnCalendar=*-*-* *:03/10:00 [Install] WantedBy=timers.target diff --git a/roles/storage/zfs/syncoid/templates/pull.service.j2 b/roles/storage/zfs/syncoid/templates/pull.service.j2 new file mode 100644 index 00000000..33283576 --- /dev/null +++ b/roles/storage/zfs/syncoid/templates/pull.service.j2 @@ -0,0 +1,22 @@ +[Unit] +Description=syncoid-based backup for {{ item.key }} + +[Service] +Type=oneshot +{% for path,config in item.value.paths.items() %} +ExecStart=/var/lib/syncoid/syncoid_wrapper --dumpsnaps --quiet {{ config.recursive | default(false) | ternary('-r ', '') }}{{ config.skip_parent | default(false) | ternary('--skip-parent ', '') }}{% for re in config.exclude | default([]) %}--exclude='{{ re }}' {% endfor %}{% if 'ssh_port' in item.value %}--sshport {{ item.value.ssh_port }} {% endif %}root@{{ item.value.ssh_hostname }}:{{ path }} {{ zfs_syncoid_target_pool }}/{{ item.key }}/{{ path }} +{% endfor %} +{% if zfs_syncoid_autosuspend %} +ExecStart=/var/lib/syncoid/syncoid_post +{% endif %} +{% if 'periodic' in item.value %} +TimeoutStartSec={{ item.value.periodic.timeout }} +{% endif %} +PrivateTmp=yes +ProtectHome=yes +ProtectKernelTunables=yes +ProtectControlGroups=yes +RestrictRealtime=yes + +[Install] +WantedBy=multi-user.target diff --git a/roles/storage/zfs/syncoid/templates/pull.timer.j2 b/roles/storage/zfs/syncoid/templates/pull.timer.j2 new file mode 100644 index 00000000..cc252854 --- /dev/null +++ b/roles/storage/zfs/syncoid/templates/pull.timer.j2 @@ -0,0 +1,9 @@ +[Unit] +Description=syncoid-based backup for {{ item.key }} + +[Timer] +OnCalendar={{ item.value.periodic.schedule }} +WakeSystem={{ zfs_syncoid_autosuspend | ternary('true', 'false') }} + +[Install] +WantedBy=timers.target diff --git a/roles/storage/zfs/syncoid/templates/run.service.j2 b/roles/storage/zfs/syncoid/templates/run.service.j2 deleted file mode 100644 index 33283576..00000000 --- a/roles/storage/zfs/syncoid/templates/run.service.j2 +++ /dev/null @@ -1,22 +0,0 @@ -[Unit] -Description=syncoid-based backup for {{ item.key }} - -[Service] -Type=oneshot -{% for path,config in item.value.paths.items() %} -ExecStart=/var/lib/syncoid/syncoid_wrapper --dumpsnaps --quiet {{ config.recursive | default(false) | ternary('-r ', '') }}{{ config.skip_parent | default(false) | ternary('--skip-parent ', '') }}{% for re in config.exclude | default([]) %}--exclude='{{ re }}' {% endfor %}{% if 'ssh_port' in item.value %}--sshport {{ item.value.ssh_port }} {% endif %}root@{{ item.value.ssh_hostname }}:{{ path }} {{ zfs_syncoid_target_pool }}/{{ item.key }}/{{ path }} -{% endfor %} -{% if zfs_syncoid_autosuspend %} -ExecStart=/var/lib/syncoid/syncoid_post -{% endif %} -{% if 'periodic' in item.value %} -TimeoutStartSec={{ item.value.periodic.timeout }} -{% endif %} -PrivateTmp=yes -ProtectHome=yes -ProtectKernelTunables=yes -ProtectControlGroups=yes -RestrictRealtime=yes - -[Install] -WantedBy=multi-user.target diff --git a/roles/storage/zfs/syncoid/templates/run.timer.j2 b/roles/storage/zfs/syncoid/templates/run.timer.j2 deleted file mode 100644 index cc252854..00000000 --- a/roles/storage/zfs/syncoid/templates/run.timer.j2 +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=syncoid-based backup for {{ item.key }} - -[Timer] -OnCalendar={{ item.value.periodic.schedule }} -WakeSystem={{ zfs_syncoid_autosuspend | ternary('true', 'false') }} - -[Install] -WantedBy=timers.target -- cgit v1.2.3 From 131b103fa7c3659f84007bda2e60ada9649c996a Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Wed, 19 May 2021 23:45:39 +0200 Subject: syncoid/autosuspend: output state of checks in any case --- roles/storage/zfs/syncoid/templates/autosuspend.py.j2 | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'roles/storage/zfs/syncoid/templates') diff --git a/roles/storage/zfs/syncoid/templates/autosuspend.py.j2 b/roles/storage/zfs/syncoid/templates/autosuspend.py.j2 index e54ddd1b..fdeada52 100644 --- a/roles/storage/zfs/syncoid/templates/autosuspend.py.j2 +++ b/roles/storage/zfs/syncoid/templates/autosuspend.py.j2 @@ -4,7 +4,7 @@ import sys import dbus import re import datetime -#import pprint +# import pprint class AutoSuspender(object): @@ -12,7 +12,7 @@ class AutoSuspender(object): def __init__(self): self.__bus = dbus.SystemBus() self.__timer_re = re.compile('^syncoid-run-.*\.timer$') - #self.__pp = pprint.PrettyPrinter(indent=2) + # self.__pp = pprint.PrettyPrinter(indent=2) def _get_interface(self, dest, object, interface): try: @@ -65,14 +65,18 @@ class AutoSuspender(object): if(len(users) > 0): print("%d users logged in: %s" % (len(users), ", ".join([user[0] for user in users]))) result = False + else: + print("no users logged in.") 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)) + print("timer '%s' elapses in less then 10 Minutes -> %s (%s)" % (timer[0], next, until)) result = False + else: + print("timer '%s' elapses in %s (%s)" % (timer[0], until, next)) return result @@ -90,3 +94,5 @@ if __name__ == "__main__": s = AutoSuspender() if s.check(): s.suspend() + else: + print("not suspending system") -- cgit v1.2.3 From 7aa53137624142e666dd444c58994f1a2d387da9 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Wed, 19 May 2021 23:57:12 +0200 Subject: syncoid/autosuspend: nicer output of autosuspender --- roles/storage/zfs/syncoid/templates/autosuspend.py.j2 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'roles/storage/zfs/syncoid/templates') diff --git a/roles/storage/zfs/syncoid/templates/autosuspend.py.j2 b/roles/storage/zfs/syncoid/templates/autosuspend.py.j2 index fdeada52..3dd893b2 100644 --- a/roles/storage/zfs/syncoid/templates/autosuspend.py.j2 +++ b/roles/storage/zfs/syncoid/templates/autosuspend.py.j2 @@ -11,7 +11,7 @@ class AutoSuspender(object): def __init__(self): self.__bus = dbus.SystemBus() - self.__timer_re = re.compile('^syncoid-run-.*\.timer$') + self.__timer_re = re.compile('^syncoid-pull-.*\.timer$') # self.__pp = pprint.PrettyPrinter(indent=2) def _get_interface(self, dest, object, interface): @@ -19,7 +19,7 @@ class AutoSuspender(object): obj = self.__bus.get_object(dest, object) return dbus.Interface(obj, interface) except dbus.exceptions.DBusException as error: - print(error) + print("ERR: %s" % error) sys.exit(1) def _get_logind_manager_interface(self): @@ -63,20 +63,20 @@ class AutoSuspender(object): 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]))) + print(" [NO] %d users logged in: %s" % (len(users), ", ".join([user[0] for user in users]))) result = False else: - print("no users logged in.") + print(" [ok] no users logged in.") 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)) + print(" [NO] timer '%s' elapses in less then 10 Minutes -> %s (%s)" % (timer[0], next, until)) result = False else: - print("timer '%s' elapses in %s (%s)" % (timer[0], until, next)) + print(" [ok] timer '%s' elapses in %s (%s)" % (timer[0], until, next)) return result @@ -85,14 +85,14 @@ class AutoSuspender(object): if not manager.CanSuspend(): print("suspending is not possible!") return - - print("suspending system") manager.Suspend(False) if __name__ == "__main__": s = AutoSuspender() + print("checking if system should be suspended:") if s.check(): + print("trying to suspend system") s.suspend() else: - print("not suspending system") + print("not suspending system because at least one check failed.") -- cgit v1.2.3