From 518b98cfd12c3f7858493e34c153951c71543d81 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Wed, 13 Dec 2023 19:02:07 +0100 Subject: [PATCH] Implemented role System Maintenance Service Freezer --- group_vars/all | 11 +++ .../README.md | 16 +++++ .../system-maintenance-service-freezer.py | 71 +++++++++++++++++++ .../handlers/main.yml | 9 +++ .../meta/main.yml | 3 + .../tasks/main.yml | 20 ++++++ ...tem-maintenance-service-freezer.service.j2 | 7 ++ .../vars/main.yml | 1 + 8 files changed, 138 insertions(+) create mode 100644 roles/system-maintenance-service-freezer/README.md create mode 100644 roles/system-maintenance-service-freezer/files/system-maintenance-service-freezer.py create mode 100644 roles/system-maintenance-service-freezer/handlers/main.yml create mode 100644 roles/system-maintenance-service-freezer/meta/main.yml create mode 100644 roles/system-maintenance-service-freezer/tasks/main.yml create mode 100644 roles/system-maintenance-service-freezer/templates/system-maintenance-service-freezer.service.j2 create mode 100644 roles/system-maintenance-service-freezer/vars/main.yml diff --git a/group_vars/all b/group_vars/all index 6b3b586a..2ffb0eb0 100644 --- a/group_vars/all +++ b/group_vars/all @@ -67,6 +67,17 @@ version_mailu: "2.0" version_akaunting: "latest" version_mastodon: "latest" +# Services which modify the system: +system_maintenance_services: + - "backup-docker-to-local" + - "backup-remote-to-local" + - "backup-data-to-usb" + - "cleanup-backups" + - "cleanup-disc-space" + - "cleanup-failed-docker-backups" + - "heal-docker" + - "update-docker" + # Routings redirect_domain_mappings: - { source: "nextcloud.{{top_domain}}", target: "cloud.{{top_domain}}" } diff --git a/roles/system-maintenance-service-freezer/README.md b/roles/system-maintenance-service-freezer/README.md new file mode 100644 index 00000000..02ca67df --- /dev/null +++ b/roles/system-maintenance-service-freezer/README.md @@ -0,0 +1,16 @@ +# System Maintenance Service Freezer + +## Overview +This Ansible role is designed to manage system services through freezing (disabling) and defrosting (enabling) actions. It automates the process of managing crucial system services, especially useful for maintenance tasks like backups, cleanups, and updates. + + +## Role Variables +- `system_maintenance_services`: List of services to be managed by this role. + +## Usage +Configure the role by defining the required variables. The role creates systemd service files that control the specified services based on the `freeze` or `defrost` actions. + +For further details and usage examples, refer to the chat conversation with ChatGPT: [Link to ChatGPT Conversation](https://chat.openai.com/share/212af169-1b57-41df-bd2d-c3d32eb1331b). + +## Dependencies +- `systemd-notifier`: Ensure this role is present for handling service failures. diff --git a/roles/system-maintenance-service-freezer/files/system-maintenance-service-freezer.py b/roles/system-maintenance-service-freezer/files/system-maintenance-service-freezer.py new file mode 100644 index 00000000..984a3301 --- /dev/null +++ b/roles/system-maintenance-service-freezer/files/system-maintenance-service-freezer.py @@ -0,0 +1,71 @@ +import argparse +import subprocess +import time + + +def check_service_active(service_name): + """Check if a service is active.""" + result = subprocess.run(['systemctl', 'is-active', service_name], stdout=subprocess.PIPE) + return result.stdout.decode('utf-8').strip() == 'active' + +def service_exists(service_name): + """Check if a service exists.""" + result = subprocess.run(['systemctl', 'status', service_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return result.returncode == 0 + +def freeze(services_to_wait_for, ignored_services): + # Filter services that exist and are not in the ignored list + active_services = [service for service in services_to_wait_for if service_exists(service) and service not in ignored_services] + + while active_services: + for service in active_services: + if not check_service_active(service): + print(f"{service} stopped.") + # Disable the service + subprocess.run(['systemctl', 'disable', service]) + print(f"{service} disabled.") + + # Stop and disable the corresponding timer, if it exists + timer_name = service + ".timer" + timer_check = subprocess.run(['systemctl', 'list-timers', '--all', timer_name], stdout=subprocess.PIPE) + if timer_name in timer_check.stdout.decode(): + subprocess.run(['systemctl', 'stop', timer_name]) + subprocess.run(['systemctl', 'disable', timer_name]) + print(f"{timer_name} stopped and disabled.") + active_services.remove(service) + else: + print(f"Waiting for {service} to stop...") + time.sleep(5) + print("All required services have stopped.") + +def defrost(services_to_wait_for, ignored_services): + for service in services_to_wait_for: + if service not in ignored_services and service_exists(service): + # Enable the service + subprocess.run(['systemctl', 'enable', service]) + print(f"{service} enabled.") + + # Start and enable the corresponding timer, if it exists + timer_name = service + ".timer" + timer_check = subprocess.run(['systemctl', 'list-timers', '--all', timer_name], stdout=subprocess.PIPE) + if timer_name in timer_check.stdout.decode(): + subprocess.run(['systemctl', 'start', timer_name]) + subprocess.run(['systemctl', 'enable', timer_name]) + print(f"{timer_name} started and enabled.") + +def main(services_to_wait_for, ignored_services, action): + if action == 'freeze': + # Code to handle freeze action + freeze(services_to_wait_for, ignored_services) + elif action == 'defrost': + defrost(services_to_wait_for, ignored_services) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='freezes and defrost systemctl services and timers') + parser.add_argument('action', choices=['freeze', 'defrost'], help='Action to perform: freeze or defrost services.') + parser.add_argument('services', help='Comma-separated list of services to apply the action to') + parser.add_argument('--ignore', help='Comma-separated list of services to ignore in the action', default='') + args = parser.parse_args() + services_to_wait_for = args.services.split(',') + ignored_services = args.ignore.split(',') if args.ignore else [] + main(services_to_wait_for, ignored_services,args.action) diff --git a/roles/system-maintenance-service-freezer/handlers/main.yml b/roles/system-maintenance-service-freezer/handlers/main.yml new file mode 100644 index 00000000..8e92350b --- /dev/null +++ b/roles/system-maintenance-service-freezer/handlers/main.yml @@ -0,0 +1,9 @@ +- name: "restart system-maintenance-service-freeze.service" + systemd: + name: system-maintenance-service-freeze.service + daemon_reload: yes + +- name: "restart system-maintenance-service-defrost.service" + systemd: + name: system-maintenance-service-defrost.service + daemon_reload: yes \ No newline at end of file diff --git a/roles/system-maintenance-service-freezer/meta/main.yml b/roles/system-maintenance-service-freezer/meta/main.yml new file mode 100644 index 00000000..bebc0b50 --- /dev/null +++ b/roles/system-maintenance-service-freezer/meta/main.yml @@ -0,0 +1,3 @@ +--- +dependencies: + - role: systemd-notifier diff --git a/roles/system-maintenance-service-freezer/tasks/main.yml b/roles/system-maintenance-service-freezer/tasks/main.yml new file mode 100644 index 00000000..eea7ab96 --- /dev/null +++ b/roles/system-maintenance-service-freezer/tasks/main.yml @@ -0,0 +1,20 @@ +--- +- name: create {{freezer_script}} + copy: + src: system-maintenance-service-freezer.py + dest: "{{freezer_script}}" + +- name: Configure system-maintenance-service for each action + loop: + - freeze + - defrost + template: + src: system-maintenance-service-freezer.service.j2 + dest: "/etc/systemd/system/system-maintenance-service-{{ item }}.service" + notify: "restart system-maintenance-service-{{ item }} service" + when: run_once_systemd_freezer is not defined + +- name: run the system_maintenance_service_freezer tasks once + set_fact: + run_once_systemd_freezer: true + when: run_once_systemd_freezer is not defined \ No newline at end of file diff --git a/roles/system-maintenance-service-freezer/templates/system-maintenance-service-freezer.service.j2 b/roles/system-maintenance-service-freezer/templates/system-maintenance-service-freezer.service.j2 new file mode 100644 index 00000000..bde33184 --- /dev/null +++ b/roles/system-maintenance-service-freezer/templates/system-maintenance-service-freezer.service.j2 @@ -0,0 +1,7 @@ +[Unit] +Description={{item}} systemctl maintanance services +OnFailure=systemd-notifier@%n.service + +[Service] +Type=oneshot +ExecStart=/bin/sh -c '/usr/bin/python {{ freezer_script }} {{item}} {{ system_maintenance_services | join(",") }}' \ No newline at end of file diff --git a/roles/system-maintenance-service-freezer/vars/main.yml b/roles/system-maintenance-service-freezer/vars/main.yml new file mode 100644 index 00000000..603baa23 --- /dev/null +++ b/roles/system-maintenance-service-freezer/vars/main.yml @@ -0,0 +1 @@ +freezer_script: "{{path_administrator_scripts}}system-maintenance-service-freezer.py"