From 6a557b86fd2f1fcb5df1a6e43decf7846926e94b Mon Sep 17 00:00:00 2001 From: Christian Krause Date: Thu, 21 Nov 2024 10:28:22 +0100 Subject: [PATCH] adds timesyncd role --- .github/workflows/molecule-timesyncd.yml | 50 +++++++ README.md | 1 + roles/systemd_timesyncd/README.md | 123 ++++++++++++++++++ roles/systemd_timesyncd/defaults/main.yml | 5 + .../systemd_timesyncd/meta/argument_specs.yml | 13 ++ roles/systemd_timesyncd/meta/main.yml | 31 +++++ .../molecule/default/converge.yml | 24 ++++ .../molecule/default/molecule.yml | 3 + .../molecule/default/verify.yml | 91 +++++++++++++ roles/systemd_timesyncd/requirements.yml | 7 + roles/systemd_timesyncd/tasks/main.yml | 97 ++++++++++++++ .../templates/timesyncd.conf.j2 | 21 +++ roles/systemd_timesyncd/vars/archlinux.yml | 5 + roles/systemd_timesyncd/vars/default.yml | 5 + roles/systemd_timesyncd/vars/main.yml | 40 ++++++ 15 files changed, 516 insertions(+) create mode 100644 .github/workflows/molecule-timesyncd.yml create mode 100644 roles/systemd_timesyncd/README.md create mode 100644 roles/systemd_timesyncd/defaults/main.yml create mode 100644 roles/systemd_timesyncd/meta/argument_specs.yml create mode 100644 roles/systemd_timesyncd/meta/main.yml create mode 100644 roles/systemd_timesyncd/molecule/default/converge.yml create mode 100644 roles/systemd_timesyncd/molecule/default/molecule.yml create mode 100644 roles/systemd_timesyncd/molecule/default/verify.yml create mode 100644 roles/systemd_timesyncd/requirements.yml create mode 100644 roles/systemd_timesyncd/tasks/main.yml create mode 100644 roles/systemd_timesyncd/templates/timesyncd.conf.j2 create mode 100644 roles/systemd_timesyncd/vars/archlinux.yml create mode 100644 roles/systemd_timesyncd/vars/default.yml create mode 100644 roles/systemd_timesyncd/vars/main.yml diff --git a/.github/workflows/molecule-timesyncd.yml b/.github/workflows/molecule-timesyncd.yml new file mode 100644 index 0000000..2da5d6a --- /dev/null +++ b/.github/workflows/molecule-timesyncd.yml @@ -0,0 +1,50 @@ +--- + +name: molecule-timesyncd + +on: + pull_request: + paths: + - .config/molecule + - .github/workflows/molecule-timesyncd.yml + - roles/systemd_timesyncd + push: + branches: + - main + - wip/next + paths: + - .config/molecule + - .github/workflows/molecule-timesyncd.yml + - roles/systemd_timesyncd + +jobs: + + molecule: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + distro: + - archlinux + - debian-bullseye + - debian-bookworm + - ubuntu-focal + - ubuntu-jammy + - ubuntu-noble + scenario: + - default + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - run: pip3 install ansible molecule molecule-plugins[docker] docker + - run: ansible --version + - run: molecule --version + - run: molecule test -p ${{ matrix.distro }} -s ${{ matrix.scenario }} + working-directory: ./roles/systemd_timesyncd + env: + ANSIBLE_DIFF_ALWAYS: 'True' + PY_COLORS: '1' + +... diff --git a/README.md b/README.md index 7bda255..6ee764d 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,4 @@ - [idiv_biodiversity.systemd.systemd_journald](roles/systemd_journald/README.md) - [idiv_biodiversity.systemd.systemd_networkd](roles/systemd_networkd/README.md) - [idiv_biodiversity.systemd.systemd_resolved](roles/systemd_resolved/README.md) +- [idiv_biodiversity.systemd.systemd_timesyncd](roles/systemd_timesyncd/README.md) diff --git a/roles/systemd_timesyncd/README.md b/roles/systemd_timesyncd/README.md new file mode 100644 index 0000000..9735b0b --- /dev/null +++ b/roles/systemd_timesyncd/README.md @@ -0,0 +1,123 @@ +Ansible Role: systemd_timesyncd +=============================== + +An Ansible role that configures **systemd-timesyncd**. + + +Table of Contents +----------------- + + + +- [Role Variables](#role-variables) + * [Time Zone](#time-zone) + * [NTP Servers](#ntp-servers) + * [Remove Legacy Packages](#remove-legacy-packages) +- [Dependencies](#dependencies) +- [Example Playbook](#example-playbook) + * [Top-Level Playbook](#top-level-playbook) + * [Role Dependency](#role-dependency) + + + +Role Variables +-------------- + +### Time Zone + +Set the system time zone. There is no default. The prefix is `system_` on +purpose, so this variable can be used across different roles that also set the +time zone. + +```yml +systemd_timesyncd_timezone: Europe/Berlin +``` + +### NTP Servers + +NTP servers are the preferred servers. They should be set to your networks +internal NTP servers. + +```yml +systemd_timesyncd_ntp_servers: + - ntp1.domain.org + - ntp2.domain.org + - ntp3.domain.org +``` + +Use regional pools for fallback servers: + +```yml +systemd_timesyncd_ntp_fallback_servers: + - 0.europe.pool.ntp.org + - 1.europe.pool.ntp.org + - 2.europe.pool.ntp.org + - 3.europe.pool.ntp.org +``` + +### Remove Legacy Packages + +Remove legacy timesync packages (ntp, chrony): + +```yml +systemd_timesyncd_remove_legacy_packages: yes +``` + + +Dependencies +------------ + +```yml +--- + +# requirements.yml + +collections: + + - name: community.general + + - name: idiv_biodiversity.systemd + version: X.Y.Z + +... +``` + + +Example Playbook +---------------- + +### Top-Level Playbook + +Write a top-level playbook: + +```yml +--- + +- name: head server + hosts: head + + roles: + - role: idiv_biodiversity.systemd.systemd_timesyncd + tags: + - systemd + - systemd-timesyncd + +... +``` + +### Role Dependency + +Define the role dependency in `meta/main.yml`: + +```yml +--- + +dependencies: + + - role: idiv_biodiversity.systemd.systemd_timesyncd + tags: + - systemd + - systemd-timesyncd + +... +``` diff --git a/roles/systemd_timesyncd/defaults/main.yml b/roles/systemd_timesyncd/defaults/main.yml new file mode 100644 index 0000000..e1d972e --- /dev/null +++ b/roles/systemd_timesyncd/defaults/main.yml @@ -0,0 +1,5 @@ +--- + +systemd_timesyncd_remove_legacy_packages: yes + +... diff --git a/roles/systemd_timesyncd/meta/argument_specs.yml b/roles/systemd_timesyncd/meta/argument_specs.yml new file mode 100644 index 0000000..9771c2c --- /dev/null +++ b/roles/systemd_timesyncd/meta/argument_specs.yml @@ -0,0 +1,13 @@ +--- + +argument_specs: + main: + options: + + systemd_timesyncd_remove_legacy_packages: + type: bool + + systemd_timesyncd_timezone: + type: str + +... diff --git a/roles/systemd_timesyncd/meta/main.yml b/roles/systemd_timesyncd/meta/main.yml new file mode 100644 index 0000000..90405a8 --- /dev/null +++ b/roles/systemd_timesyncd/meta/main.yml @@ -0,0 +1,31 @@ +--- + +galaxy_info: + author: Christian Krause + description: install and configure systemd-timesyncd + company: German Centre for Integrative Biodiversity Research (iDiv) + license: MIT + min_ansible_version: '2.9' + + platforms: + + - name: ArchLinux + versions: + - all + + - name: Debian + versions: + - bookworm + - bullseye + + - name: Ubuntu + versions: + - focal + - jammy + - noble + + galaxy_tags: + - systemd + - timesyncd + +... diff --git a/roles/systemd_timesyncd/molecule/default/converge.yml b/roles/systemd_timesyncd/molecule/default/converge.yml new file mode 100644 index 0000000..148c0f2 --- /dev/null +++ b/roles/systemd_timesyncd/molecule/default/converge.yml @@ -0,0 +1,24 @@ +--- + +- name: converge + hosts: all + + pre_tasks: + + - name: update package cache + ansible.builtin.package: + update_cache: yes + become: yes + changed_when: no + register: __update_package_cache + until: __update_package_cache is success + retries: 10 + delay: 2 + + tasks: + + - name: include the role + ansible.builtin.include_role: + name: idiv_biodiversity.systemd.systemd_timesyncd + +... diff --git a/roles/systemd_timesyncd/molecule/default/molecule.yml b/roles/systemd_timesyncd/molecule/default/molecule.yml new file mode 100644 index 0000000..c81cf5b --- /dev/null +++ b/roles/systemd_timesyncd/molecule/default/molecule.yml @@ -0,0 +1,3 @@ +--- + +... diff --git a/roles/systemd_timesyncd/molecule/default/verify.yml b/roles/systemd_timesyncd/molecule/default/verify.yml new file mode 100644 index 0000000..cf974aa --- /dev/null +++ b/roles/systemd_timesyncd/molecule/default/verify.yml @@ -0,0 +1,91 @@ +--- + +- name: verify + hosts: all + tasks: + + - name: include main vars + ansible.builtin.include_vars: ../../vars/main.yml + + - name: include OS-specific vars + ansible.builtin.include_vars: >- + {{ lookup("ansible.builtin.first_found", params) }} + vars: + params: + files: >- + {{ + __systemd_timesyncd_first_found | + map('regex_replace', '$', '.yml') | + list + }} + paths: ../../vars + + # ------------------------------------------------------------------------- + # check package + # ------------------------------------------------------------------------- + + - name: check package installation + ansible.builtin.package: + name: '{{ __systemd_timesyncd_package }}' + state: present + check_mode: yes + register: __systemd_timesyncd_installation + + - name: debug package installation + ansible.builtin.debug: + var: __systemd_timesyncd_installation + + - name: assert on package installation + ansible.builtin.assert: + that: + - not __systemd_timesyncd_installation.failed + - not __systemd_timesyncd_installation.changed + success_msg: 'package is installed' + + # ------------------------------------------------------------------------- + # check configuration + # ------------------------------------------------------------------------- + + - name: check configuration file + ansible.builtin.stat: + path: /etc/systemd/timesyncd.conf.d/60-ansible.conf + get_attributes: no + get_checksum: no + get_mime: no + check_mode: yes + register: __systemd_timesyncd_configuration + + - name: debug configuration file + ansible.builtin.debug: + var: __systemd_timesyncd_configuration + + - name: assert on configuration file + ansible.builtin.assert: + that: + - __systemd_timesyncd_configuration.stat.exists + success_msg: 'configuration file exists' + + # ------------------------------------------------------------------------- + # check service + # ------------------------------------------------------------------------- + + - name: check service + ansible.builtin.service: + name: systemd-timesyncd + enabled: yes + check_mode: yes + register: __systemd_timesyncd_service + + - name: debug service + ansible.builtin.debug: + var: __systemd_timesyncd_service + + - name: assert on service + ansible.builtin.assert: + that: + - not __systemd_timesyncd_service.failed + - not __systemd_timesyncd_service.changed + - __systemd_timesyncd_service.enabled + success_msg: 'service is enabled' + +... diff --git a/roles/systemd_timesyncd/requirements.yml b/roles/systemd_timesyncd/requirements.yml new file mode 100644 index 0000000..f229e0f --- /dev/null +++ b/roles/systemd_timesyncd/requirements.yml @@ -0,0 +1,7 @@ +--- + +collections: + + - name: community.general + +... diff --git a/roles/systemd_timesyncd/tasks/main.yml b/roles/systemd_timesyncd/tasks/main.yml new file mode 100644 index 0000000..87e0833 --- /dev/null +++ b/roles/systemd_timesyncd/tasks/main.yml @@ -0,0 +1,97 @@ +--- + +- name: include OS-specific vars + ansible.builtin.include_vars: >- + {{ lookup("ansible.builtin.first_found", __systemd_timesyncd_vars_files) }} + +- name: set time zone + community.general.timezone: + name: '{{ systemd_timesyncd_timezone }}' + become: yes + when: + - systemd_timesyncd_timezone is defined + tags: + - timezone + +# ----------------------------------------------------------------------------- +# install +# ----------------------------------------------------------------------------- + +- name: remove legacy NTP clients + ansible.builtin.package: + name: + - chrony + - ntp + state: absent + become: yes + when: + - systemd_timesyncd_remove_legacy_packages + +- name: 'install package {{ __systemd_timesyncd_package }}' + ansible.builtin.package: + name: '{{ __systemd_timesyncd_package }}' + state: present + become: yes + tags: + - install + +# ----------------------------------------------------------------------------- +# config +# ----------------------------------------------------------------------------- + +- name: create conf.d directory + ansible.builtin.file: + path: /etc/systemd/timesyncd.conf.d + state: directory + owner: root + group: root + mode: '0755' + become: yes + tags: + - config + +- name: configure systemd-timesyncd + ansible.builtin.template: + src: timesyncd.conf.j2 + dest: /etc/systemd/timesyncd.conf.d/60-ansible.conf + owner: root + group: root + mode: '0644' + become: yes + register: __systemd_timesyncd_configuration + tags: + - config + +# ----------------------------------------------------------------------------- +# service +# ----------------------------------------------------------------------------- + +- name: start systemd-timesyncd service + ansible.builtin.systemd: + name: systemd-timesyncd + state: >- + {{ + __systemd_timesyncd_configuration.changed | + default(False) | + ternary("restarted", "started") + }} + become: yes + when: + # forbidden in service unit via `ConditionVirtualization=!container` + - not ( + ansible_facts.virtualization_role == "guest" + and + ansible_facts.virtualization_type == "docker" + ) + tags: + - service + +- name: enable systemd-timesyncd service + ansible.builtin.systemd: + name: systemd-timesyncd + enabled: yes + become: yes + tags: + - service + +... diff --git a/roles/systemd_timesyncd/templates/timesyncd.conf.j2 b/roles/systemd_timesyncd/templates/timesyncd.conf.j2 new file mode 100644 index 0000000..2d5807a --- /dev/null +++ b/roles/systemd_timesyncd/templates/timesyncd.conf.j2 @@ -0,0 +1,21 @@ +# {{ ansible_managed }} + +[Time] +{% if systemd_timesyncd_ntp_servers is defined %} +{% if systemd_timesyncd_ntp_servers is iterable and + systemd_timesyncd_ntp_servers is not mapping and + systemd_timesyncd_ntp_servers is not string %} +NTP={{ systemd_timesyncd_ntp_servers | join(" ") }} +{% else %} +NTP={{ systemd_timesyncd_ntp_servers }} +{% endif %} +{% endif %} +{% if systemd_timesyncd_ntp_fallback_servers is defined %} +{% if systemd_timesyncd_ntp_fallback_servers is iterable and + systemd_timesyncd_ntp_fallback_servers is not mapping and + systemd_timesyncd_ntp_fallback_servers is not string %} +FallbackNTP={{ systemd_timesyncd_ntp_fallback_servers | join(" ") }} +{% else %} +FallbackNTP={{ systemd_timesyncd_ntp_fallback_servers }} +{% endif %} +{% endif %} diff --git a/roles/systemd_timesyncd/vars/archlinux.yml b/roles/systemd_timesyncd/vars/archlinux.yml new file mode 100644 index 0000000..fc97509 --- /dev/null +++ b/roles/systemd_timesyncd/vars/archlinux.yml @@ -0,0 +1,5 @@ +--- + +__systemd_timesyncd_package: systemd + +... diff --git a/roles/systemd_timesyncd/vars/default.yml b/roles/systemd_timesyncd/vars/default.yml new file mode 100644 index 0000000..259a9a6 --- /dev/null +++ b/roles/systemd_timesyncd/vars/default.yml @@ -0,0 +1,5 @@ +--- + +__systemd_timesyncd_package: systemd-timesyncd + +... diff --git a/roles/systemd_timesyncd/vars/main.yml b/roles/systemd_timesyncd/vars/main.yml new file mode 100644 index 0000000..66201b5 --- /dev/null +++ b/roles/systemd_timesyncd/vars/main.yml @@ -0,0 +1,40 @@ +--- + +# ----------------------------------------------------------------------------- +# distro / os handles +# ----------------------------------------------------------------------------- + +__systemd_timesyncd_distro: >- + {{ ansible_distribution | lower }} + +__systemd_timesyncd_os: >- + {{ ansible_os_family | lower }} + +__systemd_timesyncd_distro_version: >- + {{ __systemd_timesyncd_distro }}_{{ ansible_distribution_major_version }} + +__systemd_timesyncd_os_version: >- + {{ __systemd_timesyncd_os }}_{{ ansible_distribution_major_version }} + +# ----------------------------------------------------------------------------- +# first found snippets +# ----------------------------------------------------------------------------- + +__systemd_timesyncd_first_found: + - '{{ __systemd_timesyncd_distro_version }}' + - '{{ __systemd_timesyncd_os_version }}' + - '{{ __systemd_timesyncd_distro }}' + - '{{ __systemd_timesyncd_os }}' + - default + +__systemd_timesyncd_vars_files: + files: >- + {{ + __systemd_timesyncd_first_found | + map('regex_replace', '$', '.yml') | + list + }} + paths: + - vars + +...