From 1ed0a59bed5269d63494ce8a7967c6b262e96814 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Thu, 19 Dec 2024 16:52:48 +0100 Subject: [PATCH] Adding and using Ansible info modules --- lib/ansible/ds389_instances_info.py | 116 ++++++++ lib/ansible/ds389_logging_info.py | 277 ++++++++++++++++++ playbooks/library | 1 + roles/389ds-check-initial/tasks/main.yaml | 13 +- .../tasks/config-facility.yaml | 76 +++-- roles/389ds-config-logging/tasks/main.yaml | 22 +- 6 files changed, 472 insertions(+), 33 deletions(-) create mode 100644 lib/ansible/ds389_instances_info.py create mode 100644 lib/ansible/ds389_logging_info.py create mode 120000 playbooks/library diff --git a/lib/ansible/ds389_instances_info.py b/lib/ansible/ds389_instances_info.py new file mode 100644 index 0000000..ccb9891 --- /dev/null +++ b/lib/ansible/ds389_instances_info.py @@ -0,0 +1,116 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +@summary: Ansible info module for retrieving list of 389ds directory server instances. + +@author: Frank Brehm +@contact: frank.brehm@pixelpark.com +@copyright: © 2024 by Frank Brehm, Berlin +""" + +# Copyright (c) 2024, Frank Brehm +# GNU General Public License v3.0+ +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r""" +--- +module: ds389_instances_info +short_description: Retrieve list of 389ds directory server instances +description: + - Get a list of installed instances of a 389ds directory server +author: "Frank Brehm " +extends_documentation_fragment: + - community.general.attributes + - community.general.attributes.info_module +options: {} + +""" + +EXAMPLES = r""" +# Gather list of instances of 389ds directory server +- name: Get list of instances of 389ds directory server + ds389_instances_info: {} + register: ds389_instance_info + +""" + +RETURN = r""" +--- +changed: false +failed: false +instances: + - slapd-dev-ds01 + +""" + +# Standard modules +import warnings +from subprocess import run + +# Third party modules +from ansible.module_utils.basic import AnsibleModule +# from ansible.module_utils.common.text.converters import to_text +from ansible.module_utils.common.process import get_bin_path + + +# ############################################################################## +class Ds389InstancesInfo(object): + """Class for retrieving list of 389ds directory server instances.""" + + encoding = 'utf-8' + + # -------------------------------------------------------------------------- + def __init__(self): + """Initialize the Ds389InstancesInfo object.""" + pass + + # -------------------------------------------------------------------------- + def run(self): + """Retrieve the list anyhow ...""" + try: + cmd = get_bin_path('dsctl') + except ValueError: + return [] + + if not cmd: + return [] + + proc = run([cmd, '-l'], capture_output=True, encoding=self.encoding) + + if proc.stderr: + warnings.warn(proc.stderr) + + result = [] + for line in proc.stdout.splitlines(): + instance = line.strip() + if instance: + result.append(instance) + + return result + + +# ############################################################################## +def main(): + """Execute main entry function of this module.""" + global module + + module = AnsibleModule( + argument_spec={}, + supports_check_mode=True, + ) + + instances = Ds389InstancesInfo().run() + module.exit_json(changed=False, instances=instances) + + +# ############################################################################## +if __name__ == '__main__': + main() + +# ============================================================================= + +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list diff --git a/lib/ansible/ds389_logging_info.py b/lib/ansible/ds389_logging_info.py new file mode 100644 index 0000000..be1a647 --- /dev/null +++ b/lib/ansible/ds389_logging_info.py @@ -0,0 +1,277 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +@summary: Ansible info module for retrieving logging configuration of a 389ds instance. + +@author: Frank Brehm +@contact: frank.brehm@pixelpark.com +@copyright: © 2024 by Frank Brehm, Berlin +""" + +# Copyright (c) 2024, Frank Brehm +# GNU General Public License v3.0+ +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r""" +--- +module: ds389_logging_info +short_description: Retrieve logging configuration of a 389ds instance +description: + - Get the logging configuration of an instance of a 389ds directory server +author: "Frank Brehm " +extends_documentation_fragment: + - community.general.attributes + - community.general.attributes.info_module +options: + instance: + type: str + description: + - The name of the instance, where the logging configuration should be retrieved. + required: true + +""" + +EXAMPLES = r""" +- name: Get logging configuration of the 389ds directory server + ds389_logging_info: + instance: slapd-dev-ds21 + register: ds389_logging_config + +""" + +RETURN = r""" +--- +changed: false +failed: false +config: + access: + compress: false + level: 256 + list: + - /var/log/dirsrv/slapd-dev-ds21/access.log.20241209-160851 + - /var/log/dirsrv/slapd-dev-ds21/access.log.20241210-160853 + - /var/log/dirsrv/slapd-dev-ds21/access.log.20241211-160858 + logfile: /var/log/dirsrv/slapd-dev-ds21/access.log + logbuffering: true + logexpirationtime: 1 + logexpirationtimeunit: 'month' + logging-enabled: true + logmaxdiskspace: 500 + logminfreediskspace: 5 + logrotationsync-enabled: false + logrotationsynchour: 0 + logrotationsyncmin: 0 + logrotationtime: 1 + logrotationtimeunit: 'day' + maxlogsize: 100 + maxlogsperdir: 10 + mode: '600' + audit: + compress: false + display-attrs: ~ + level: 256 + list: + - /var/log/dirsrv/slapd-dev-ds21/audit.log.20241211-154245 + logfile: /var/log/dirsrv/slapd-dev-ds21/access.log + logbuffering: true + logexpirationtime: 1 + logexpirationtimeunit: 'month' + logging-enabled: false + logmaxdiskspace: 500 + logminfreediskspace: 5 + logrotationsync-enabled: false + logrotationsynchour: 0 + logrotationsyncmin: 0 + logrotationtime: 1 + logrotationtimeunit: 'week' + maxlogsize: 100 + maxlogsperdir: 10 + mode: '600' + auditfail: + compress: false + list: [] + logfile: /var/log/dirsrv/slapd-dev-ds21/access.log + logbuffering: true + logexpirationtime: 1 + logexpirationtimeunit: 'month' + logging-enabled: true + logmaxdiskspace: 500 + logminfreediskspace: 5 + logrotationsync-enabled: false + logrotationsynchour: 0 + logrotationsyncmin: 0 + logrotationtime: 1 + logrotationtimeunit: 'week' + maxlogsize: 100 + maxlogsperdir: 10 + mode: '600' + error: + compress: false + level: 16384 + list: + - /var/log/dirsrv/slapd-dev-ds21/error.log.20241211-174002 + logfile: /var/log/dirsrv/slapd-dev-ds21/access.log + logexpirationtime: 1 + logexpirationtimeunit: 'month' + logging-enabled: true + logmaxdiskspace: 500 + logminfreediskspace: 5 + logrotationsync-enabled: false + logrotationsynchour: 0 + logrotationsyncmin: 0 + logrotationtime: 1 + logrotationtimeunit: 'day' + maxlogsize: 100 + maxlogsperdir: 10 + mode: '600' + security: + compress: true + level: 256 + list: + - '/var/log/dirsrv/slapd-dev-ds21/security.log.20241210-153222' + - '/var/log/dirsrv/slapd-dev-ds21/security.log.20241126-153149' + - '/var/log/dirsrv/slapd-dev-ds21/security.log.20241119-153143' + - '/var/log/dirsrv/slapd-dev-ds21/security.log.20241112-153127' + - '/var/log/dirsrv/slapd-dev-ds21/security.log.20241105-153116' + - '/var/log/dirsrv/slapd-dev-ds21/security.log.20241203-153153' + logbuffering: true + logexpirationtime: 1 + logexpirationtimeunit: 'month' + logfile: '/var/log/dirsrv/slapd-dev-ds21/security.log' + logging-enabled: true + logmaxdiskspace: 500 + logminfreediskspace: 5 + logrotationsync-enabled: false + logrotationsynchour: 0 + logrotationsyncmin: 0 + logrotationtime: 1 + logrotationtimeunit: 'week' + maxlogsize: 100 + maxlogsperdir: 10 + mode: '600' + + +""" + +# Standard modules +import re +import warnings +from subprocess import run + +# Third party modules +from ansible.module_utils.basic import AnsibleModule +# from ansible.module_utils.common.text.converters import to_text +from ansible.module_utils.common.process import get_bin_path + + +# ############################################################################## +class Ds389LoggingInfo(object): + """Class for retrieving logging configuration of a 389ds directory server instance.""" + + encoding = 'utf-8' + + log_facilities = ('access', 'audit', 'auditfail', 'error', 'security') + + # -------------------------------------------------------------------------- + def __init__(self): + """Initialize the Ds389LoggingInfo object.""" + self.instance = module.params.get('instance') + + # -------------------------------------------------------------------------- + def run(self): + """Retrieve the list anyhow ...""" + try: + cmd = get_bin_path('dsconf') + except ValueError: + return {} + + if not cmd: + return {} + + proc = run( + [cmd, self.instance, 'config', 'get'], + capture_output=True, encoding=self.encoding) + + if proc.stderr: + warnings.warn(proc.stderr) + + proc.check_returncode() + + pat_logconfig = ( + r'^nsslapd-(' + '|'.join(self.log_facilities) + r')log(?:-([a-z-]*))?:\s*(\S.*)?') + re_logconfig = re.compile(pat_logconfig, re.IGNORECASE) + re_int = re.compile(r'^[+-]?\d+$') + re_float = re.compile(r'^[+-]?\d+\.\d*$') + + result = {} + for line in proc.stdout.splitlines(): + line = line.strip() + m = re_logconfig.match(line) + if not m: + continue + facility = m.group(1).lower() + key = m.group(2) + value = m.group(3) + + if not key: + key = 'logfile' + else: + key = key.lower() + + if facility not in result: + result[facility] = {} + + if key == 'list': + if 'list' not in result[facility]: + result[facility]['list'] = [] + if value: + result[facility]['list'].append(value) + continue + + if key != 'mode' and value is not None: + if re_int.match(value): + value = int(value) + elif re_float.match(value): + value = float(value) + elif value in ('yes', 'on', 'true'): + value = True + elif value in ('no', 'off', 'false'): + value = False + elif value == '': + value = None + + result[facility][key] = value + + return result + + +# ############################################################################## +def main(): + """Execute main entry function of this module.""" + global module + + module = AnsibleModule( + argument_spec={ + 'instance': { + 'type': 'str', + 'required': True, + }, + }, + supports_check_mode=True, + ) + + config = Ds389LoggingInfo().run() + module.exit_json(changed=False, config=config) + + +# ############################################################################## +if __name__ == '__main__': + main() + +# ============================================================================= + +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list diff --git a/playbooks/library b/playbooks/library new file mode 120000 index 0000000..7ae5bc3 --- /dev/null +++ b/playbooks/library @@ -0,0 +1 @@ +../lib/ansible \ No newline at end of file diff --git a/roles/389ds-check-initial/tasks/main.yaml b/roles/389ds-check-initial/tasks/main.yaml index c29caee..1bb24d1 100644 --- a/roles/389ds-check-initial/tasks/main.yaml +++ b/roles/389ds-check-initial/tasks/main.yaml @@ -11,13 +11,24 @@ - name: "Get the version of the 389ds LDAP server." ansible.builtin.set_fact: version_389ds: "{{ get_389ds_version.stdout }}" - cacheable: true - name: "Show version of 389ds LDAP server." debug: var: version_389ds verbosity: 0 +- name: "Get a list of all 389ds directory server instances." + ds389_instances_info: {} + register: ds389_instances_info + +- set_fact: + ds389_instances: "{{ ds389_instances_info.instances }}" + +- name: "Show the list of all 389ds directory server instances." + debug: + var: ds389_instances + verbosity: 0 + - name: "Fail for non existing 389ds LDAP server." ansible.builtin.fail: msg: "No 389ds LDAP server found on host '{{ inventory_hostname }}'." diff --git a/roles/389ds-config-logging/tasks/config-facility.yaml b/roles/389ds-config-logging/tasks/config-facility.yaml index 03fc655..96a5eb2 100644 --- a/roles/389ds-config-logging/tasks/config-facility.yaml +++ b/roles/389ds-config-logging/tasks/config-facility.yaml @@ -1,43 +1,59 @@ --- -- name: "Get current configuration of config for log facility '{{ log_facility.key }}'." - ansible.builtin.shell: "dsconf {{ slapd_instance | quote }} config get | grep -P -i 'nsslapd-{{ log_facility.key }}log' || true" - register: config_get - changed_when: false - check_mode: false - -- name: "Show current config_get" - debug: - var: config_get - verbosity: 3 - -- name: "Generate config hash." - when: config_get.stdout is not empty +- name: "Log facility is existing." + when: log_config.facility in ds389_logging_config block: - - name: "Set logging variables" + - name: "Set facts for {{ log_config.facility | quote }} logging." set_fact: - log_config: "{{ config_get.stdout_lines | cfg_389ds_to_dict }}" + cur_logfile: "{{ ds389_logging_config[log_config.facility]['logfile'] }}" + cur_enabled: "{{ ds389_logging_config[log_config.facility]['logging-enabled'] }}" + exp_logfile: "{{ base_logdir }}/slapd-{{ slapd_instance }}/{{ log_config.config.logfile }}" + exp_enabled: "{{ log_config.config.enabled }}" + config_logfile_key: "nsslapd-{{log_config.facility}}log" + facility: "{{ log_config.facility }}" + + - debug: + msg: "Current logfile: {{ cur_logfile | quote }}, expected: {{ exp_logfile | quote }}." + verbosity: 1 - - name: "Show config hash:" - debug: - var: log_config - verbosity: 2 + - name: "Replace logfile" + when: cur_logfile != exp_logfile + block: - - name: "Set config key for '{{ log_facility.key }}' logfile." - set_fact: - exp_logfile: "{{ base_logdir }}/slapd-{{ slapd_instance }}/{{ log_facility.value.logfile }}" - dict_logfile_key: "{{ log_facility.key }}log" - config_logfile_key: "nsslapd-{{ log_facility.key }}log" + - name: "Define command for setting logfile." + set_fact: + cmd_set_logfile: "dsconf {{ slapd_instance | quote }} config replace {{ config_logfile_key }}={{ exp_logfile }}" + + - name: "Show command for setting logfile." + debug: + var: cmd_set_logfile + verbosity: 0 + + - name: "Setting new value for {{ log_config.facility }} log to {{ exp_logfile | quote }} ..." + ansible.builtin.shell: "{{ cmd_set_logfile }}" - - name: "Show logfile stuff" - debug: - msg: "Current logfile: '{{ log_config[dict_logfile_key] }}', expected: '{{ exp_logfile }}'." + - debug: + msg: "Logging for facility {{ log_config.facility | quote }} is currently enabled: \ + {{ cur_enabled }}, expected is {{ log_config.config.enabled }}." verbosity: 1 - - name: "Setting new value for {{ log_facility.key }} log to '{{ exp_logfile }}' ..." - ansible.builtin.shell: "dsconf {{ slapd_instance | quote }} config replace {{ config_logfile_key }}={{ exp_logfile }}" - when: log_config[dict_logfile_key] != exp_logfile + - name: "Enabling/disabling logging" + when: cur_enabled != log_config.config.enabled + block: + + - name: "Define command for enabling/disabling logging facility {{ log_config.facility | quote }}." + set_fact: + cmd_enabling_logging: "dsconf {{ slapd_instance | quote }} config replace \ + nsslapd-{{ log_config.facility }}log-logging-enabled={{ log_config.config.enabled | bool_to_on_off }}" + + - name: "Show command for enabling/disabling logging facility {{ log_config.facility | quote }}." + debug: + var: cmd_enabling_logging + verbosity: 0 + + - name: "Enabling/disabling logging for facility {{ log_config.facility | quote }}." + ansible.builtin.shell: "{{ cmd_enabling_logging }}" # vim: filetype=yaml diff --git a/roles/389ds-config-logging/tasks/main.yaml b/roles/389ds-config-logging/tasks/main.yaml index 34e4c96..405b216 100644 --- a/roles/389ds-config-logging/tasks/main.yaml +++ b/roles/389ds-config-logging/tasks/main.yaml @@ -2,10 +2,28 @@ # Configuring logging of a 389ds LDAP server +- name: "Get logging configuration." + ds389_logging_info: + instance: "slapd-{{ slapd_instance }}" + register: get_logging_info + +- name: "Show get_logging_info" + debug: + var: get_logging_info + verbosity: 3 + +- set_fact: + ds389_logging_config: "{{ get_logging_info.config }}" + +- name: "Show current logging information:" + debug: + var: ds389_logging_config + verbosity: 1 + - name: "Configuring logging facility '{{ log_facility.key }}'." include_tasks: 'config-facility.yaml' - loop: "{{ logging | dict2items | list }}" + loop: "{{ logging | dict2items(key_name='facility', value_name='config') | list }}" loop_control: - loop_var: log_facility + loop_var: log_config # vim: filetype=yaml -- 2.39.5