From 878bf895ce1c470f7f57b0a8f08e8084143df45b Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Jul 12 2018 14:28:54 +0000 Subject: Ticket 49840 - ds-replcheck command returns traceback errors against ldif files having garbage content when run in offline mode Description: Added a basic check to see if the LDIF files are actually LDIF files. Also added checks that the database RUV are present as well. https://pagure.io/389-ds-base/issue/49840 Reviewed by: spichugi(Thanks!) (cherry picked from commit 60cb52040704686d9541a2e2eb2765d86cb10af2) --- diff --git a/ldap/admin/src/scripts/ds-replcheck b/ldap/admin/src/scripts/ds-replcheck index 62f9110..5c195f9 100755 --- a/ldap/admin/src/scripts/ds-replcheck +++ b/ldap/admin/src/scripts/ds-replcheck @@ -10,18 +10,19 @@ # import os +import sys import re import time import ldap import ldapurl import argparse import getpass - +from ldif import LDIFRecordList from ldap.ldapobject import SimpleLDAPObject from ldap.cidict import cidict from ldap.controls import SimplePagedResultsControl -VERSION = "1.3" +VERSION = "1.4" RUV_FILTER = '(&(nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff)(objectclass=nstombstone))' LDAP = 'ldap' LDAPS = 'ldaps' @@ -386,14 +387,17 @@ def ldif_search(LDIF, dn): return result -def get_dns(LDIF, opts): +def get_dns(LDIF, filename, opts): ''' Get all the DN's from an LDIF file ''' dns = [] found = False + found_ruv = False + LDIF.seek(0) for line in LDIF: if line.startswith('dn: ') and line[4:].startswith('nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff'): opts['ruv_dn'] = line[4:].lower().strip() + found_ruv = True elif line.startswith('dn: '): found = True dn = line[4:].lower().strip() @@ -407,6 +411,14 @@ def get_dns(LDIF, opts): found = False dns.append(dn) + if not found_ruv: + print('Failed to find the database RUV in the LDIF file: ' + filename + ', the LDIF ' + + 'file must contain replication state information.') + dns = None + else: + # All good, reset cursor + LDIF.seek(0) + return dns @@ -415,6 +427,7 @@ def get_ldif_ruv(LDIF, opts): ''' LDIF.seek(0) result = ldif_search(LDIF, opts['ruv_dn']) + LDIF.seek(0) # Reset cursor return result['entry'].data['nsds50ruv'] @@ -549,6 +562,7 @@ def do_offline_report(opts, output_file=None): rconflicts = [] rtombstones = 0 mtombstones = 0 + idx = 0 # Open LDIF files try: @@ -561,12 +575,36 @@ def do_offline_report(opts, output_file=None): RLDIF = open(opts['rldif'], "r") except Exception as e: print('Failed to open Replica LDIF: ' + str(e)) + MLDIF.close() + return None + + # Verify LDIF Files + try: + print("Validating Master ldif file ({})...".format(opts['mldif'])) + LDIFRecordList(MLDIF).parse() + except ValueError: + print('Master LDIF file in invalid, aborting...') + MLDIF.close() + RLDIF.close() + return None + try: + print("Validating Replica ldif file ({})...".format(opts['rldif'])) + LDIFRecordList(RLDIF).parse() + except ValueError: + print('Replica LDIF file is invalid, aborting...') + MLDIF.close() + RLDIF.close() return None # Get all the dn's, and entry counts print ("Gathering all the DN's...") - master_dns = get_dns(MLDIF, opts) - replica_dns = get_dns(RLDIF, opts) + master_dns = get_dns(MLDIF, opts['mldif'], opts) + replica_dns = get_dns(RLDIF, opts['rldif'], opts) + if master_dns is None or replica_dns is None: + print("Aborting scan...") + MLDIF.close() + RLDIF.close() + sys.exit(1) m_count = len(master_dns) r_count = len(replica_dns) @@ -575,11 +613,6 @@ def do_offline_report(opts, output_file=None): opts['master_ruv'] = get_ldif_ruv(MLDIF, opts) opts['replica_ruv'] = get_ldif_ruv(RLDIF, opts) - # Reset the cursors - idx = 0 - MLDIF.seek(idx) - RLDIF.seek(idx) - """ Compare the master entries with the replica's. Take our list of dn's from the master ldif and get that entry( dn) from the master and replica ldif. In this phase we keep keep track of conflict/tombstone counts, and we check for