From bed295f3ff2572dcb48cf58a1fe0c449a6e2c815 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Thu, 19 Dec 2024 18:28:48 +0100 Subject: [PATCH] Adding Ansible info module ds389_plugins_info --- lib/ansible/ds389_plugins_info.py | 309 ++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 lib/ansible/ds389_plugins_info.py diff --git a/lib/ansible/ds389_plugins_info.py b/lib/ansible/ds389_plugins_info.py new file mode 100644 index 0000000..1b7bbff --- /dev/null +++ b/lib/ansible/ds389_plugins_info.py @@ -0,0 +1,309 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +@summary: Ansible info module for retrieving configuration of all plugins 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 all plugins configuration of a 389ds instance +description: + - Get the configuration of all plugims 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 plugins configuration should be retrieved. + required: true + +""" + +EXAMPLES = r""" +- name: Get plugin configuration of the 389ds directory server + ds389_plugins_info: + instance: slapd-dev-ds21 + register: ds389_plugin_config + +""" + +RETURN = r""" +--- +changed: false +failed: false +config: + memberof: + enabled: true + +""" + +# Standard modules +import re +import warnings +from subprocess import CalledProcessError +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 Ds389PluginsInfo(object): + """Class for retrieving configuration of all plugins of a 389ds directory server instance.""" + + encoding = 'utf-8' + + re_int = re.compile(r'^[+-]?\d+$') + re_float = re.compile(r'^[+-]?\d+\.\d*$') + re_cn = re.compile(r'^cn:\s+(.*)', re.IGNORECASE) + re_plugin_version = re.compile(r'^nsslapd-pluginVersion:\s+(.*)', re.IGNORECASE) + re_enabled = re.compile(r'^nsslapd-pluginEnabled:\s+(.*)', re.IGNORECASE) + + # -------------------------------------------------------------------------- + def __init__(self): + """Initialize the Ds389LoggingInfo object.""" + self.instance = module.params.get('instance') + + self.result = {} + self.cmd = None + + # -------------------------------------------------------------------------- + def run(self): + """Retrieve the plugins comfig anyhow ...""" + try: + self.cmd = get_bin_path('dsconf') + except ValueError: + return {} + + if not self.cmd: + return {} + + self.get_memberof() + self.get_referint() + + return self.result + + # ------------------ + def mangle_value(self, value): + """Tape cast the given value to a boolean, integer of float value, if it is looking so.""" + if self.re_int.match(value): + return int(value) + if self.re_float.match(value): + return float(value) + if value.lower() == 'on': + return True + if value.lower() == 'yes': + return True + if value.lower() == 'off': + return False + if value.lower() == 'no': + return False + + return value + + # -------------------------------------------------------------------------- + def get_memberof(self): + """Get config of memberof plugin.""" + proc = run( + [self.cmd, self.instance, 'plugin', 'memberof', 'show'], + capture_output=True, encoding=self.encoding) + if proc.stderr: + warnings.warn(proc.stderr) + try: + proc.check_returncode() + except CalledProcessError: + return + + re_attr = re.compile(r'^memberOfAttr:\s+(.*)', re.IGNORECASE) + re_groupattr = re.compile(r'^memberOfGroupAttr:\s+(.*)', re.IGNORECASE) + re_allbackends = re.compile(r'^memberOfAllBackends:\s+(.*)', re.IGNORECASE) + re_skipnested = re.compile(r'^memberOfSkipNested:\s+(.*)', re.IGNORECASE) + re_scope = re.compile(r'^memberOfEntryScope:\s+(.*)', re.IGNORECASE) + re_exclude = re.compile(r'^memberOfEntryScopeExcludeSubtree:\s+(.*)', re.IGNORECASE) + re_autoaddoc = re.compile(r'^memberOfAutoAddOC:\s+(.*)', re.IGNORECASE) + + if 'memberof' not in self.result: + self.result['memberof'] = {} + + for line in proc.stdout.splitlines(): + line = line.strip() + if not line: + continue + + m = self.re_cn.match(line) + if m: + self.result['memberof']['cn'] = m.group(1) + continue + + m = self.re_plugin_version.match(line) + if m: + self.result['memberof']['plugin_version'] = m.group(1) + continue + + m = self.re_enabled.match(line) + if m: + self.result['memberof']['enabled'] = self.mangle_value(m.group(1)) + continue + + m = re_attr.match(line) + if m: + self.result['memberof']['attr'] = m.group(1) + continue + + m = re_groupattr.match(line) + if m: + if 'groupattr' not in self.result['memberof']: + self.result['memberof']['groupattr'] = [] + self.result['memberof']['groupattr'].append(m.group(1)) + continue + + m = re_allbackends.match(line) + if m: + self.result['memberof']['allbackends'] = self.mangle_value(m.group(1)) + continue + + m = re_skipnested.match(line) + if m: + self.result['memberof']['skipnested'] = self.mangle_value(m.group(1)) + continue + + m = re_scope.match(line) + if m: + if 'scope' not in self.result['memberof']: + self.result['memberof']['scope'] = [] + self.result['memberof']['scope'].append(m.group(1)) + continue + + m = re_exclude.match(line) + if m: + if 'exclude' not in self.result['memberof']: + self.result['memberof']['exclude'] = [] + self.result['memberof']['exclude'].append(m.group(1)) + continue + + m = re_autoaddoc.match(line) + if m: + self.result['memberof']['auto_add_oc'] = m.group(1) + continue + + # -------------------------------------------------------------------------- + def get_referint(self): + """Get config of mreferential-integrity plugin.""" + proc = run( + [self.cmd, self.instance, 'plugin', 'referential-integrity', 'show'], + capture_output=True, encoding=self.encoding) + if proc.stderr: + warnings.warn(proc.stderr) + try: + proc.check_returncode() + except CalledProcessError: + return + + re_upate_delay = re.compile(r'^referint-update-delay:\s+(.*)', re.IGNORECASE) + re_membership_attr = re.compile(r'^referint-membership-attr:\s+(.*)', re.IGNORECASE) + re_entry_scope = re.compile(r'^nsslapd-pluginEntryScope:\s+(.*)', re.IGNORECASE) + re_exclude_entry_scope = re.compile( + r'^nsslapd-pluginExcludeEntryScope:\s+(.*)', re.IGNORECASE) + re_container_scope = re.compile(r'^mnsslapd-pluginContainerScope:\s+(.*)', re.IGNORECASE) + re_logfile = re.compile(r'^referint-logfile:\s+(.*)', re.IGNORECASE) + + if 'referint' not in self.result: + self.result['referint'] = {} + + for line in proc.stdout.splitlines(): + line = line.strip() + if not line: + continue + + m = self.re_cn.match(line) + if m: + self.result['referint']['cn'] = m.group(1) + continue + + m = self.re_plugin_version.match(line) + if m: + self.result['referint']['plugin_version'] = m.group(1) + continue + + m = self.re_enabled.match(line) + if m: + self.result['referint']['enabled'] = self.mangle_value(m.group(1)) + continue + + m = re_membership_attr.match(line) + if m: + if 'membership_attr' not in self.result['referint']: + self.result['referint']['membership_attr'] = [] + self.result['referint']['membership_attr'].append(m.group(1)) + continue + + m = re_upate_delay.match(line) + if m: + self.result['referint']['update_delay'] = self.mangle_value(m.group(1)) + continue + + m = re_entry_scope.match(line) + if m: + self.result['referint']['entry_scope'] = m.group(1) + continue + + m = re_exclude_entry_scope.match(line) + if m: + self.result['referint']['exclude_entry_scope'] = m.group(1) + continue + + m = re_container_scope.match(line) + if m: + self.result['referint']['container_scope'] = m.group(1) + continue + + m = re_logfile.match(line) + if m: + self.result['referint']['logfile'] = m.group(1) + continue + + +# ############################################################################## +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 = Ds389PluginsInfo().run() + module.exit_json(changed=False, config=config) + + +# ############################################################################## +if __name__ == '__main__': + main() + +# ============================================================================= + +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list -- 2.39.5