From b94f3d0993355f935b469db652e3123af0fdbffe Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Mon, 27 Apr 2026 08:01:14 -0600 Subject: [PATCH] feat: add role fingerprints to syslog Feature: Add a fingerprint string to the system log to indicate when the role began successfully, and when the role finished successfully. The fingerprint string indicates the role name, a timestamp, and the platform. Reason: Users can see when the role was used and if it was used successfully. This information from the system log can be collected by log scanners and aggregators for further analysis. Result: The role logs fingerprints to the system log. This also adds a test to check if the fingerprints were written upon a successful role invocation. Signed-off-by: Rich Megginson --- .sanity-ansible-ignore-2.11.txt | 1 + .sanity-ansible-ignore-2.12.txt | 1 + .sanity-ansible-ignore-2.13.txt | 1 + .sanity-ansible-ignore-2.14.txt | 1 + .sanity-ansible-ignore-2.15.txt | 1 + .sanity-ansible-ignore-2.16.txt | 1 + .sanity-ansible-ignore-2.17.txt | 1 + .sanity-ansible-ignore-2.18.txt | 1 + .sanity-ansible-ignore-2.19.txt | 1 + .sanity-ansible-ignore-2.20.txt | 1 + .sanity-ansible-ignore-2.21.txt | 1 + .sanity-ansible-ignore-2.22.txt | 1 + .sanity-ansible-ignore-2.9.txt | 1 + library/sr_fingerprint.py | 88 +++++++++++++++++++++++++++++++++ tasks/main.yml | 6 +++ tasks/set_facts.yml | 6 +++ tests/tests_default.yml | 25 ++++++++++ 17 files changed, 138 insertions(+) create mode 100644 library/sr_fingerprint.py diff --git a/.sanity-ansible-ignore-2.11.txt b/.sanity-ansible-ignore-2.11.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.11.txt +++ b/.sanity-ansible-ignore-2.11.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.12.txt b/.sanity-ansible-ignore-2.12.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.12.txt +++ b/.sanity-ansible-ignore-2.12.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.13.txt b/.sanity-ansible-ignore-2.13.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.13.txt +++ b/.sanity-ansible-ignore-2.13.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.14.txt b/.sanity-ansible-ignore-2.14.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.14.txt +++ b/.sanity-ansible-ignore-2.14.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.15.txt b/.sanity-ansible-ignore-2.15.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.15.txt +++ b/.sanity-ansible-ignore-2.15.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.16.txt b/.sanity-ansible-ignore-2.16.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.16.txt +++ b/.sanity-ansible-ignore-2.16.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.17.txt b/.sanity-ansible-ignore-2.17.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.17.txt +++ b/.sanity-ansible-ignore-2.17.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.18.txt b/.sanity-ansible-ignore-2.18.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.18.txt +++ b/.sanity-ansible-ignore-2.18.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.19.txt b/.sanity-ansible-ignore-2.19.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.19.txt +++ b/.sanity-ansible-ignore-2.19.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.20.txt b/.sanity-ansible-ignore-2.20.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.20.txt +++ b/.sanity-ansible-ignore-2.20.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.21.txt b/.sanity-ansible-ignore-2.21.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.21.txt +++ b/.sanity-ansible-ignore-2.21.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.22.txt b/.sanity-ansible-ignore-2.22.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.22.txt +++ b/.sanity-ansible-ignore-2.22.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/.sanity-ansible-ignore-2.9.txt b/.sanity-ansible-ignore-2.9.txt index 5eff638..1d96d58 100644 --- a/.sanity-ansible-ignore-2.9.txt +++ b/.sanity-ansible-ignore-2.9.txt @@ -1,3 +1,4 @@ plugins/modules/network_connections.py validate-modules:missing-gplv3-license plugins/modules/network_state.py validate-modules:missing-gplv3-license +plugins/modules/sr_fingerprint.py validate-modules:missing-gplv3-license tests/network/ensure_provider_tests.py shebang!skip diff --git a/library/sr_fingerprint.py b/library/sr_fingerprint.py new file mode 100644 index 0000000..593dd39 --- /dev/null +++ b/library/sr_fingerprint.py @@ -0,0 +1,88 @@ +#!/usr/bin/python + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: sr_fingerprint +short_description: Write a message string to syslog using Ansible C(module.log) function. +description: + - Writes the given string to the system log using Ansible C(module.log) function. + - Intended for role-internal or diagnostic use. +author: Rich Megginson (@richm) +options: + sr_message: + description: Text to record in syslog. + type: str + required: true +""" + +EXAMPLES = """ +- name: Record a fingerprint message in syslog + sr_fingerprint: + sr_message: "system_role:ROLENAME" +""" + +RETURN = r""" # """ + +from ansible.module_utils.basic import AnsibleModule + +import datetime + + +def _local_iso8601_no_microseconds(): + """System local wall clock with local tz offset, ISO 8601, seconds only.""" + try: + utc = datetime.timezone.utc + except AttributeError: + import time + + return time.strftime("%Y-%m-%dT%H:%M:%S%z", time.localtime()) + # Prefer the local clock interpreted in the system timezone (not UTC displayed). + now = datetime.datetime.now() + astimezone = getattr(now, "astimezone", None) + if astimezone is not None: + try: + return astimezone().replace(microsecond=0).isoformat() + except (OSError, TypeError, ValueError): + pass + return datetime.datetime.now(utc).astimezone().replace(microsecond=0).isoformat() + + +def run_module(): + module_args = dict( + sr_message=dict(type="str", required=True), + ) + + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + ) + + log_message = "%s %s" % ( + module.params["sr_message"], + _local_iso8601_no_microseconds(), + ) + + if module.check_mode: + module.exit_json( + changed=False, + message="Check mode: message not logged - [%s]" % log_message, + ) + + module.log(log_message) + + # we don't actually change anything, so we're not changed - writing a log message + # is not considered a change + # also, we don't want to report changed every time the role runs + module.exit_json(changed=False) + + +def main(): + run_module() + + +if __name__ == "__main__": + main() diff --git a/tasks/main.yml b/tasks/main.yml index 8079dd7..6fae953 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -220,3 +220,9 @@ - name: Re-test connectivity ping: + +- name: Record role success fingerprint + sr_fingerprint: + sr_message: >- + success system_role:network ansible_version={{ ansible_version.full }} + {{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_version'] }} diff --git a/tasks/set_facts.yml b/tasks/set_facts.yml index 424a1a5..6ad4e40 100644 --- a/tasks/set_facts.yml +++ b/tasks/set_facts.yml @@ -7,6 +7,12 @@ difference(ansible_facts.keys() | list) | length > 0 no_log: true +- name: Record role begin fingerprint + sr_fingerprint: + sr_message: >- + begin system_role:network ansible_version={{ ansible_version.full }} + {{ ansible_facts['distribution'] }}-{{ ansible_facts['distribution_version'] }} + - name: Determine if system is ostree and set flag when: not __network_is_ostree is defined block: diff --git a/tests/tests_default.yml b/tests/tests_default.yml index ccc3e60..e071e46 100644 --- a/tests/tests_default.yml +++ b/tests/tests_default.yml @@ -3,12 +3,37 @@ - name: Test executing the role with default parameters hosts: all tasks: + - name: See if /dev/log exists for the fingerprint check + ansible.builtin.stat: + path: /dev/log + register: __register_dev_log + + - name: Set the start time for the journal search + ansible.builtin.set_fact: + __journal_start_time: "{{ ansible_facts['date_time']['date'] ~ ' ' ~ ansible_facts['date_time']['time'] }}" + when: __register_dev_log.stat.exists + - name: Include the task 'el_repo_setup.yml' include_tasks: tasks/el_repo_setup.yml - name: Run role with clear facts include_tasks: tasks/run_role_with_clear_facts.yml + # look for the exact module invocation, not some other message that might contain the string + - name: Check system journal contains role fingerprints + ansible.builtin.shell: + executable: /bin/bash + cmd: >- + set -eo pipefail; + journalctl --since "{{ __journal_start_time }}" --no-pager | + grep -v " Invoked with" | grep "sr_fingerprint.*begin system_role:network" || + { echo ERROR: BEGIN fingerprint not found; exit 1; }; + journalctl --since "{{ __journal_start_time }}" --no-pager | + grep -v " Invoked with" | grep "sr_fingerprint.*success system_role:network" || + { echo ERROR: SUCCESS fingerprint not found; exit 1; } + changed_when: false + when: __register_dev_log.stat.exists + # module_warning - see https://github.com/ansible/ansible/issues/85394 - name: Test warning and info logs assert: