Blame SOURCES/0006-Issue-51102-RFE-ds-replcheck-make-online-timeout-con.patch

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