--- /dev/null
+#!/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 <frank.brehm@pixelpark.com>
+# 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 <frank.brehm@pixelpark.com>"
+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