From 022c8fe7f68af78e456af1bab454a57badcc4c88 Mon Sep 17 00:00:00 2001 From: Benjamin Renard Date: Thu, 14 Mar 2024 00:02:33 +0100 Subject: [PATCH] Introduce pre-commit hooks --- .pre-commit-config.yaml | 64 ++++++++ .woodpecker.yml | 2 +- README.md | 29 ++-- build.sh | 2 +- check_syncrepl_extended | 335 ++++++++++++++++++---------------------- debian/control | 4 +- icingaexchange.yml | 19 +-- 7 files changed, 241 insertions(+), 214 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..9bab40d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,64 @@ +# Pre-commit hooks to run tests and ensure code is cleaned. +# See https://pre-commit.com for more information +--- +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.6 + hooks: + - id: ruff + args: ["--fix"] + - repo: https://github.com/asottile/pyupgrade + rev: v3.15.0 + hooks: + - id: pyupgrade + args: ["--keep-percent-format", "--py37-plus"] + - repo: https://github.com/psf/black + rev: 23.11.0 + hooks: + - id: black + args: ["--target-version", "py37", "--line-length", "100"] + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort + args: ["--profile", "black", "--line-length", "100"] + - repo: https://github.com/PyCQA/flake8 + rev: 6.1.0 + hooks: + - id: flake8 + args: ["--max-line-length=100"] + - repo: https://github.com/codespell-project/codespell + rev: v2.2.2 + hooks: + - id: codespell + args: + - --ignore-words-list=exten + - --skip="./.*,*.csv,*.json,*.ini,*.subject,*.txt,*.html,*.log,*.conf" + - --quiet-level=2 + - --ignore-regex=.*codespell-ignore$ + # - --write-changes # Uncomment to write changes + exclude_types: [csv, json] + - repo: https://github.com/adrienverge/yamllint + rev: v1.32.0 + hooks: + - id: yamllint + ignore: .github/ + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.7.1 + hooks: + - id: prettier + args: ["--print-width", "100"] + - repo: local + hooks: + - id: pylint + name: pylint + entry: pylint + language: system + types: [python] + require_serial: true + - repo: https://github.com/PyCQA/bandit + rev: 1.7.5 + hooks: + - id: bandit + args: [--skip, "B101", --recursive] +minimum_pre_commit_version: 3.2.0 diff --git a/.woodpecker.yml b/.woodpecker.yml index 933e5ac..9b9213e 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -25,7 +25,7 @@ pipeline: commands: - echo "$GPG_KEY"|base64 -d|gpg --import - ./build.sh --quiet - secrets: [ maintainer_name, maintainer_email, gpg_key, debian_codename ] + secrets: [maintainer_name, maintainer_email, gpg_key, debian_codename] publish-dryrun: group: publish diff --git a/README.md b/README.md index ce5ac19..674385d 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,21 @@ # Script to check LDAP syncrepl replication state between two servers + This script check LDAP syncrepl replication state between two servers. One server is consider as provider and the other as consumer. This script can check replication state with two method : - - by the fisrt, entryCSN of all entries of LDAP directory will be compare between two servers - - by the second, all values of all atributes of all entries will be compare between two servers. -In all case, contextCSN of servers will be compare and entries not present in consumer or in provider will be notice. You can decide to disable contextCSN verification by using argument *--no-check-contextCSN*. +- by the first, entryCSN of all entries of LDAP directory will be compare between two servers +- by the second, all values of all attributes of all entries will be compare between two servers. -This script is also able to *"touch"* LDAP object on provider to force synchronisation of this object. This mechanism consist to add *'%%TOUCH%%'* value to an attribute of this object and remove it just after. The -touched attribute is specify by parameter *--touch*. Of course, couple of DN and password provided, must have write right on this attribute. +In all case, contextCSN of servers will be compare and entries not present in consumer or in provider will be notice. You can decide to disable contextCSN verification by using argument _--no-check-contextCSN_. -If your prefer, you can use *--replace-touch* parameter to replace value of touched attribute instead of adding the touched value. Use-ful in case of single-value attribute. +This script is also able to _"touch"_ LDAP object on provider to force synchronisation of this object. This mechanism consist to add _'%%TOUCH%%'_ value to an attribute of this object and remove it just after. The +touched attribute is specify by parameter _--touch_. Of course, couple of DN and password provided, must have write right on this attribute. -To use this script as an Icinga (or Nagios) plugin, use *-n* argument +If your prefer, you can use _--replace-touch_ parameter to replace value of touched attribute instead of adding the touched value. Use-ful in case of single-value attribute. + +To use this script as an Icinga (or Nagios) plugin, use _-n_ argument ## Requirement @@ -21,12 +23,13 @@ A single couple of DN and password able to connect to both server and without re ## Dependencies -* python 3 (for python 2.7 compatibility, see python2.7 branch) -* python-ldap +- python 3 (for python 2.7 compatibility, see python2.7 branch) +- python-ldap ## Installation ### If you plan to use it with NRPE + ``` apt install -y python3-ldap git git clone https://gitea.zionetrix.net/bn8/check_syncrepl_extended.git /usr/local/src/check_syncrepl_extended @@ -39,6 +42,7 @@ service nagios-nrpe-server reload ``` ### Otherwise + ``` apt install python3-ldap git git clone https://gitea.zionetrix.net/bn8/check_syncrepl_extended.git /usr/local/src/check_syncrepl_extended @@ -46,6 +50,7 @@ ln -s /usr/local/src/check_syncrepl_extended/check_syncrepl_extended /usr/local/ ``` ## Usage + ``` usage: check_syncrepl_extended [-h] [-v] [-p PROVIDER] [-c CONSUMER] [-i SERVERID] [-T] [-D DN] [-P PWD] [-b BASEDN] @@ -90,7 +95,7 @@ optional arguments: Don't check servers contextCSN (Default: False) -a, --attributes Check attributes values (Default: check only entryCSN) --exclude-attributes EXCL_ATTRS - Don't check this attribut (only in attribute check + Don't check this attribute (only in attribute check mode) --touch TOUCH Touch attribute giving in parameter to force resync a this LDAP object from provider. A value '%TOUCH%' will @@ -115,6 +120,6 @@ Copyright (c) 2017 Benjamin Renard This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation. -This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. diff --git a/build.sh b/build.sh index 35062ee..c91d897 100755 --- a/build.sh +++ b/build.sh @@ -24,7 +24,7 @@ sed -i "s/^VERSION *=.*$/VERSION = '$VERSION'/" $BDIR/check_syncrepl_extended if [ -z "$DEBIAN_CODENAME" ] then - echo "Retreive debian codename using lsb_release..." + echo "Retrieve debian codename using lsb_release..." DEBIAN_CODENAME=$( lsb_release -c -s ) else echo "Use debian codename from environment ($DEBIAN_CODENAME)" diff --git a/check_syncrepl_extended b/check_syncrepl_extended index ca357b3..4c729fc 100755 --- a/check_syncrepl_extended +++ b/check_syncrepl_extended @@ -5,9 +5,9 @@ # One server is consider as provider and the other as consumer. # # This script can check replication state with two method : -# - by the fisrt, entryCSN of all entries of LDAP directory will be +# - by the first, entryCSN of all entries of LDAP directory will be # compare between two servers -# - by the second, all values of all atributes of all entries will +# - by the second, all values of all attributes of all entries will # be compare between two servers. # # In all case, contextCSN of servers will be compare and entries not @@ -35,47 +35,48 @@ # import argparse +import getpass import logging import sys -import getpass - import ldap from ldap import LDAPError # pylint: disable=no-name-in-module -from ldap.controls import SimplePagedResultsControl from ldap import modlist +from ldap.controls import SimplePagedResultsControl -VERSION = '0.0' -TOUCH_VALUE = b'%%TOUCH%%' +VERSION = "0.0" +TOUCH_VALUE = b"%%TOUCH%%" parser = argparse.ArgumentParser( - description=( - "Script to check LDAP syncrepl replication state between " - "two servers."), + description=("Script to check LDAP syncrepl replication state between two servers."), epilog=( - 'Author: Benjamin Renard , ' - f'Version: {VERSION}, ' - 'Source: https://gitea.zionetrix.net/bn8/check_syncrepl_extended') + "Author: Benjamin Renard , " + f"Version: {VERSION}, " + "Source: https://gitea.zionetrix.net/bn8/check_syncrepl_extended" + ), ) parser.add_argument( - "-p", "--provider", + "-p", + "--provider", dest="provider", action="store", type=str, - help="LDAP provider URI (example: ldaps://ldapmaster.foo:636)" + help="LDAP provider URI (example: ldaps://ldapmaster.foo:636)", ) parser.add_argument( - "-c", "--consumer", + "-c", + "--consumer", dest="consumer", action="store", type=str, - help="LDAP consumer URI (example: ldaps://ldapslave.foo:636)" + help="LDAP consumer URI (example: ldaps://ldapslave.foo:636)", ) parser.add_argument( - "-i", "--serverID", + "-i", + "--serverID", dest="serverid", action="store", type=int, @@ -84,74 +85,67 @@ parser.add_argument( "setups where each master has a unique ID and a contextCSN for " "each replicated master exists. A valid serverID is a integer " "value from 0 to 4095 (limited to 3 hex digits, example: '12' " - "compares the contextCSN matching '#00C#')"), - default=False + "compares the contextCSN matching '#00C#')" + ), + default=False, ) parser.add_argument( - "-T", "--starttls", + "-T", + "--starttls", dest="starttls", action="store_true", help="Start TLS on LDAP provider/consumers connections", - default=False + default=False, ) parser.add_argument( - "-D", "--dn", + "-D", + "--dn", dest="dn", action="store", type=str, - help="LDAP bind DN (example: uid=nagios,ou=sysaccounts,o=example" + help="LDAP bind DN (example: uid=nagios,ou=sysaccounts,o=example", ) parser.add_argument( - "-P", "--pwd", - dest="pwd", - action="store", - type=str, - help="LDAP bind password", - default=None + "-P", "--pwd", dest="pwd", action="store", type=str, help="LDAP bind password", default=None ) parser.add_argument( - "-b", "--basedn", + "-b", + "--basedn", dest="basedn", action="store", type=str, - help="LDAP base DN (example: o=example)" + help="LDAP base DN (example: o=example)", ) parser.add_argument( - "-f", "--filter", + "-f", + "--filter", dest="filterstr", action="store", type=str, help="LDAP filter (default: (objectClass=*))", - default='(objectClass=*)' + default="(objectClass=*)", ) parser.add_argument( - "-d", "--debug", - dest="debug", - action="store_true", - help="Debug mode", - default=False + "-d", "--debug", dest="debug", action="store_true", help="Debug mode", default=False ) parser.add_argument( - "-n", "--nagios", + "-n", + "--nagios", dest="nagios", action="store_true", help="Nagios check plugin mode", - default=False + default=False, ) parser.add_argument( - "-q", "--quiet", - dest="quiet", - action="store_true", - help="Quiet mode", - default=False + "-q", "--quiet", dest="quiet", action="store_true", help="Quiet mode", default=False ) parser.add_argument( @@ -159,7 +153,7 @@ parser.add_argument( dest="nocheckcert", action="store_true", help="Don't check the server certificate (Default: False)", - default=False + default=False, ) parser.add_argument( @@ -167,25 +161,24 @@ parser.add_argument( dest="nocheckcontextcsn", action="store_true", help="Don't check servers contextCSN (Default: False)", - default=False + default=False, ) parser.add_argument( "--only-check-contextCSN", dest="onlycheckcontextcsn", action="store_true", - help=( - "Only check servers root contextCSN (objects check disabled, " - "default : False)"), - default=False + help=("Only check servers root contextCSN (objects check disabled, default : False)"), + default=False, ) parser.add_argument( - "-a", "--attributes", + "-a", + "--attributes", dest="attrs", action="store_true", help="Check attributes values (Default: check only entryCSN)", - default=False + default=False, ) parser.add_argument( @@ -193,8 +186,8 @@ parser.add_argument( dest="excl_attrs", action="store", type=str, - help="Don't check this attribut (only in attribute check mode)", - default=None + help="Don't check this attribute (only in attribute check mode)", + default=None, ) parser.add_argument( @@ -203,13 +196,13 @@ parser.add_argument( action="store", type=str, help=( - 'Touch attribute giving in parameter to force resync a this LDAP ' + "Touch attribute giving in parameter to force resync a this LDAP " f'object from provider. A value "{TOUCH_VALUE.decode()}" will be ' - 'add to this attribute and remove after. The user use to connect ' - 'to the LDAP directory must have write permission on this ' - 'attribute on each object.' + "add to this attribute and remove after. The user use to connect " + "to the LDAP directory must have write permission on this " + "attribute on each object." ), - default=None + default=None, ) parser.add_argument( @@ -217,7 +210,7 @@ parser.add_argument( dest="replacetouch", action="store_true", help="In touch mode, replace value instead of adding.", - default=False + default=False, ) parser.add_argument( @@ -225,7 +218,7 @@ parser.add_argument( dest="removetouchvalue", action="store_true", help="In touch mode, remove touch value if present.", - default=False + default=False, ) parser.add_argument( @@ -233,10 +226,8 @@ parser.add_argument( dest="page_size", action="store", type=int, - help=( - "Page size: if defined, paging control using LDAP v3 extended " - "control will be enabled."), - default=None + help=("Page size: if defined, paging control using LDAP v3 extended control will be enabled."), + default=None, ) options = parser.parse_args() @@ -244,7 +235,8 @@ options = parser.parse_args() if options.nocheckcontextcsn and options.onlycheckcontextcsn: parser.error( "You can't use both --no-check-contextCSN and " - "--only-check-contextCSN parameters and the same time") + "--only-check-contextCSN parameters and the same time" + ) if options.nagios: sys.exit(3) sys.exit(1) @@ -264,14 +256,14 @@ if not options.basedn: if not 0 <= options.serverid <= 4095: parser.error( - "ServerID should be a integer value from 0 to 4095 " - "(limited to 3 hexadecimal digits).") + "ServerID should be a integer value from 0 to 4095 (limited to 3 hexadecimal digits)." + ) if options.nagios: sys.exit(3) sys.exit(1) if options.touch and not options.attrs: - logging.info('Force option attrs on touch mode') + logging.info("Force option attrs on touch mode") options.attrs = True if options.dn and options.pwd is None: @@ -279,7 +271,7 @@ if options.dn and options.pwd is None: excl_attrs = [] if options.excl_attrs: - for ex in options.excl_attrs.split(','): + for ex in options.excl_attrs.split(","): excl_attrs.append(ex.strip()) FORMAT = "%(asctime)s - %(levelname)s: %(message)s" @@ -296,10 +288,9 @@ else: class LdapServer: - - uri = "" - dn = "" - pwd = "" + uri = None + dn = None + pwd = None start_tls = False con = 0 @@ -330,21 +321,20 @@ class LdapServer: def getContextCSN(self, basedn=False, serverid=False): if not basedn: basedn = self.dn - data = self.search( - basedn, '(objectclass=*)', attrs=['contextCSN'], scope='base') + data = self.search(basedn, "(objectclass=*)", attrs=["contextCSN"], scope="base") if data: - contextCSNs = data[0][0][1]['contextCSN'] - logging.debug('Found contextCSNs %s', contextCSNs) + contextCSNs = data[0][0][1]["contextCSN"] + logging.debug("Found contextCSNs %s", contextCSNs) if serverid is False: return contextCSNs[0] - csnid = str(format(serverid, 'X')).zfill(3) - sub = str.encode(f'#{csnid}#', encoding="ascii", errors="replace") + csnid = str(format(serverid, "X")).zfill(3) + sub = str.encode(f"#{csnid}#", encoding="ascii", errors="replace") CSN = [s for s in contextCSNs if sub in s] if not CSN: logging.error( - "No contextCSN matching with ServerID %s (=%s) could be " - "found.", - serverid, sub + "No contextCSN matching with ServerID %s (=%s) could be found.", + serverid, + sub, ) return False return CSN[0] @@ -352,21 +342,19 @@ class LdapServer: @staticmethod def get_scope(scope): - if scope == 'base': + if scope == "base": return ldap.SCOPE_BASE # pylint: disable=no-member - if scope == 'one': + if scope == "one": return ldap.SCOPE_ONELEVEL # pylint: disable=no-member - if scope == 'sub': + if scope == "sub": return ldap.SCOPE_SUBTREE # pylint: disable=no-member - raise Exception(f'Unknown LDAP scope "{scope}"') + raise Exception(f'Unknown LDAP scope "{scope}"') # pylint: disable=broad-exception-raised def search(self, basedn, filterstr, attrs=None, scope=None): if self.page_size: - return self.paged_search( - basedn, filterstr, attrs=attrs, scope=scope) + return self.paged_search(basedn, filterstr, attrs=attrs, scope=scope) res_id = self.con.search( - basedn, self.get_scope(scope if scope else 'sub'), - filterstr, attrs if attrs else [] + basedn, self.get_scope(scope if scope else "sub"), filterstr, attrs if attrs else [] ) ret = [] while 1: @@ -380,13 +368,16 @@ class LdapServer: def paged_search(self, basedn, filterstr, attrs=None, scope=None): ret = [] page = 0 - pg_ctrl = SimplePagedResultsControl(True, self.page_size, '') + pg_ctrl = SimplePagedResultsControl(True, self.page_size, "") while page == 0 or pg_ctrl.cookie: page += 1 - logging.debug('Page search: loading page %d', page) + logging.debug("Page search: loading page %d", page) res_id = self.con.search_ext( - basedn, self.get_scope(scope if scope else 'sub'), - filterstr, attrs if attrs else [], serverctrls=[pg_ctrl] + basedn, + self.get_scope(scope if scope else "sub"), + filterstr, + attrs if attrs else [], + serverctrls=[pg_ctrl], ) # pylint: disable=unused-variable res_type, res_data, res_id, serverctrls = self.con.result3(res_id) @@ -403,11 +394,11 @@ class LdapServer: if not ldif: return True try: - logging.debug('Update object %s: %s', dn, ldif) + logging.debug("Update object %s: %s", dn, ldif) self.con.modify_s(dn, ldif) return True except LDAPError: - logging.error('Error updating object %s', dn, exc_info=True) + logging.error("Error updating object %s", dn, exc_info=True) return False @staticmethod @@ -432,14 +423,9 @@ class LdapServer: else: new[attr].append(TOUCH_VALUE) try: - logging.info( - 'Touch object "%s" on attribute "%s": %s => %s', - dn, attr, old, new - ) + logging.info('Touch object "%s" on attribute "%s": %s => %s', dn, attr, old, new) if self.update_object(dn, old, new): - logging.info( - 'Restore original value of attribute "%s" of object "%s"', - attr, dn) + logging.info('Restore original value of attribute "%s" of object "%s"', attr, dn) if options.removetouchvalue and TOUCH_VALUE in old[attr]: old[attr].remove(TOUCH_VALUE) self.update_object(dn=dn, old=new, new=old) @@ -451,8 +437,7 @@ class LdapServer: if options.nocheckcert: # pylint: disable=no-member - ldap.set_option( - ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) + ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) servers = [options.provider, options.consumer] @@ -461,44 +446,36 @@ LdapObjects = {} LdapServersCSN = {} for srv in servers: - logging.info('Connect to %s', srv) - LdapServers[srv] = LdapServer(srv, options.dn, options.pwd, - options.starttls, - page_size=options.page_size) + logging.info("Connect to %s", srv) + LdapServers[srv] = LdapServer( + srv, options.dn, options.pwd, options.starttls, page_size=options.page_size + ) if not LdapServers[srv].connect(): if options.nagios: - print(f'UNKWNON - Failed to connect to {srv}') + print(f"UNKWNON - Failed to connect to {srv}") sys.exit(3) else: sys.exit(1) if not options.nocheckcontextcsn: - LdapServersCSN[srv] = LdapServers[srv].getContextCSN( - options.basedn, options.serverid) - logging.info('ContextCSN of %s: %s', srv, LdapServersCSN[srv]) + LdapServersCSN[srv] = LdapServers[srv].getContextCSN(options.basedn, options.serverid) + logging.info("ContextCSN of %s: %s", srv, LdapServersCSN[srv]) if not options.onlycheckcontextcsn: - logging.info('List objects from %s', srv) + logging.info("List objects from %s", srv) LdapObjects[srv] = {} if options.attrs: - for obj in LdapServers[srv].search( - options.basedn, options.filterstr, [] - ): - logging.debug('Found on %s: %s', srv, obj[0][0]) + for obj in LdapServers[srv].search(options.basedn, options.filterstr, []): + logging.debug("Found on %s: %s", srv, obj[0][0]) LdapObjects[srv][obj[0][0]] = obj[0][1] else: - for obj in LdapServers[srv].search( - options.basedn, options.filterstr, ['entryCSN'] - ): - logging.debug( - 'Found on %s: %s / %s', - srv, obj[0][0], obj[0][1]['entryCSN'][0] - ) - LdapObjects[srv][obj[0][0]] = obj[0][1]['entryCSN'][0] + for obj in LdapServers[srv].search(options.basedn, options.filterstr, ["entryCSN"]): + logging.debug("Found on %s: %s / %s", srv, obj[0][0], obj[0][1]["entryCSN"][0]) + LdapObjects[srv][obj[0][0]] = obj[0][1]["entryCSN"][0] - logging.info('%s objects founds', len(LdapObjects[srv])) + logging.info("%s objects founds", len(LdapObjects[srv])) if not options.onlycheckcontextcsn: @@ -510,14 +487,11 @@ if not options.onlycheckcontextcsn: not_sync[srv] = [] if options.attrs: - logging.info( - "Check if objects a are synchronized (by comparing attributes's " - "values)") + logging.info("Check if objects a are synchronized (by comparing attributes's values)") else: - logging.info( - 'Check if objets are synchronized (by comparing entryCSN)') + logging.info("Check if objects are synchronized (by comparing entryCSN)") for obj in LdapObjects[options.provider]: - logging.debug('Check obj %s', obj) + logging.debug("Check obj %s", obj) for srv_name, srv in LdapObjects.items(): if srv_name == options.provider: continue @@ -533,7 +507,9 @@ if not options.onlycheckcontextcsn: attrs_list.append(attr) logging.debug( "Obj %s not synchronized: %s not present on %s", - obj, ','.join(attrs_list), srv_name + obj, + ",".join(attrs_list), + srv_name, ) touch = True else: @@ -543,7 +519,8 @@ if not options.onlycheckcontextcsn: attrs_list.append(attr) logging.debug( "Obj %s not synchronized: %s not same value(s)", - obj, ','.join(attrs_list) + obj, + ",".join(attrs_list), ) touch = True if attrs_list: @@ -551,29 +528,29 @@ if not options.onlycheckcontextcsn: else: logging.debug( "Obj %s not synchronized: %s <-> %s", - obj, LdapObjects[options.provider][obj], srv[obj] + obj, + LdapObjects[options.provider][obj], + srv[obj], ) not_sync[srv_name].append(obj) if touch and options.touch: orig_value = [] if options.touch in LdapObjects[options.provider][obj]: orig_value = LdapObjects[options.provider][obj][options.touch] - LdapServers[options.provider].touch_object( - obj, options.touch, orig_value) + LdapServers[options.provider].touch_object(obj, options.touch, orig_value) else: - logging.debug('Obj %s: not found on %s', obj, srv_name) + logging.debug("Obj %s: not found on %s", obj, srv_name) not_found[srv_name].append(obj) if options.touch: orig_value = [] if options.touch in LdapObjects[options.provider][obj]: orig_value = LdapObjects[options.provider][obj][options.touch] - LdapServers[options.provider].touch_object( - obj, options.touch, orig_value) + LdapServers[options.provider].touch_object(obj, options.touch, orig_value) for obj in LdapObjects[options.consumer]: - logging.debug('Check obj %s of consumer', obj) + logging.debug("Check obj %s of consumer", obj) if obj not in LdapObjects[options.provider]: - logging.debug('Obj %s: not found on provider', obj) + logging.debug("Obj %s: not found on provider", obj) not_found[options.provider].append(obj) if options.nagios: @@ -582,91 +559,75 @@ if options.nagios: if not options.nocheckcontextcsn: if not LdapServersCSN[options.provider]: - errors.append('ContextCSN of LDAP server provider could not be found') + errors.append("ContextCSN of LDAP server provider could not be found") else: long_output.append( - f'ContextCSN on LDAP server provider = {LdapServersCSN[options.provider]}') + f"ContextCSN on LDAP server provider = {LdapServersCSN[options.provider]}" + ) for srv_name, srv_csn in LdapServersCSN.items(): if srv_name == options.provider: continue if not srv_csn: - errors.append(f'ContextCSN of {srv_name} not found') + errors.append(f"ContextCSN of {srv_name} not found") elif srv_csn != LdapServersCSN[options.provider]: - errors.append( - f'ContextCSN of {srv_name} not the same of provider') - long_output.append( - f'ContextCSN on LDAP server {srv_name} = {srv_csn}') + errors.append(f"ContextCSN of {srv_name} not the same of provider") + long_output.append(f"ContextCSN on LDAP server {srv_name} = {srv_csn}") if not options.onlycheckcontextcsn: if not_found[options.consumer]: - errors.append( - f'{len(not_found[options.consumer])} not found object(s) on ' - 'consumer') - long_output.append( - f'Object(s) not found on server {options.consumer} ' - '(consumer):') + errors.append(f"{len(not_found[options.consumer])} not found object(s) on consumer") + long_output.append(f"Object(s) not found on server {options.consumer} (consumer):") for obj in not_found[options.consumer]: - long_output.append(f' - {obj}') + long_output.append(f" - {obj}") if not_found[options.provider]: - errors.append( - f'{len(not_found[options.provider])} not found object(s) on ' - 'provider') - long_output.append( - f'Object(s) not found on server {options.provider} ' - '(provider):') + errors.append(f"{len(not_found[options.provider])} not found object(s) on provider") + long_output.append(f"Object(s) not found on server {options.provider} (provider):") for obj in not_found[options.provider]: - long_output.append(f' - {obj}') + long_output.append(f" - {obj}") if not_sync[options.consumer]: errors.append( - f'{len(not_sync[options.consumer])} not synchronized object(s) ' - 'on consumer') + f"{len(not_sync[options.consumer])} not synchronized object(s) on consumer" + ) long_output.append( - f'Object(s) not synchronized on server {options.consumer} ' - '(consumer):') + f"Object(s) not synchronized on server {options.consumer} (consumer):" + ) for obj in not_sync[options.consumer]: - long_output.append(f' - {obj}') + long_output.append(f" - {obj}") if errors: print(f'CRITICAL: {", ".join(errors)}') - print('\n\n') + print("\n\n") print("\n".join(long_output)) sys.exit(2) else: - print('OK: consumer and provider are synchronized') + print("OK: consumer and provider are synchronized") sys.exit(0) else: noerror = True for srv in servers: if not options.nocheckcontextcsn: if not LdapServersCSN[options.provider]: - logging.warning( - 'ContextCSN of LDAP server provider could not be found') + logging.warning("ContextCSN of LDAP server provider could not be found") noerror = False else: for srv_name, srv_csn in LdapServersCSN.items(): if srv_name == options.provider: continue if not srv_csn: - logging.warning('ContextCSN of %s not found', srv_name) + logging.warning("ContextCSN of %s not found", srv_name) noerror = False elif srv_csn != LdapServersCSN[options.provider]: - logging.warning( - 'ContextCSN of %s not the same of provider', - srv_name) + logging.warning("ContextCSN of %s not the same of provider", srv_name) noerror = False if not options.onlycheckcontextcsn: if not_found[srv]: logging.warning( - 'Not found objects on %s :\n - %s', - srv, '\n - '.join(not_found[srv]) + "Not found objects on %s :\n - %s", srv, "\n - ".join(not_found[srv]) ) noerror = False if not_sync[srv]: - logging.warning( - 'Not sync objects on %s: %s', - srv, '\n - '.join(not_sync[srv]) - ) + logging.warning("Not sync objects on %s: %s", srv, "\n - ".join(not_sync[srv])) noerror = False if noerror: - logging.info('No sync problem detected') + logging.info("No sync problem detected") diff --git a/debian/control b/debian/control index 1948328..07bc104 100644 --- a/debian/control +++ b/debian/control @@ -12,9 +12,9 @@ Description: Check LDAP syncrepl replication state between two servers This script check LDAP syncrepl replication state between two servers. One server is consider as provider and the other as consumer. This script can check replication state with two method : - - by the fisrt, entryCSN of all entries of LDAP directory will be compare + - by the first, entryCSN of all entries of LDAP directory will be compare between two servers - - by the second, all values of all atributes of all entries will be compare + - by the second, all values of all attributes of all entries will be compare between two servers. In all case, contextCSN of servers will be compare and entries not present in consumer or in provider will be notice. You can decide to disable contextCSN diff --git a/icingaexchange.yml b/icingaexchange.yml index cfb6a49..7ece069 100644 --- a/icingaexchange.yml +++ b/icingaexchange.yml @@ -1,3 +1,4 @@ +--- name: check_syncrepl_extended description: "file:///README.md" url: "https://gitea.zionetrix.net/bn8/check_syncrepl_extended" @@ -6,22 +7,18 @@ vendor: OpenLDAP target: Database type: Plugin license: gplv2 -releases: - - - name: v2017.09.11 +releases: + - name: v2017.09.11 description: "v2017.09.11 Release" - files: - - - name: check_syncrepl_extended + files: + - name: check_syncrepl_extended url: "file:///check_syncrepl_extended" description: "First release upload on Icinga Exchange" checksum: cee2e8fdc243edcbab30d3b034e8b9bf - - - name: v2017.09.12 + - name: v2017.09.12 description: "v2017.09.12 Release" - files: - - - name: check_syncrepl_extended + files: + - name: check_syncrepl_extended url: "file:///check_syncrepl_extended" description: "Improve ContextCSN checking (thanks Orhan)" checksum: a954ca49855c23f2f098f4696c4c0ebd