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

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