Introduce pre-commit hooks
This commit is contained in:
parent
f3f8ae9f1a
commit
022c8fe7f6
7 changed files with 241 additions and 214 deletions
64
.pre-commit-config.yaml
Normal file
64
.pre-commit-config.yaml
Normal file
|
@ -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
|
25
README.md
25
README.md
|
@ -1,19 +1,21 @@
|
||||||
# Script to check LDAP syncrepl replication state between two servers
|
# Script to check LDAP syncrepl replication state between two servers
|
||||||
|
|
||||||
This script 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.
|
One server is consider as provider and the other as consumer.
|
||||||
|
|
||||||
This script can check replication state with two method :
|
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
|
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_.
|
||||||
touched attribute is specify by parameter *--touch*. Of course, couple of DN and password provided, must have write right on this attribute.
|
|
||||||
|
|
||||||
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
|
## Requirement
|
||||||
|
|
||||||
|
@ -21,12 +23,13 @@ A single couple of DN and password able to connect to both server and without re
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
* python 3 (for python 2.7 compatibility, see python2.7 branch)
|
- python 3 (for python 2.7 compatibility, see python2.7 branch)
|
||||||
* python-ldap
|
- python-ldap
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### If you plan to use it with NRPE
|
### If you plan to use it with NRPE
|
||||||
|
|
||||||
```
|
```
|
||||||
apt install -y python3-ldap git
|
apt install -y python3-ldap git
|
||||||
git clone https://gitea.zionetrix.net/bn8/check_syncrepl_extended.git /usr/local/src/check_syncrepl_extended
|
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
|
### Otherwise
|
||||||
|
|
||||||
```
|
```
|
||||||
apt install python3-ldap git
|
apt install python3-ldap git
|
||||||
git clone https://gitea.zionetrix.net/bn8/check_syncrepl_extended.git /usr/local/src/check_syncrepl_extended
|
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
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: check_syncrepl_extended [-h] [-v] [-p PROVIDER] [-c CONSUMER]
|
usage: check_syncrepl_extended [-h] [-v] [-p PROVIDER] [-c CONSUMER]
|
||||||
[-i SERVERID] [-T] [-D DN] [-P PWD] [-b BASEDN]
|
[-i SERVERID] [-T] [-D DN] [-P PWD] [-b BASEDN]
|
||||||
|
@ -90,7 +95,7 @@ optional arguments:
|
||||||
Don't check servers contextCSN (Default: False)
|
Don't check servers contextCSN (Default: False)
|
||||||
-a, --attributes Check attributes values (Default: check only entryCSN)
|
-a, --attributes Check attributes values (Default: check only entryCSN)
|
||||||
--exclude-attributes EXCL_ATTRS
|
--exclude-attributes EXCL_ATTRS
|
||||||
Don't check this attribut (only in attribute check
|
Don't check this attribute (only in attribute check
|
||||||
mode)
|
mode)
|
||||||
--touch TOUCH Touch attribute giving in parameter to force resync a
|
--touch TOUCH Touch attribute giving in parameter to force resync a
|
||||||
this LDAP object from provider. A value '%TOUCH%' will
|
this LDAP object from provider. A value '%TOUCH%' will
|
||||||
|
|
2
build.sh
2
build.sh
|
@ -24,7 +24,7 @@ sed -i "s/^VERSION *=.*$/VERSION = '$VERSION'/" $BDIR/check_syncrepl_extended
|
||||||
|
|
||||||
if [ -z "$DEBIAN_CODENAME" ]
|
if [ -z "$DEBIAN_CODENAME" ]
|
||||||
then
|
then
|
||||||
echo "Retreive debian codename using lsb_release..."
|
echo "Retrieve debian codename using lsb_release..."
|
||||||
DEBIAN_CODENAME=$( lsb_release -c -s )
|
DEBIAN_CODENAME=$( lsb_release -c -s )
|
||||||
else
|
else
|
||||||
echo "Use debian codename from environment ($DEBIAN_CODENAME)"
|
echo "Use debian codename from environment ($DEBIAN_CODENAME)"
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
# One server is consider as provider and the other as consumer.
|
# One server is consider as provider and the other as consumer.
|
||||||
#
|
#
|
||||||
# This script can check replication state with two method :
|
# 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
|
# 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.
|
# be compare between two servers.
|
||||||
#
|
#
|
||||||
# In all case, contextCSN of servers will be compare and entries not
|
# In all case, contextCSN of servers will be compare and entries not
|
||||||
|
@ -35,47 +35,48 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import getpass
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import getpass
|
|
||||||
|
|
||||||
import ldap
|
import ldap
|
||||||
from ldap import LDAPError # pylint: disable=no-name-in-module
|
from ldap import LDAPError # pylint: disable=no-name-in-module
|
||||||
from ldap.controls import SimplePagedResultsControl
|
|
||||||
from ldap import modlist
|
from ldap import modlist
|
||||||
|
from ldap.controls import SimplePagedResultsControl
|
||||||
|
|
||||||
VERSION = '0.0'
|
VERSION = "0.0"
|
||||||
TOUCH_VALUE = b'%%TOUCH%%'
|
TOUCH_VALUE = b"%%TOUCH%%"
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description=(
|
description=("Script to check LDAP syncrepl replication state between two servers."),
|
||||||
"Script to check LDAP syncrepl replication state between "
|
|
||||||
"two servers."),
|
|
||||||
epilog=(
|
epilog=(
|
||||||
'Author: Benjamin Renard <brenard@easter-eggs.com>, '
|
"Author: Benjamin Renard <brenard@easter-eggs.com>, "
|
||||||
f'Version: {VERSION}, '
|
f"Version: {VERSION}, "
|
||||||
'Source: https://gitea.zionetrix.net/bn8/check_syncrepl_extended')
|
"Source: https://gitea.zionetrix.net/bn8/check_syncrepl_extended"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-p", "--provider",
|
"-p",
|
||||||
|
"--provider",
|
||||||
dest="provider",
|
dest="provider",
|
||||||
action="store",
|
action="store",
|
||||||
type=str,
|
type=str,
|
||||||
help="LDAP provider URI (example: ldaps://ldapmaster.foo:636)"
|
help="LDAP provider URI (example: ldaps://ldapmaster.foo:636)",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-c", "--consumer",
|
"-c",
|
||||||
|
"--consumer",
|
||||||
dest="consumer",
|
dest="consumer",
|
||||||
action="store",
|
action="store",
|
||||||
type=str,
|
type=str,
|
||||||
help="LDAP consumer URI (example: ldaps://ldapslave.foo:636)"
|
help="LDAP consumer URI (example: ldaps://ldapslave.foo:636)",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-i", "--serverID",
|
"-i",
|
||||||
|
"--serverID",
|
||||||
dest="serverid",
|
dest="serverid",
|
||||||
action="store",
|
action="store",
|
||||||
type=int,
|
type=int,
|
||||||
|
@ -84,74 +85,67 @@ parser.add_argument(
|
||||||
"setups where each master has a unique ID and a contextCSN for "
|
"setups where each master has a unique ID and a contextCSN for "
|
||||||
"each replicated master exists. A valid serverID is a integer "
|
"each replicated master exists. A valid serverID is a integer "
|
||||||
"value from 0 to 4095 (limited to 3 hex digits, example: '12' "
|
"value from 0 to 4095 (limited to 3 hex digits, example: '12' "
|
||||||
"compares the contextCSN matching '#00C#')"),
|
"compares the contextCSN matching '#00C#')"
|
||||||
default=False
|
),
|
||||||
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-T", "--starttls",
|
"-T",
|
||||||
|
"--starttls",
|
||||||
dest="starttls",
|
dest="starttls",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Start TLS on LDAP provider/consumers connections",
|
help="Start TLS on LDAP provider/consumers connections",
|
||||||
default=False
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-D", "--dn",
|
"-D",
|
||||||
|
"--dn",
|
||||||
dest="dn",
|
dest="dn",
|
||||||
action="store",
|
action="store",
|
||||||
type=str,
|
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(
|
parser.add_argument(
|
||||||
"-P", "--pwd",
|
"-P", "--pwd", dest="pwd", action="store", type=str, help="LDAP bind password", default=None
|
||||||
dest="pwd",
|
|
||||||
action="store",
|
|
||||||
type=str,
|
|
||||||
help="LDAP bind password",
|
|
||||||
default=None
|
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-b", "--basedn",
|
"-b",
|
||||||
|
"--basedn",
|
||||||
dest="basedn",
|
dest="basedn",
|
||||||
action="store",
|
action="store",
|
||||||
type=str,
|
type=str,
|
||||||
help="LDAP base DN (example: o=example)"
|
help="LDAP base DN (example: o=example)",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-f", "--filter",
|
"-f",
|
||||||
|
"--filter",
|
||||||
dest="filterstr",
|
dest="filterstr",
|
||||||
action="store",
|
action="store",
|
||||||
type=str,
|
type=str,
|
||||||
help="LDAP filter (default: (objectClass=*))",
|
help="LDAP filter (default: (objectClass=*))",
|
||||||
default='(objectClass=*)'
|
default="(objectClass=*)",
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-d", "--debug",
|
"-d", "--debug", dest="debug", action="store_true", help="Debug mode", default=False
|
||||||
dest="debug",
|
|
||||||
action="store_true",
|
|
||||||
help="Debug mode",
|
|
||||||
default=False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-n", "--nagios",
|
"-n",
|
||||||
|
"--nagios",
|
||||||
dest="nagios",
|
dest="nagios",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Nagios check plugin mode",
|
help="Nagios check plugin mode",
|
||||||
default=False
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-q", "--quiet",
|
"-q", "--quiet", dest="quiet", action="store_true", help="Quiet mode", default=False
|
||||||
dest="quiet",
|
|
||||||
action="store_true",
|
|
||||||
help="Quiet mode",
|
|
||||||
default=False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -159,7 +153,7 @@ parser.add_argument(
|
||||||
dest="nocheckcert",
|
dest="nocheckcert",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Don't check the server certificate (Default: False)",
|
help="Don't check the server certificate (Default: False)",
|
||||||
default=False
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -167,25 +161,24 @@ parser.add_argument(
|
||||||
dest="nocheckcontextcsn",
|
dest="nocheckcontextcsn",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Don't check servers contextCSN (Default: False)",
|
help="Don't check servers contextCSN (Default: False)",
|
||||||
default=False
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--only-check-contextCSN",
|
"--only-check-contextCSN",
|
||||||
dest="onlycheckcontextcsn",
|
dest="onlycheckcontextcsn",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help=(
|
help=("Only check servers root contextCSN (objects check disabled, default : False)"),
|
||||||
"Only check servers root contextCSN (objects check disabled, "
|
default=False,
|
||||||
"default : False)"),
|
|
||||||
default=False
|
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-a", "--attributes",
|
"-a",
|
||||||
|
"--attributes",
|
||||||
dest="attrs",
|
dest="attrs",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Check attributes values (Default: check only entryCSN)",
|
help="Check attributes values (Default: check only entryCSN)",
|
||||||
default=False
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -193,8 +186,8 @@ parser.add_argument(
|
||||||
dest="excl_attrs",
|
dest="excl_attrs",
|
||||||
action="store",
|
action="store",
|
||||||
type=str,
|
type=str,
|
||||||
help="Don't check this attribut (only in attribute check mode)",
|
help="Don't check this attribute (only in attribute check mode)",
|
||||||
default=None
|
default=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -203,13 +196,13 @@ parser.add_argument(
|
||||||
action="store",
|
action="store",
|
||||||
type=str,
|
type=str,
|
||||||
help=(
|
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 '
|
f'object from provider. A value "{TOUCH_VALUE.decode()}" will be '
|
||||||
'add to this attribute and remove after. The user use to connect '
|
"add to this attribute and remove after. The user use to connect "
|
||||||
'to the LDAP directory must have write permission on this '
|
"to the LDAP directory must have write permission on this "
|
||||||
'attribute on each object.'
|
"attribute on each object."
|
||||||
),
|
),
|
||||||
default=None
|
default=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -217,7 +210,7 @@ parser.add_argument(
|
||||||
dest="replacetouch",
|
dest="replacetouch",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="In touch mode, replace value instead of adding.",
|
help="In touch mode, replace value instead of adding.",
|
||||||
default=False
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -225,7 +218,7 @@ parser.add_argument(
|
||||||
dest="removetouchvalue",
|
dest="removetouchvalue",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="In touch mode, remove touch value if present.",
|
help="In touch mode, remove touch value if present.",
|
||||||
default=False
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
@ -233,10 +226,8 @@ parser.add_argument(
|
||||||
dest="page_size",
|
dest="page_size",
|
||||||
action="store",
|
action="store",
|
||||||
type=int,
|
type=int,
|
||||||
help=(
|
help=("Page size: if defined, paging control using LDAP v3 extended control will be enabled."),
|
||||||
"Page size: if defined, paging control using LDAP v3 extended "
|
default=None,
|
||||||
"control will be enabled."),
|
|
||||||
default=None
|
|
||||||
)
|
)
|
||||||
|
|
||||||
options = parser.parse_args()
|
options = parser.parse_args()
|
||||||
|
@ -244,7 +235,8 @@ options = parser.parse_args()
|
||||||
if options.nocheckcontextcsn and options.onlycheckcontextcsn:
|
if options.nocheckcontextcsn and options.onlycheckcontextcsn:
|
||||||
parser.error(
|
parser.error(
|
||||||
"You can't use both --no-check-contextCSN and "
|
"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:
|
if options.nagios:
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -264,14 +256,14 @@ if not options.basedn:
|
||||||
|
|
||||||
if not 0 <= options.serverid <= 4095:
|
if not 0 <= options.serverid <= 4095:
|
||||||
parser.error(
|
parser.error(
|
||||||
"ServerID should be a integer value from 0 to 4095 "
|
"ServerID should be a integer value from 0 to 4095 (limited to 3 hexadecimal digits)."
|
||||||
"(limited to 3 hexadecimal digits).")
|
)
|
||||||
if options.nagios:
|
if options.nagios:
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if options.touch and not options.attrs:
|
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
|
options.attrs = True
|
||||||
|
|
||||||
if options.dn and options.pwd is None:
|
if options.dn and options.pwd is None:
|
||||||
|
@ -279,7 +271,7 @@ if options.dn and options.pwd is None:
|
||||||
|
|
||||||
excl_attrs = []
|
excl_attrs = []
|
||||||
if options.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())
|
excl_attrs.append(ex.strip())
|
||||||
|
|
||||||
FORMAT = "%(asctime)s - %(levelname)s: %(message)s"
|
FORMAT = "%(asctime)s - %(levelname)s: %(message)s"
|
||||||
|
@ -296,10 +288,9 @@ else:
|
||||||
|
|
||||||
|
|
||||||
class LdapServer:
|
class LdapServer:
|
||||||
|
uri = None
|
||||||
uri = ""
|
dn = None
|
||||||
dn = ""
|
pwd = None
|
||||||
pwd = ""
|
|
||||||
start_tls = False
|
start_tls = False
|
||||||
|
|
||||||
con = 0
|
con = 0
|
||||||
|
@ -330,21 +321,20 @@ class LdapServer:
|
||||||
def getContextCSN(self, basedn=False, serverid=False):
|
def getContextCSN(self, basedn=False, serverid=False):
|
||||||
if not basedn:
|
if not basedn:
|
||||||
basedn = self.dn
|
basedn = self.dn
|
||||||
data = self.search(
|
data = self.search(basedn, "(objectclass=*)", attrs=["contextCSN"], scope="base")
|
||||||
basedn, '(objectclass=*)', attrs=['contextCSN'], scope='base')
|
|
||||||
if data:
|
if data:
|
||||||
contextCSNs = data[0][0][1]['contextCSN']
|
contextCSNs = data[0][0][1]["contextCSN"]
|
||||||
logging.debug('Found contextCSNs %s', contextCSNs)
|
logging.debug("Found contextCSNs %s", contextCSNs)
|
||||||
if serverid is False:
|
if serverid is False:
|
||||||
return contextCSNs[0]
|
return contextCSNs[0]
|
||||||
csnid = str(format(serverid, 'X')).zfill(3)
|
csnid = str(format(serverid, "X")).zfill(3)
|
||||||
sub = str.encode(f'#{csnid}#', encoding="ascii", errors="replace")
|
sub = str.encode(f"#{csnid}#", encoding="ascii", errors="replace")
|
||||||
CSN = [s for s in contextCSNs if sub in s]
|
CSN = [s for s in contextCSNs if sub in s]
|
||||||
if not CSN:
|
if not CSN:
|
||||||
logging.error(
|
logging.error(
|
||||||
"No contextCSN matching with ServerID %s (=%s) could be "
|
"No contextCSN matching with ServerID %s (=%s) could be found.",
|
||||||
"found.",
|
serverid,
|
||||||
serverid, sub
|
sub,
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
return CSN[0]
|
return CSN[0]
|
||||||
|
@ -352,21 +342,19 @@ class LdapServer:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_scope(scope):
|
def get_scope(scope):
|
||||||
if scope == 'base':
|
if scope == "base":
|
||||||
return ldap.SCOPE_BASE # pylint: disable=no-member
|
return ldap.SCOPE_BASE # pylint: disable=no-member
|
||||||
if scope == 'one':
|
if scope == "one":
|
||||||
return ldap.SCOPE_ONELEVEL # pylint: disable=no-member
|
return ldap.SCOPE_ONELEVEL # pylint: disable=no-member
|
||||||
if scope == 'sub':
|
if scope == "sub":
|
||||||
return ldap.SCOPE_SUBTREE # pylint: disable=no-member
|
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):
|
def search(self, basedn, filterstr, attrs=None, scope=None):
|
||||||
if self.page_size:
|
if self.page_size:
|
||||||
return self.paged_search(
|
return self.paged_search(basedn, filterstr, attrs=attrs, scope=scope)
|
||||||
basedn, filterstr, attrs=attrs, scope=scope)
|
|
||||||
res_id = self.con.search(
|
res_id = self.con.search(
|
||||||
basedn, self.get_scope(scope if scope else 'sub'),
|
basedn, self.get_scope(scope if scope else "sub"), filterstr, attrs if attrs else []
|
||||||
filterstr, attrs if attrs else []
|
|
||||||
)
|
)
|
||||||
ret = []
|
ret = []
|
||||||
while 1:
|
while 1:
|
||||||
|
@ -380,13 +368,16 @@ class LdapServer:
|
||||||
def paged_search(self, basedn, filterstr, attrs=None, scope=None):
|
def paged_search(self, basedn, filterstr, attrs=None, scope=None):
|
||||||
ret = []
|
ret = []
|
||||||
page = 0
|
page = 0
|
||||||
pg_ctrl = SimplePagedResultsControl(True, self.page_size, '')
|
pg_ctrl = SimplePagedResultsControl(True, self.page_size, "")
|
||||||
while page == 0 or pg_ctrl.cookie:
|
while page == 0 or pg_ctrl.cookie:
|
||||||
page += 1
|
page += 1
|
||||||
logging.debug('Page search: loading page %d', page)
|
logging.debug("Page search: loading page %d", page)
|
||||||
res_id = self.con.search_ext(
|
res_id = self.con.search_ext(
|
||||||
basedn, self.get_scope(scope if scope else 'sub'),
|
basedn,
|
||||||
filterstr, attrs if attrs else [], serverctrls=[pg_ctrl]
|
self.get_scope(scope if scope else "sub"),
|
||||||
|
filterstr,
|
||||||
|
attrs if attrs else [],
|
||||||
|
serverctrls=[pg_ctrl],
|
||||||
)
|
)
|
||||||
# pylint: disable=unused-variable
|
# pylint: disable=unused-variable
|
||||||
res_type, res_data, res_id, serverctrls = self.con.result3(res_id)
|
res_type, res_data, res_id, serverctrls = self.con.result3(res_id)
|
||||||
|
@ -403,11 +394,11 @@ class LdapServer:
|
||||||
if not ldif:
|
if not ldif:
|
||||||
return True
|
return True
|
||||||
try:
|
try:
|
||||||
logging.debug('Update object %s: %s', dn, ldif)
|
logging.debug("Update object %s: %s", dn, ldif)
|
||||||
self.con.modify_s(dn, ldif)
|
self.con.modify_s(dn, ldif)
|
||||||
return True
|
return True
|
||||||
except LDAPError:
|
except LDAPError:
|
||||||
logging.error('Error updating object %s', dn, exc_info=True)
|
logging.error("Error updating object %s", dn, exc_info=True)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -432,14 +423,9 @@ class LdapServer:
|
||||||
else:
|
else:
|
||||||
new[attr].append(TOUCH_VALUE)
|
new[attr].append(TOUCH_VALUE)
|
||||||
try:
|
try:
|
||||||
logging.info(
|
logging.info('Touch object "%s" on attribute "%s": %s => %s', dn, attr, old, new)
|
||||||
'Touch object "%s" on attribute "%s": %s => %s',
|
|
||||||
dn, attr, old, new
|
|
||||||
)
|
|
||||||
if self.update_object(dn, old, new):
|
if self.update_object(dn, old, new):
|
||||||
logging.info(
|
logging.info('Restore original value of attribute "%s" of object "%s"', attr, dn)
|
||||||
'Restore original value of attribute "%s" of object "%s"',
|
|
||||||
attr, dn)
|
|
||||||
if options.removetouchvalue and TOUCH_VALUE in old[attr]:
|
if options.removetouchvalue and TOUCH_VALUE in old[attr]:
|
||||||
old[attr].remove(TOUCH_VALUE)
|
old[attr].remove(TOUCH_VALUE)
|
||||||
self.update_object(dn=dn, old=new, new=old)
|
self.update_object(dn=dn, old=new, new=old)
|
||||||
|
@ -451,8 +437,7 @@ class LdapServer:
|
||||||
|
|
||||||
if options.nocheckcert:
|
if options.nocheckcert:
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
ldap.set_option(
|
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
||||||
ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
|
||||||
|
|
||||||
servers = [options.provider, options.consumer]
|
servers = [options.provider, options.consumer]
|
||||||
|
|
||||||
|
@ -461,44 +446,36 @@ LdapObjects = {}
|
||||||
LdapServersCSN = {}
|
LdapServersCSN = {}
|
||||||
|
|
||||||
for srv in servers:
|
for srv in servers:
|
||||||
logging.info('Connect to %s', srv)
|
logging.info("Connect to %s", srv)
|
||||||
LdapServers[srv] = LdapServer(srv, options.dn, options.pwd,
|
LdapServers[srv] = LdapServer(
|
||||||
options.starttls,
|
srv, options.dn, options.pwd, options.starttls, page_size=options.page_size
|
||||||
page_size=options.page_size)
|
)
|
||||||
|
|
||||||
if not LdapServers[srv].connect():
|
if not LdapServers[srv].connect():
|
||||||
if options.nagios:
|
if options.nagios:
|
||||||
print(f'UNKWNON - Failed to connect to {srv}')
|
print(f"UNKWNON - Failed to connect to {srv}")
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
else:
|
else:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not options.nocheckcontextcsn:
|
if not options.nocheckcontextcsn:
|
||||||
LdapServersCSN[srv] = LdapServers[srv].getContextCSN(
|
LdapServersCSN[srv] = LdapServers[srv].getContextCSN(options.basedn, options.serverid)
|
||||||
options.basedn, options.serverid)
|
logging.info("ContextCSN of %s: %s", srv, LdapServersCSN[srv])
|
||||||
logging.info('ContextCSN of %s: %s', srv, LdapServersCSN[srv])
|
|
||||||
|
|
||||||
if not options.onlycheckcontextcsn:
|
if not options.onlycheckcontextcsn:
|
||||||
logging.info('List objects from %s', srv)
|
logging.info("List objects from %s", srv)
|
||||||
LdapObjects[srv] = {}
|
LdapObjects[srv] = {}
|
||||||
|
|
||||||
if options.attrs:
|
if options.attrs:
|
||||||
for obj in LdapServers[srv].search(
|
for obj in LdapServers[srv].search(options.basedn, options.filterstr, []):
|
||||||
options.basedn, options.filterstr, []
|
logging.debug("Found on %s: %s", srv, obj[0][0])
|
||||||
):
|
|
||||||
logging.debug('Found on %s: %s', srv, obj[0][0])
|
|
||||||
LdapObjects[srv][obj[0][0]] = obj[0][1]
|
LdapObjects[srv][obj[0][0]] = obj[0][1]
|
||||||
else:
|
else:
|
||||||
for obj in LdapServers[srv].search(
|
for obj in LdapServers[srv].search(options.basedn, options.filterstr, ["entryCSN"]):
|
||||||
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.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:
|
if not options.onlycheckcontextcsn:
|
||||||
|
@ -510,14 +487,11 @@ if not options.onlycheckcontextcsn:
|
||||||
not_sync[srv] = []
|
not_sync[srv] = []
|
||||||
|
|
||||||
if options.attrs:
|
if options.attrs:
|
||||||
logging.info(
|
logging.info("Check if objects a are synchronized (by comparing attributes's values)")
|
||||||
"Check if objects a are synchronized (by comparing attributes's "
|
|
||||||
"values)")
|
|
||||||
else:
|
else:
|
||||||
logging.info(
|
logging.info("Check if objects are synchronized (by comparing entryCSN)")
|
||||||
'Check if objets are synchronized (by comparing entryCSN)')
|
|
||||||
for obj in LdapObjects[options.provider]:
|
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():
|
for srv_name, srv in LdapObjects.items():
|
||||||
if srv_name == options.provider:
|
if srv_name == options.provider:
|
||||||
continue
|
continue
|
||||||
|
@ -533,7 +507,9 @@ if not options.onlycheckcontextcsn:
|
||||||
attrs_list.append(attr)
|
attrs_list.append(attr)
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"Obj %s not synchronized: %s not present on %s",
|
"Obj %s not synchronized: %s not present on %s",
|
||||||
obj, ','.join(attrs_list), srv_name
|
obj,
|
||||||
|
",".join(attrs_list),
|
||||||
|
srv_name,
|
||||||
)
|
)
|
||||||
touch = True
|
touch = True
|
||||||
else:
|
else:
|
||||||
|
@ -543,7 +519,8 @@ if not options.onlycheckcontextcsn:
|
||||||
attrs_list.append(attr)
|
attrs_list.append(attr)
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"Obj %s not synchronized: %s not same value(s)",
|
"Obj %s not synchronized: %s not same value(s)",
|
||||||
obj, ','.join(attrs_list)
|
obj,
|
||||||
|
",".join(attrs_list),
|
||||||
)
|
)
|
||||||
touch = True
|
touch = True
|
||||||
if attrs_list:
|
if attrs_list:
|
||||||
|
@ -551,29 +528,29 @@ if not options.onlycheckcontextcsn:
|
||||||
else:
|
else:
|
||||||
logging.debug(
|
logging.debug(
|
||||||
"Obj %s not synchronized: %s <-> %s",
|
"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)
|
not_sync[srv_name].append(obj)
|
||||||
if touch and options.touch:
|
if touch and options.touch:
|
||||||
orig_value = []
|
orig_value = []
|
||||||
if options.touch in LdapObjects[options.provider][obj]:
|
if options.touch in LdapObjects[options.provider][obj]:
|
||||||
orig_value = LdapObjects[options.provider][obj][options.touch]
|
orig_value = LdapObjects[options.provider][obj][options.touch]
|
||||||
LdapServers[options.provider].touch_object(
|
LdapServers[options.provider].touch_object(obj, options.touch, orig_value)
|
||||||
obj, options.touch, orig_value)
|
|
||||||
else:
|
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)
|
not_found[srv_name].append(obj)
|
||||||
if options.touch:
|
if options.touch:
|
||||||
orig_value = []
|
orig_value = []
|
||||||
if options.touch in LdapObjects[options.provider][obj]:
|
if options.touch in LdapObjects[options.provider][obj]:
|
||||||
orig_value = LdapObjects[options.provider][obj][options.touch]
|
orig_value = LdapObjects[options.provider][obj][options.touch]
|
||||||
LdapServers[options.provider].touch_object(
|
LdapServers[options.provider].touch_object(obj, options.touch, orig_value)
|
||||||
obj, options.touch, orig_value)
|
|
||||||
|
|
||||||
for obj in LdapObjects[options.consumer]:
|
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]:
|
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)
|
not_found[options.provider].append(obj)
|
||||||
|
|
||||||
if options.nagios:
|
if options.nagios:
|
||||||
|
@ -582,91 +559,75 @@ if options.nagios:
|
||||||
|
|
||||||
if not options.nocheckcontextcsn:
|
if not options.nocheckcontextcsn:
|
||||||
if not LdapServersCSN[options.provider]:
|
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:
|
else:
|
||||||
long_output.append(
|
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():
|
for srv_name, srv_csn in LdapServersCSN.items():
|
||||||
if srv_name == options.provider:
|
if srv_name == options.provider:
|
||||||
continue
|
continue
|
||||||
if not srv_csn:
|
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]:
|
elif srv_csn != LdapServersCSN[options.provider]:
|
||||||
errors.append(
|
errors.append(f"ContextCSN of {srv_name} not the same of provider")
|
||||||
f'ContextCSN of {srv_name} not the same of provider')
|
long_output.append(f"ContextCSN on LDAP server {srv_name} = {srv_csn}")
|
||||||
long_output.append(
|
|
||||||
f'ContextCSN on LDAP server {srv_name} = {srv_csn}')
|
|
||||||
|
|
||||||
if not options.onlycheckcontextcsn:
|
if not options.onlycheckcontextcsn:
|
||||||
if not_found[options.consumer]:
|
if not_found[options.consumer]:
|
||||||
errors.append(
|
errors.append(f"{len(not_found[options.consumer])} not found object(s) on consumer")
|
||||||
f'{len(not_found[options.consumer])} not found object(s) on '
|
long_output.append(f"Object(s) not found on server {options.consumer} (consumer):")
|
||||||
'consumer')
|
|
||||||
long_output.append(
|
|
||||||
f'Object(s) not found on server {options.consumer} '
|
|
||||||
'(consumer):')
|
|
||||||
for obj in not_found[options.consumer]:
|
for obj in not_found[options.consumer]:
|
||||||
long_output.append(f' - {obj}')
|
long_output.append(f" - {obj}")
|
||||||
if not_found[options.provider]:
|
if not_found[options.provider]:
|
||||||
errors.append(
|
errors.append(f"{len(not_found[options.provider])} not found object(s) on provider")
|
||||||
f'{len(not_found[options.provider])} not found object(s) on '
|
long_output.append(f"Object(s) not found on server {options.provider} (provider):")
|
||||||
'provider')
|
|
||||||
long_output.append(
|
|
||||||
f'Object(s) not found on server {options.provider} '
|
|
||||||
'(provider):')
|
|
||||||
for obj in not_found[options.provider]:
|
for obj in not_found[options.provider]:
|
||||||
long_output.append(f' - {obj}')
|
long_output.append(f" - {obj}")
|
||||||
if not_sync[options.consumer]:
|
if not_sync[options.consumer]:
|
||||||
errors.append(
|
errors.append(
|
||||||
f'{len(not_sync[options.consumer])} not synchronized object(s) '
|
f"{len(not_sync[options.consumer])} not synchronized object(s) on consumer"
|
||||||
'on consumer')
|
)
|
||||||
long_output.append(
|
long_output.append(
|
||||||
f'Object(s) not synchronized on server {options.consumer} '
|
f"Object(s) not synchronized on server {options.consumer} (consumer):"
|
||||||
'(consumer):')
|
)
|
||||||
for obj in not_sync[options.consumer]:
|
for obj in not_sync[options.consumer]:
|
||||||
long_output.append(f' - {obj}')
|
long_output.append(f" - {obj}")
|
||||||
if errors:
|
if errors:
|
||||||
print(f'CRITICAL: {", ".join(errors)}')
|
print(f'CRITICAL: {", ".join(errors)}')
|
||||||
print('\n\n')
|
print("\n\n")
|
||||||
print("\n".join(long_output))
|
print("\n".join(long_output))
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
else:
|
else:
|
||||||
print('OK: consumer and provider are synchronized')
|
print("OK: consumer and provider are synchronized")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
noerror = True
|
noerror = True
|
||||||
for srv in servers:
|
for srv in servers:
|
||||||
if not options.nocheckcontextcsn:
|
if not options.nocheckcontextcsn:
|
||||||
if not LdapServersCSN[options.provider]:
|
if not LdapServersCSN[options.provider]:
|
||||||
logging.warning(
|
logging.warning("ContextCSN of LDAP server provider could not be found")
|
||||||
'ContextCSN of LDAP server provider could not be found')
|
|
||||||
noerror = False
|
noerror = False
|
||||||
else:
|
else:
|
||||||
for srv_name, srv_csn in LdapServersCSN.items():
|
for srv_name, srv_csn in LdapServersCSN.items():
|
||||||
if srv_name == options.provider:
|
if srv_name == options.provider:
|
||||||
continue
|
continue
|
||||||
if not srv_csn:
|
if not srv_csn:
|
||||||
logging.warning('ContextCSN of %s not found', srv_name)
|
logging.warning("ContextCSN of %s not found", srv_name)
|
||||||
noerror = False
|
noerror = False
|
||||||
elif srv_csn != LdapServersCSN[options.provider]:
|
elif srv_csn != LdapServersCSN[options.provider]:
|
||||||
logging.warning(
|
logging.warning("ContextCSN of %s not the same of provider", srv_name)
|
||||||
'ContextCSN of %s not the same of provider',
|
|
||||||
srv_name)
|
|
||||||
noerror = False
|
noerror = False
|
||||||
|
|
||||||
if not options.onlycheckcontextcsn:
|
if not options.onlycheckcontextcsn:
|
||||||
if not_found[srv]:
|
if not_found[srv]:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
'Not found objects on %s :\n - %s',
|
"Not found objects on %s :\n - %s", srv, "\n - ".join(not_found[srv])
|
||||||
srv, '\n - '.join(not_found[srv])
|
|
||||||
)
|
)
|
||||||
noerror = False
|
noerror = False
|
||||||
if not_sync[srv]:
|
if not_sync[srv]:
|
||||||
logging.warning(
|
logging.warning("Not sync objects on %s: %s", srv, "\n - ".join(not_sync[srv]))
|
||||||
'Not sync objects on %s: %s',
|
|
||||||
srv, '\n - '.join(not_sync[srv])
|
|
||||||
)
|
|
||||||
noerror = False
|
noerror = False
|
||||||
|
|
||||||
if noerror:
|
if noerror:
|
||||||
logging.info('No sync problem detected')
|
logging.info("No sync problem detected")
|
||||||
|
|
4
debian/control
vendored
4
debian/control
vendored
|
@ -12,9 +12,9 @@ Description: Check LDAP syncrepl replication state between two servers
|
||||||
This script check LDAP syncrepl replication state between two servers. One
|
This script check LDAP syncrepl replication state between two servers. One
|
||||||
server is consider as provider and the other as consumer.
|
server is consider as provider and the other as consumer.
|
||||||
This script can check replication state with two method :
|
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
|
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.
|
between two servers.
|
||||||
In all case, contextCSN of servers will be compare and entries not present in
|
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
|
consumer or in provider will be notice. You can decide to disable contextCSN
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
---
|
||||||
name: check_syncrepl_extended
|
name: check_syncrepl_extended
|
||||||
description: "file:///README.md"
|
description: "file:///README.md"
|
||||||
url: "https://gitea.zionetrix.net/bn8/check_syncrepl_extended"
|
url: "https://gitea.zionetrix.net/bn8/check_syncrepl_extended"
|
||||||
|
@ -7,21 +8,17 @@ target: Database
|
||||||
type: Plugin
|
type: Plugin
|
||||||
license: gplv2
|
license: gplv2
|
||||||
releases:
|
releases:
|
||||||
-
|
- name: v2017.09.11
|
||||||
name: v2017.09.11
|
|
||||||
description: "v2017.09.11 Release"
|
description: "v2017.09.11 Release"
|
||||||
files:
|
files:
|
||||||
-
|
- name: check_syncrepl_extended
|
||||||
name: check_syncrepl_extended
|
|
||||||
url: "file:///check_syncrepl_extended"
|
url: "file:///check_syncrepl_extended"
|
||||||
description: "First release upload on Icinga Exchange"
|
description: "First release upload on Icinga Exchange"
|
||||||
checksum: cee2e8fdc243edcbab30d3b034e8b9bf
|
checksum: cee2e8fdc243edcbab30d3b034e8b9bf
|
||||||
-
|
- name: v2017.09.12
|
||||||
name: v2017.09.12
|
|
||||||
description: "v2017.09.12 Release"
|
description: "v2017.09.12 Release"
|
||||||
files:
|
files:
|
||||||
-
|
- name: check_syncrepl_extended
|
||||||
name: check_syncrepl_extended
|
|
||||||
url: "file:///check_syncrepl_extended"
|
url: "file:///check_syncrepl_extended"
|
||||||
description: "Improve ContextCSN checking (thanks Orhan)"
|
description: "Improve ContextCSN checking (thanks Orhan)"
|
||||||
checksum: a954ca49855c23f2f098f4696c4c0ebd
|
checksum: a954ca49855c23f2f098f4696c4c0ebd
|
||||||
|
|
Loading…
Reference in a new issue