#!/usr/bin/python3 # Usage: dbscan -f /var/lib/dirsrv/slapd-supplier2/db/userroot/entryrdn.db | python3 check_entryrdn # (using the proper instance and backend names ) # Walk all entries in entryrdn index and follow their parent links until hitting the suffix # detecting loops and missing entries import sys import re from dataclasses import dataclass @dataclass class RDNElem: id: int rdn: str nrdn: str def __init__(self, s): res = re.match(r'^ID: (\d+); RDN: "([^"]*)"; NRDN: "([^"]*)"', s) self.id = res.group(1) self.rdn = res.group(2) self.nrdn = res.group(3) def __str__(self): return f"ID: {self.id}; RDN: {self.rdn}; NRDN: {self.nrdn}" db = {} key = None for line in sys.stdin: if key is None: key = line.strip() else: db[key] = RDNElem(line.strip()) key = None # print(db) kp = { k[1:]:v for k,v in db.items() if k.startswith('P') } kc = { k[1:]:v for k,v in db.items() if k.startswith('C') } ke = { k:v for k,v in db.items() if k.isdigit() } ks = { k:v for k,v in db.items() if '=' in k } nbentries = 0 nberr = 0 for k,v in ke.items(): dn = [] ids = [] v1 = v while True: dn.append(v1.rdn) ids.append(v1.id) if len(ids) > 50: print(f'Loop detected: ids={ids} dn: {dn}') nberr += 1 break if v1.id in kp: v1 = kp[v1.id] elif v1.nrdn in ks: sdn = ",".join(dn) # print(f'DN: {sdn}') nbentries += 1 break else: print(f'Missing parent entry detected: ids={ids} dn: {dn}') nberr += 1 break print(f'check_entryrdn processed {nbentries} entries with {nberr} errors')