Compare commits

..

No commits in common. "b1953a5b5115893d9fb7fdfea452fa5be94fcab7" and "a3d6c8cfb086ce0a10f2b0b03da568b1e5ebaca7" have entirely different histories.

12 changed files with 81 additions and 327 deletions

1
.gitignore vendored
View file

@ -1,2 +1 @@
*~ *~
dist/

View file

@ -1,71 +0,0 @@
clone:
git:
image: woodpeckerci/plugin-git
tags: true
pipeline:
test-pylint:
group: test
image: debian
commands:
- DEBIAN_FRONTEND=noninteractive apt-get -qq update < /dev/null > /dev/null
- DEBIAN_FRONTEND=noninteractive apt-get -qq -y install --no-install-recommends python3-ldap pylint3 < /dev/null > /dev/null
- pylint3 gitdch
test-flake8:
group: test
image: pipelinecomponents/flake8
commands:
- flake8 gitdch
build:
image: brenard/debian-python-deb
when:
event: tag
commands:
- echo "$GPG_KEY"|base64 -d|gpg --import
- ./build.sh --quiet
secrets: [ maintainer_name, maintainer_email, gpg_key, debian_codename ]
publish-dryrun:
group: publish
image: alpine
when:
event: tag
commands:
- ls dist/* dist/check-syncrepl-extended-*/check_syncrepl_extended
publish-gitea:
group: publish
image: plugins/gitea-release
when:
event: tag
settings:
api_key:
from_secret: gitea_token
base_url: https://gitea.zionetrix.net
files:
- dist/check-syncrepl-extended-*/check_syncrepl_extended
- dist/*.deb
checksum:
- md5
- sha512
publish-apt:
group: publish
image: brenard/curl
when:
event: tag
commands:
- curl -u $APT_CREDS -X POST -F file=@$( ls dist/check-syncrepl-extended_*_all.deb ) $APT_API_URL/files/gitdch
- curl -u $APT_CREDS -X POST -F file=@$( ls dist/check-syncrepl-extended_*.buildinfo ) $APT_API_URL/files/gitdch
- curl -u $APT_CREDS -X POST -F file=@$( ls dist/check-syncrepl-extended_*.changes ) $APT_API_URL/files/gitdch
- curl -u $APT_CREDS -X POST -F file=@$( ls dist/check-syncrepl-extended_*.dsc ) $APT_API_URL/files/gitdch
- curl -u $APT_CREDS -X POST -F file=@$( ls dist/check-syncrepl-extended_*.tar.gz ) $APT_API_URL/files/gitdch
- curl -u $APT_CREDS -X POST $APT_API_URL/repos/$APT_REPO_NAME/include/gitdch
- APT_SNAP_NAME=$(date +%s)_$APT_REPO_NAME
- >
curl -u $APT_CREDS -X POST -H 'Content-Type: application/json' --data "{\"Name\":\"$APT_SNAP_NAME\"}" $APT_API_URL/repos/$APT_REPO_NAME/snapshots
- >
curl -u $APT_CREDS -X PUT -H 'Content-Type: application/json' --data "{\"Snapshots\": [{\"Component\": \"main\", \"Name\": \"$APT_SNAP_NAME\"}]}" $APT_API_URL/publish/:./$APT_REPO_NAME
secrets: [ apt_api_url, apt_creds, apt_repo_name ]

View file

@ -1,61 +0,0 @@
#!/bin/bash
QUIET_ARG=""
[ "$1" == "--quiet" ] && QUIET_ARG="--quiet"
# Enter source directory
cd $( dirname $0 )
echo "Clean previous build..."
rm -fr dist
echo "Detect version using git describe..."
VERSION="$( git describe --tags|sed 's/^[^0-9]*//' )"
echo "Create building environemt..."
BDIR=dist/check-syncrepl-extended-$VERSION
mkdir -p $BDIR
[ -z "$QUIET_ARG" ] && RSYNC_ARG="-v" || RSYNC_ARG=""
rsync -a $RSYNC_ARG debian/ $BDIR/debian/
cp check_syncrepl_extended $BDIR/
echo "Set VERSION=$VERSION in gitdch using sed..."
sed -i "s/^VERSION *=.*$/VERSION = '$VERSION'/" $BDIR/check_syncrepl_extended
if [ -z "$DEBIAN_CODENAME" ]
then
echo "Retreive debian codename using lsb_release..."
DEBIAN_CODENAME=$( lsb_release -c -s )
else
echo "Use debian codename from environment ($DEBIAN_CODENAME)"
fi
echo "Generate debian changelog using gitdch..."
GITDCH_ARGS=('--verbose')
[ -n "$QUIET_ARG" ] && GITDCH_ARGS=('--warning')
if [ -n "$MAINTAINER_NAME" ]
then
echo "Use maintainer name from environment ($MAINTAINER_NAME)"
GITDCH_ARGS+=("--maintainer-name" "${MAINTAINER_NAME}")
fi
if [ -n "$MAINTAINER_EMAIL" ]
then
echo "Use maintainer email from environment ($MAINTAINER_EMAIL)"
GITDCH_ARGS+=("--maintainer-email" "$MAINTAINER_EMAIL")
fi
gitdch \
--package-name check-syncrepl-extended \
--version "${VERSION}" \
--code-name $DEBIAN_CODENAME \
--output $BDIR/debian/changelog \
"${GITDCH_ARGS[@]}"
if [ -n "$MAINTAINER_NAME" -a -n "$MAINTAINER_EMAIL" ]
then
echo "Set Maintainer field in debian control file ($MAINTAINER_NAME <$MAINTAINER_EMAIL>)..."
sed -i "s/^Maintainer: .*$/Maintainer: $MAINTAINER_NAME <$MAINTAINER_EMAIL>/" $BDIR/debian/control
fi
echo "Build debian package..."
cd $BDIR
dpkg-buildpackage

View file

@ -43,19 +43,15 @@ 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.controls import SimplePagedResultsControl
from ldap import modlist import ldap.modlist as modlist
VERSION = '0.0'
TOUCH_VALUE = '%%TOUCH%%' TOUCH_VALUE = '%%TOUCH%%'
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description=( description=("Script to check LDAP syncrepl replication state between "+
"Script to check LDAP syncrepl replication state between "
"two servers."), "two servers."),
epilog=( epilog=("Author: Benjamin Renard <brenard@easter-eggs.com>, "+
'Author: Benjamin Renard <brenard@easter-eggs.com>, ' "Source: https://gogs.zionetrix.net/bn8/check_syncrepl_extended")
f'Version: {VERSION}, '
'Source: https://gogs.zionetrix.net/bn8/check_syncrepl_extended')
) )
parser.add_argument( parser.add_argument(
@ -79,11 +75,10 @@ parser.add_argument(
dest="serverid", dest="serverid",
action="store", action="store",
type=int, type=int,
help=( help=("Compare contextCSN of a specific master. Useful in MultiMaster "+
"Compare contextCSN of a specific master. Useful in MultiMaster " "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
) )
@ -174,8 +169,7 @@ 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, "+
"Only check servers root contextCSN (objects check disabled, "
"default : False)"), "default : False)"),
default=False default=False
) )
@ -202,13 +196,11 @@ parser.add_argument(
dest="touch", dest="touch",
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 ' "object from provider. A value '{}' will be add to this attribute "+
f'object from provider. A value "{TOUCH_VALUE}" will be add to this ' "and remove after. The user use to connect to the LDAP directory "+
'attribute and remove after. The user use to connect to the LDAP ' "must have write permission on this attribute on each object."
'directory must have write permission on this attribute on each ' ).format(TOUCH_VALUE),
'object.'
),
default=None default=None
) )
@ -233,8 +225,7 @@ 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 " +
"Page size: if defined, paging control using LDAP v3 extended "
"control will be enabled."), "control will be enabled."),
default=None default=None
) )
@ -242,8 +233,7 @@ parser.add_argument(
options = parser.parse_args() 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)
@ -261,10 +251,8 @@ if not options.basedn:
sys.exit(3) sys.exit(3)
sys.exit(1) sys.exit(1)
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)
@ -294,8 +282,7 @@ elif options.quiet:
else: else:
logging.basicConfig(level=logging.INFO, format=FORMAT) logging.basicConfig(level=logging.INFO, format=FORMAT)
class LdapServer(object):
class LdapServer:
uri = "" uri = ""
dn = "" dn = ""
@ -315,8 +302,7 @@ class LdapServer:
if self.con == 0: if self.con == 0:
try: try:
con = ldap.initialize(self.uri) con = ldap.initialize(self.uri)
# pylint: disable=no-member con.protocol_version = ldap.VERSION3 # pylint: disable=no-member
con.protocol_version = ldap.VERSION3
if self.start_tls: if self.start_tls:
con.start_tls_s() con.start_tls_s()
if self.dn: if self.dn:
@ -330,20 +316,18 @@ 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('#%s#' % 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, sub serverid, sub
) )
return False return False
@ -358,12 +342,11 @@ class LdapServer:
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("Unknown LDAP scope '%s'" % scope)
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 []
@ -388,8 +371,7 @@ class LdapServer:
basedn, self.get_scope(scope if scope else 'sub'), basedn, self.get_scope(scope if scope else 'sub'),
filterstr, attrs if attrs else [], serverctrls=[pg_ctrl] filterstr, attrs if attrs else [], serverctrls=[pg_ctrl]
) )
# pylint: disable=unused-variable res_type, res_data, res_id, serverctrls = self.con.result3(res_id) # pylint: disable=unused-variable
res_type, res_data, res_id, serverctrls = self.con.result3(res_id)
for serverctrl in serverctrls: for serverctrl in serverctrls:
if serverctrl.controlType == SimplePagedResultsControl.controlType: if serverctrl.controlType == SimplePagedResultsControl.controlType:
pg_ctrl.cookie = serverctrl.cookie pg_ctrl.cookie = serverctrl.cookie
@ -400,7 +382,7 @@ class LdapServer:
def update_object(self, dn, old, new): def update_object(self, dn, old, new):
ldif = modlist.modifyModlist(old, new) ldif = modlist.modifyModlist(old, new)
if not ldif: if ldif == []:
return True return True
try: try:
logging.debug('Update object %s: %s', dn, ldif) logging.debug('Update object %s: %s', dn, ldif)
@ -437,9 +419,7 @@ class LdapServer:
dn, attr, old, new 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)
@ -448,11 +428,8 @@ class LdapServer:
logging.error('Error touching object "%s"', dn, exc_info=True) logging.error('Error touching object "%s"', dn, exc_info=True)
return False return False
if options.nocheckcert: if options.nocheckcert:
# pylint: disable=no-member ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) # pylint: disable=no-member
ldap.set_option(
ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
servers = [options.provider, options.consumer] servers = [options.provider, options.consumer]
@ -468,14 +445,13 @@ for srv in servers:
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("UNKWNON - Failed to connect to %s" % srv) # pylint: disable=print-statement
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:
@ -483,15 +459,11 @@ for srv in servers:
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( logging.debug(
'Found on %s: %s / %s', 'Found on %s: %s / %s',
srv, obj[0][0], obj[0][1]['entryCSN'][0] srv, obj[0][0], obj[0][1]['entryCSN'][0]
@ -510,36 +482,33 @@ 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 objets 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 in LdapObjects:
if srv_name == options.provider: if srv == options.provider:
continue continue
if obj in srv: if obj in LdapObjects[srv]:
touch = False touch = False
if LdapObjects[options.provider][obj] != srv[obj]: if LdapObjects[options.provider][obj] != LdapObjects[srv][obj]:
if options.attrs: if options.attrs:
attrs_list = [] attrs_list = []
for attr in LdapObjects[options.provider][obj]: for attr in LdapObjects[options.provider][obj]:
if attr in excl_attrs: if attr in excl_attrs:
continue continue
if attr not in srv[obj]: if attr not in LdapObjects[srv][obj]:
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
) )
touch = True touch = True
else: else:
srv[obj][attr].sort() LdapObjects[srv][obj][attr].sort()
LdapObjects[options.provider][obj][attr].sort() LdapObjects[options.provider][obj][attr].sort()
if srv[obj][attr] != LdapObjects[options.provider][obj][attr]: if LdapObjects[srv][obj][attr] != LdapObjects[options.provider][obj][attr]:
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)",
@ -547,28 +516,26 @@ if not options.onlycheckcontextcsn:
) )
touch = True touch = True
if attrs_list: if attrs_list:
not_sync[srv_name].append(f'{obj} ({",".join(attrs_list)})') not_sync[srv].append("%s (%s)" % (obj, ','.join(attrs_list)))
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], LdapObjects[srv][obj]
) )
not_sync[srv_name].append(obj) not_sync[srv].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)
not_found[srv_name].append(obj) not_found[srv].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)
@ -584,74 +551,54 @@ if options.nagios:
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('ContextCSN on LDAP server provider = %s' % LdapServersCSN[options.provider])
f'ContextCSN on LDAP server provider = {LdapServersCSN[options.provider]}') for srv in LdapServersCSN:
for srv_name, srv_csn in LdapServersCSN.items(): if srv == options.provider:
if srv_name == options.provider:
continue continue
if not srv_csn: if not LdapServersCSN[srv]:
errors.append(f'ContextCSN of {srv_name} not found') errors.append('ContextCSN of %s not found' % srv)
elif srv_csn != LdapServersCSN[options.provider]: elif LdapServersCSN[srv] != LdapServersCSN[options.provider]:
errors.append( errors.append('ContextCSN of %s not the same of provider' % srv)
f'ContextCSN of {srv_name} not the same of provider') long_output.append('ContextCSN on LDAP server %s = %s' % (srv, LdapServersCSN[srv]))
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("%s not found object(s) on consumer" % len(not_found[options.consumer]))
f'{len(not_found[options.consumer])} not found object(s) on ' long_output.append("Object(s) not found on server %s (consumer) :" % options.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(" - %s" % obj)
if not_found[options.provider]: if not_found[options.provider]:
errors.append( errors.append("%s not found object(s) on provider" % len(not_found[options.provider]))
f'{len(not_found[options.provider])} not found object(s) on ' long_output.append("Object(s) not found on server %s (provider) :" % options.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(" - %s" % obj)
if not_sync[options.consumer]: if not_sync[options.consumer]:
errors.append( errors.append("%s not synchronized object(s) on consumer" % len(not_sync[options.consumer]))
f'{len(not_sync[options.consumer])} not synchronized object(s) ' long_output.append("Object(s) not synchronized on server %s (consumer) :" % options.consumer)
'on consumer')
long_output.append(
f'Object(s) not synchronized on server {options.consumer} '
'(consumer):')
for obj in not_sync[options.consumer]: for obj in not_sync[options.consumer]:
long_output.append(f' - {obj}') long_output.append(" - %s" % obj)
if errors: if errors:
print(f'CRITICAL: {", ".join(errors)}') print("CRITICAL: " + ', '.join(errors) + "\n\n" + "\n".join(long_output)) # pylint: disable=print-statement
print('\n\n')
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') # pylint: disable=print-statement
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 in LdapServersCSN:
if srv_name == options.provider: if srv == options.provider:
continue continue
if not srv_csn: if not LdapServersCSN[srv]:
logging.warning('ContextCSN of %s not found', srv_name) logging.warning('ContextCSN of %s not found', srv)
noerror = False noerror = False
elif srv_csn != LdapServersCSN[options.provider]: elif LdapServersCSN[srv] != LdapServersCSN[options.provider]:
logging.warning( logging.warning('ContextCSN of %s not the same of provider', srv)
'ContextCSN of %s not the same of provider',
srv_name)
noerror = False noerror = False
if not options.onlycheckcontextcsn: if not options.onlycheckcontextcsn:

1
debian/compat vendored
View file

@ -1 +0,0 @@
11

30
debian/control vendored
View file

@ -1,30 +0,0 @@
Source: check-syncrepl-extended
Section: admin
Priority: optional
Maintainer: Debian Zionetrix - check-syncrepl-extended <debian+check-syncrepl-extended@zionetrix.net>
Build-Depends: debhelper (>> 11.0.0)
Standards-Version: 3.9.6
Package: check-syncrepl-extended
Architecture: all
Depends: ${misc:Depends}, python3, python3-ldap
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
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.
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.
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

20
debian/copyright vendored
View file

@ -1,20 +0,0 @@
This package was written by Benjamin Renard <brenard@zionetrix.net>.
Copyright (C) 2022 Benjamin Renard <brenard@zionetrix.net>
check-syncrepl-extended is licensed under the GNU general public license, version 3.
check-syncrepl-extended is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2, or (at your option) any later version.
check-syncrepl-extended 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
check-syncrepl-extended; see the file COPYING. If not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
On Debian systems, a copy of the GNU General Public License is available in
/usr/share/common-licenses/GPL-3 as part of the base-files package.

1
debian/dirs vendored
View file

@ -1 +0,0 @@
usr/lib/nagios/plugins

1
debian/install vendored
View file

@ -1 +0,0 @@
check_syncrepl_extended usr/lib/nagios/plugins

4
debian/rules vendored
View file

@ -1,4 +0,0 @@
#!/usr/bin/make -f
#export DH_VERBOSE=1
%:
dh $@

View file

@ -1 +0,0 @@
1.0

View file

@ -1,2 +0,0 @@
[flake8]
ignore = E501