|
|
d69b2b |
From c0cb15445c1434b3d317b1c06ab1a0ba8dbc6f04 Mon Sep 17 00:00:00 2001
|
|
|
d69b2b |
From: Mark Reynolds <mreynolds@redhat.com>
|
|
|
d69b2b |
Date: Tue, 19 May 2020 15:11:53 -0400
|
|
|
d69b2b |
Subject: [PATCH 06/12] Issue 51102 - RFE - ds-replcheck - make online timeout
|
|
|
d69b2b |
configurable
|
|
|
d69b2b |
|
|
|
d69b2b |
Bug Description: When doing an online check with replicas that are very
|
|
|
d69b2b |
far apart the connection can time out as the hardcoded
|
|
|
d69b2b |
timeout is 5 seconds.
|
|
|
d69b2b |
|
|
|
d69b2b |
Fix Description: Change the default timeout to never timeout, and add an
|
|
|
d69b2b |
CLI option to specify a specific timeout.
|
|
|
d69b2b |
|
|
|
d69b2b |
Also caught all the possible LDAP exceptions so we can
|
|
|
d69b2b |
cleanly "fail". Fixed some python syntax issues, and
|
|
|
d69b2b |
improved the entry inconsistency report
|
|
|
d69b2b |
|
|
|
d69b2b |
relates: https://pagure.io/389-ds-base/issue/51102
|
|
|
d69b2b |
|
|
|
d69b2b |
Reviewed by: firstyear & spichugi(Thanks!)
|
|
|
d69b2b |
---
|
|
|
d69b2b |
ldap/admin/src/scripts/ds-replcheck | 90 ++++++++++++++++++-----------
|
|
|
d69b2b |
1 file changed, 57 insertions(+), 33 deletions(-)
|
|
|
d69b2b |
|
|
|
d69b2b |
diff --git a/ldap/admin/src/scripts/ds-replcheck b/ldap/admin/src/scripts/ds-replcheck
|
|
|
d69b2b |
index 30bcfd65d..5bb7dfce3 100755
|
|
|
d69b2b |
--- a/ldap/admin/src/scripts/ds-replcheck
|
|
|
d69b2b |
+++ b/ldap/admin/src/scripts/ds-replcheck
|
|
|
d69b2b |
@@ -1,7 +1,7 @@
|
|
|
d69b2b |
#!/usr/bin/python3
|
|
|
d69b2b |
|
|
|
d69b2b |
# --- BEGIN COPYRIGHT BLOCK ---
|
|
|
d69b2b |
-# Copyright (C) 2018 Red Hat, Inc.
|
|
|
d69b2b |
+# Copyright (C) 2020 Red Hat, Inc.
|
|
|
d69b2b |
# All rights reserved.
|
|
|
d69b2b |
#
|
|
|
d69b2b |
# License: GPL (version 3 or any later version).
|
|
|
d69b2b |
@@ -21,10 +21,9 @@ import getpass
|
|
|
d69b2b |
import signal
|
|
|
d69b2b |
from ldif import LDIFRecordList
|
|
|
d69b2b |
from ldap.ldapobject import SimpleLDAPObject
|
|
|
d69b2b |
-from ldap.cidict import cidict
|
|
|
d69b2b |
from ldap.controls import SimplePagedResultsControl
|
|
|
d69b2b |
from lib389._entry import Entry
|
|
|
d69b2b |
-from lib389.utils import ensure_str, ensure_list_str, ensure_int
|
|
|
d69b2b |
+from lib389.utils import ensure_list_str, ensure_int
|
|
|
d69b2b |
|
|
|
d69b2b |
VERSION = "2.0"
|
|
|
d69b2b |
RUV_FILTER = '(&(nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff)(objectclass=nstombstone))'
|
|
|
d69b2b |
@@ -185,11 +184,11 @@ def report_conflict(entry, attr, opts):
|
|
|
d69b2b |
report = True
|
|
|
d69b2b |
|
|
|
d69b2b |
if 'nscpentrywsi' in entry.data:
|
|
|
d69b2b |
- found = False
|
|
|
d69b2b |
for val in entry.data['nscpentrywsi']:
|
|
|
d69b2b |
if val.lower().startswith(attr + ';'):
|
|
|
d69b2b |
if (opts['starttime'] - extract_time(val)) <= opts['lag']:
|
|
|
d69b2b |
report = False
|
|
|
d69b2b |
+ break
|
|
|
d69b2b |
|
|
|
d69b2b |
return report
|
|
|
d69b2b |
|
|
|
d69b2b |
@@ -321,6 +320,9 @@ def ldif_search(LDIF, dn):
|
|
|
d69b2b |
count = 0
|
|
|
d69b2b |
ignore_list = ['conflictcsn', 'modifytimestamp', 'modifiersname']
|
|
|
d69b2b |
val = ""
|
|
|
d69b2b |
+ attr = ""
|
|
|
d69b2b |
+ state_attr = ""
|
|
|
d69b2b |
+ part_dn = ""
|
|
|
d69b2b |
result['entry'] = None
|
|
|
d69b2b |
result['conflict'] = None
|
|
|
d69b2b |
result['tombstone'] = False
|
|
|
d69b2b |
@@ -570,6 +572,7 @@ def cmp_entry(mentry, rentry, opts):
|
|
|
d69b2b |
if val.lower().startswith(mattr + ';'):
|
|
|
d69b2b |
if not found:
|
|
|
d69b2b |
diff['diff'].append(" Master:")
|
|
|
d69b2b |
+ diff['diff'].append(" - Value: %s" % (val.split(':')[1].lstrip()))
|
|
|
d69b2b |
diff['diff'].append(" - State Info: %s" % (val))
|
|
|
d69b2b |
diff['diff'].append(" - Date: %s\n" % (time.ctime(extract_time(val))))
|
|
|
d69b2b |
found = True
|
|
|
d69b2b |
@@ -588,6 +591,7 @@ def cmp_entry(mentry, rentry, opts):
|
|
|
d69b2b |
if val.lower().startswith(mattr + ';'):
|
|
|
d69b2b |
if not found:
|
|
|
d69b2b |
diff['diff'].append(" Replica:")
|
|
|
d69b2b |
+ diff['diff'].append(" - Value: %s" % (val.split(':')[1].lstrip()))
|
|
|
d69b2b |
diff['diff'].append(" - State Info: %s" % (val))
|
|
|
d69b2b |
diff['diff'].append(" - Date: %s\n" % (time.ctime(extract_time(val))))
|
|
|
d69b2b |
found = True
|
|
|
d69b2b |
@@ -654,7 +658,6 @@ def do_offline_report(opts, output_file=None):
|
|
|
d69b2b |
rconflicts = []
|
|
|
d69b2b |
rtombstones = 0
|
|
|
d69b2b |
mtombstones = 0
|
|
|
d69b2b |
- idx = 0
|
|
|
d69b2b |
|
|
|
d69b2b |
# Open LDIF files
|
|
|
d69b2b |
try:
|
|
|
d69b2b |
@@ -926,7 +929,7 @@ def validate_suffix(ldapnode, suffix, hostname):
|
|
|
d69b2b |
:return - True if suffix exists, otherwise False
|
|
|
d69b2b |
"""
|
|
|
d69b2b |
try:
|
|
|
d69b2b |
- master_basesuffix = ldapnode.search_s(suffix, ldap.SCOPE_BASE )
|
|
|
d69b2b |
+ ldapnode.search_s(suffix, ldap.SCOPE_BASE)
|
|
|
d69b2b |
except ldap.NO_SUCH_OBJECT:
|
|
|
d69b2b |
print("Error: Failed to validate suffix in {}. {} does not exist.".format(hostname, suffix))
|
|
|
d69b2b |
return False
|
|
|
d69b2b |
@@ -968,12 +971,12 @@ def connect_to_replicas(opts):
|
|
|
d69b2b |
replica = SimpleLDAPObject(ruri)
|
|
|
d69b2b |
|
|
|
d69b2b |
# Set timeouts
|
|
|
d69b2b |
- master.set_option(ldap.OPT_NETWORK_TIMEOUT,5.0)
|
|
|
d69b2b |
- master.set_option(ldap.OPT_TIMEOUT,5.0)
|
|
|
d69b2b |
- replica.set_option(ldap.OPT_NETWORK_TIMEOUT,5.0)
|
|
|
d69b2b |
- replica.set_option(ldap.OPT_TIMEOUT,5.0)
|
|
|
d69b2b |
+ master.set_option(ldap.OPT_NETWORK_TIMEOUT, opts['timeout'])
|
|
|
d69b2b |
+ master.set_option(ldap.OPT_TIMEOUT, opts['timeout'])
|
|
|
d69b2b |
+ replica.set_option(ldap.OPT_NETWORK_TIMEOUT, opts['timeout'])
|
|
|
d69b2b |
+ replica.set_option(ldap.OPT_TIMEOUT, opts['timeout'])
|
|
|
d69b2b |
|
|
|
d69b2b |
- # Setup Secure Conenction
|
|
|
d69b2b |
+ # Setup Secure Connection
|
|
|
d69b2b |
if opts['certdir'] is not None:
|
|
|
d69b2b |
# Setup Master
|
|
|
d69b2b |
if opts['mprotocol'] != LDAPI:
|
|
|
d69b2b |
@@ -1003,7 +1006,7 @@ def connect_to_replicas(opts):
|
|
|
d69b2b |
try:
|
|
|
d69b2b |
master.simple_bind_s(opts['binddn'], opts['bindpw'])
|
|
|
d69b2b |
except ldap.SERVER_DOWN as e:
|
|
|
d69b2b |
- print("Cannot connect to %r" % muri)
|
|
|
d69b2b |
+ print(f"Cannot connect to {muri} ({str(e)})")
|
|
|
d69b2b |
sys.exit(1)
|
|
|
d69b2b |
except ldap.LDAPError as e:
|
|
|
d69b2b |
print("Error: Failed to authenticate to Master: ({}). "
|
|
|
d69b2b |
@@ -1014,7 +1017,7 @@ def connect_to_replicas(opts):
|
|
|
d69b2b |
try:
|
|
|
d69b2b |
replica.simple_bind_s(opts['binddn'], opts['bindpw'])
|
|
|
d69b2b |
except ldap.SERVER_DOWN as e:
|
|
|
d69b2b |
- print("Cannot connect to %r" % ruri)
|
|
|
d69b2b |
+ print(f"Cannot connect to {ruri} ({str(e)})")
|
|
|
d69b2b |
sys.exit(1)
|
|
|
d69b2b |
except ldap.LDAPError as e:
|
|
|
d69b2b |
print("Error: Failed to authenticate to Replica: ({}). "
|
|
|
d69b2b |
@@ -1218,7 +1221,6 @@ def do_online_report(opts, output_file=None):
|
|
|
d69b2b |
"""
|
|
|
d69b2b |
m_done = False
|
|
|
d69b2b |
r_done = False
|
|
|
d69b2b |
- done = False
|
|
|
d69b2b |
report = {}
|
|
|
d69b2b |
report['diff'] = []
|
|
|
d69b2b |
report['m_missing'] = []
|
|
|
d69b2b |
@@ -1257,15 +1259,22 @@ def do_online_report(opts, output_file=None):
|
|
|
d69b2b |
|
|
|
d69b2b |
# Read the results and start comparing
|
|
|
d69b2b |
while not m_done or not r_done:
|
|
|
d69b2b |
- if not m_done:
|
|
|
d69b2b |
- m_rtype, m_rdata, m_rmsgid, m_rctrls = master.result3(master_msgid)
|
|
|
d69b2b |
- elif not r_done:
|
|
|
d69b2b |
- m_rdata = []
|
|
|
d69b2b |
-
|
|
|
d69b2b |
- if not r_done:
|
|
|
d69b2b |
- r_rtype, r_rdata, r_rmsgid, r_rctrls = replica.result3(replica_msgid)
|
|
|
d69b2b |
- elif not m_done:
|
|
|
d69b2b |
- r_rdata = []
|
|
|
d69b2b |
+ try:
|
|
|
d69b2b |
+ if not m_done:
|
|
|
d69b2b |
+ m_rtype, m_rdata, m_rmsgid, m_rctrls = master.result3(master_msgid)
|
|
|
d69b2b |
+ elif not r_done:
|
|
|
d69b2b |
+ m_rdata = []
|
|
|
d69b2b |
+ except ldap.LDAPError as e:
|
|
|
d69b2b |
+ print("Error: Problem getting the results from the master: %s", str(e))
|
|
|
d69b2b |
+ sys.exit(1)
|
|
|
d69b2b |
+ try:
|
|
|
d69b2b |
+ if not r_done:
|
|
|
d69b2b |
+ r_rtype, r_rdata, r_rmsgid, r_rctrls = replica.result3(replica_msgid)
|
|
|
d69b2b |
+ elif not m_done:
|
|
|
d69b2b |
+ r_rdata = []
|
|
|
d69b2b |
+ except ldap.LDAPError as e:
|
|
|
d69b2b |
+ print("Error: Problem getting the results from the replica: %s", str(e))
|
|
|
d69b2b |
+ sys.exit(1)
|
|
|
d69b2b |
|
|
|
d69b2b |
# Convert entries
|
|
|
d69b2b |
mresult = convert_entries(m_rdata)
|
|
|
d69b2b |
@@ -1291,11 +1300,15 @@ def do_online_report(opts, output_file=None):
|
|
|
d69b2b |
]
|
|
|
d69b2b |
if m_pctrls:
|
|
|
d69b2b |
if m_pctrls[0].cookie:
|
|
|
d69b2b |
- # Copy cookie from response control to request control
|
|
|
d69b2b |
- req_pr_ctrl.cookie = m_pctrls[0].cookie
|
|
|
d69b2b |
- master_msgid = master.search_ext(opts['suffix'], ldap.SCOPE_SUBTREE,
|
|
|
d69b2b |
- "(|(objectclass=*)(objectclass=ldapsubentry))",
|
|
|
d69b2b |
- ['*', 'createtimestamp', 'nscpentrywsi', 'conflictcsn', 'nsds5replconflict'], serverctrls=controls)
|
|
|
d69b2b |
+ try:
|
|
|
d69b2b |
+ # Copy cookie from response control to request control
|
|
|
d69b2b |
+ req_pr_ctrl.cookie = m_pctrls[0].cookie
|
|
|
d69b2b |
+ master_msgid = master.search_ext(opts['suffix'], ldap.SCOPE_SUBTREE,
|
|
|
d69b2b |
+ "(|(objectclass=*)(objectclass=ldapsubentry))",
|
|
|
d69b2b |
+ ['*', 'createtimestamp', 'nscpentrywsi', 'conflictcsn', 'nsds5replconflict'], serverctrls=controls)
|
|
|
d69b2b |
+ except ldap.LDAPError as e:
|
|
|
d69b2b |
+ print("Error: Problem searching the master: %s", str(e))
|
|
|
d69b2b |
+ sys.exit(1)
|
|
|
d69b2b |
else:
|
|
|
d69b2b |
m_done = True # No more pages available
|
|
|
d69b2b |
else:
|
|
|
d69b2b |
@@ -1311,11 +1324,15 @@ def do_online_report(opts, output_file=None):
|
|
|
d69b2b |
|
|
|
d69b2b |
if r_pctrls:
|
|
|
d69b2b |
if r_pctrls[0].cookie:
|
|
|
d69b2b |
- # Copy cookie from response control to request control
|
|
|
d69b2b |
- req_pr_ctrl.cookie = r_pctrls[0].cookie
|
|
|
d69b2b |
- replica_msgid = replica.search_ext(opts['suffix'], ldap.SCOPE_SUBTREE,
|
|
|
d69b2b |
- "(|(objectclass=*)(objectclass=ldapsubentry))",
|
|
|
d69b2b |
- ['*', 'createtimestamp', 'nscpentrywsi', 'conflictcsn', 'nsds5replconflict'], serverctrls=controls)
|
|
|
d69b2b |
+ try:
|
|
|
d69b2b |
+ # Copy cookie from response control to request control
|
|
|
d69b2b |
+ req_pr_ctrl.cookie = r_pctrls[0].cookie
|
|
|
d69b2b |
+ replica_msgid = replica.search_ext(opts['suffix'], ldap.SCOPE_SUBTREE,
|
|
|
d69b2b |
+ "(|(objectclass=*)(objectclass=ldapsubentry))",
|
|
|
d69b2b |
+ ['*', 'createtimestamp', 'nscpentrywsi', 'conflictcsn', 'nsds5replconflict'], serverctrls=controls)
|
|
|
d69b2b |
+ except ldap.LDAPError as e:
|
|
|
d69b2b |
+ print("Error: Problem searching the replica: %s", str(e))
|
|
|
d69b2b |
+ sys.exit(1)
|
|
|
d69b2b |
else:
|
|
|
d69b2b |
r_done = True # No more pages available
|
|
|
d69b2b |
else:
|
|
|
d69b2b |
@@ -1426,6 +1443,9 @@ def init_online_params(args):
|
|
|
d69b2b |
# prompt for password
|
|
|
d69b2b |
opts['bindpw'] = getpass.getpass('Enter password: ')
|
|
|
d69b2b |
|
|
|
d69b2b |
+ # lastly handle the timeout
|
|
|
d69b2b |
+ opts['timeout'] = int(args.timeout)
|
|
|
d69b2b |
+
|
|
|
d69b2b |
return opts
|
|
|
d69b2b |
|
|
|
d69b2b |
|
|
|
d69b2b |
@@ -1553,6 +1573,8 @@ def main():
|
|
|
d69b2b |
state_parser.add_argument('-y', '--pass-file', help='A text file containing the clear text password for the bind dn', dest='pass_file', default=None)
|
|
|
d69b2b |
state_parser.add_argument('-Z', '--cert-dir', help='The certificate database directory for secure connections',
|
|
|
d69b2b |
dest='certdir', default=None)
|
|
|
d69b2b |
+ state_parser.add_argument('-t', '--timeout', help='The timeout for the LDAP connections. Default is no timeout.',
|
|
|
d69b2b |
+ type=int, dest='timeout', default=-1)
|
|
|
d69b2b |
|
|
|
d69b2b |
# Online mode
|
|
|
d69b2b |
online_parser = subparsers.add_parser('online', help="Compare two online replicas for differences")
|
|
|
d69b2b |
@@ -1577,6 +1599,8 @@ def main():
|
|
|
d69b2b |
online_parser.add_argument('-p', '--page-size', help='The paged-search result grouping size (default 500 entries)',
|
|
|
d69b2b |
dest='pagesize', default=500)
|
|
|
d69b2b |
online_parser.add_argument('-o', '--out-file', help='The output file', dest='file', default=None)
|
|
|
d69b2b |
+ online_parser.add_argument('-t', '--timeout', help='The timeout for the LDAP connections. Default is no timeout.',
|
|
|
d69b2b |
+ type=int, dest='timeout', default=-1)
|
|
|
d69b2b |
|
|
|
d69b2b |
# Offline LDIF mode
|
|
|
d69b2b |
offline_parser = subparsers.add_parser('offline', help="Compare two replication LDIF files for differences (LDIF file generated by 'db2ldif -r')")
|
|
|
d69b2b |
--
|
|
|
d69b2b |
2.26.2
|
|
|
d69b2b |
|