From 7ad48bb0dcb3717b1ed51599da94b2667a490fc9 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Tue, 1 Nov 2022 15:44:15 +0100 Subject: [PATCH] Nearly finished bin/mirror-ldap-instance --- etc/mirror-ldap.yaml | 8 ++ lib/pp_admintools/app/mirror_ldap.py | 148 ++++++++++++++++++++++++++- 2 files changed, 154 insertions(+), 2 deletions(-) diff --git a/etc/mirror-ldap.yaml b/etc/mirror-ldap.yaml index 9e95101..0750388 100644 --- a/etc/mirror-ldap.yaml +++ b/etc/mirror-ldap.yaml @@ -6,5 +6,13 @@ mirror-ldap: - 'cn=Replication monitor,o=isp' spk-stage: - 'cn=Replication monitor,dc=spk,dc=pixelpark,dc=net' + - 'dnaHostname=prd-ds01-spk.spk.pixelpark.net+dnaPortNum=389,cn=Account UIDs,ou=Ranges,dc=spk,dc=pixelpark,dc=net' + - 'dnaHostname=prd-ds02-spk.spk.pixelpark.net+dnaPortNum=389,cn=Account UIDs,ou=Ranges,dc=spk,dc=pixelpark,dc=net' + - 'dnaHostname=prd-ds01-spk.spk.pixelpark.net+dnaPortNum=389,cn=Group GIDs,ou=Ranges,dc=spk,dc=pixelpark,dc=net' + - 'dnaHostname=prd-ds02-spk.spk.pixelpark.net+dnaPortNum=389,cn=Group GIDs,ou=Ranges,dc=spk,dc=pixelpark,dc=net' + - 'dnaHostname=stage-ds01-spk.spk.pixelpark.net,cn=Account UIDs,ou=Ranges,dc=spk,dc=pixelpark,dc=net' + - 'dnaHostname=stage-ds02-spk.spk.pixelpark.net,cn=Account UIDs,ou=Ranges,dc=spk,dc=pixelpark,dc=net' + - 'dnaHostname=stage-ds01-spk.spk.pixelpark.net,cn=Group GIDs,ou=Ranges,dc=spk,dc=pixelpark,dc=net' + - 'dnaHostname=stage-ds02-spk.spk.pixelpark.net,cn=Group GIDs,ou=Ranges,dc=spk,dc=pixelpark,dc=net' # vim: filetype=yaml diff --git a/lib/pp_admintools/app/mirror_ldap.py b/lib/pp_admintools/app/mirror_ldap.py index d33c4b3..111a2ef 100644 --- a/lib/pp_admintools/app/mirror_ldap.py +++ b/lib/pp_admintools/app/mirror_ldap.py @@ -39,7 +39,7 @@ from .ldap import BaseLdapApplication from ..argparse_actions import NonNegativeItegerOptionAction from ..argparse_actions import LimitedFloatOptionAction -__version__ = '0.7.3' +__version__ = '0.8.1' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -87,6 +87,10 @@ class MirrorLdapApplication(BaseLdapApplication): self.only_struct = False self.mirrored_entries = 0 + self.total_deleted = 0 + self.total_updated = 0 + self.total_created = 0 + self.structural_entr_dns = [] self.non_structural_entr_dns = [] self.keep_entry_dns = [] @@ -263,6 +267,31 @@ class MirrorLdapApplication(BaseLdapApplication): self.clean_tgt_non_struct_entries() self.clean_tgt_struct_entries() self.mirror_struct_entries() + self.mirror_non_struct_entries() + + if not self.quiet: + self.empty_line() + title = _("Changes total:") + print(self.colored(title, 'CYAN')) + self.line(width=len(title), linechar='=', color='CYAN') + self.empty_line() + + msg = ' * ' + ngettext( + "{:>5} entry deleted.", "{:>5} entries deleted.", self.total_deleted).format( + self.total_deleted) + print(msg) + + msg = ' * ' + ngettext( + "{:>5} entry updated.", "{:>5} entries updated.", self.total_updated).format( + self.total_updated) + print(msg) + + msg = ' * ' + ngettext( + "{:>5} entry created.", "{:>5} entries created.", self.total_created).format( + self.total_created) + print(msg) + + self.empty_line() except KeyboardInterrupt: msg = _("Got a {}:").format('KeyboardInterrupt') + ' ' + _("Interrupted on demand.") @@ -475,10 +504,12 @@ class MirrorLdapApplication(BaseLdapApplication): self.empty_line() self.delete_entry(self.tgt_instance, dn) count += 1 + self.total_deleted += 1 if self.wait_after_write and not self.simulate: time.sleep(self.wait_after_write) if count: + self.empty_line() msg = ngettext( "Removed one not structural entry in target LDAP instance.", "Removed {no} not structural entries in target LDAP instance.", @@ -529,11 +560,13 @@ class MirrorLdapApplication(BaseLdapApplication): self.empty_line() self.delete_entry(self.tgt_instance, dn) + self.total_deleted += 1 count += 1 if self.wait_after_write and not self.simulate: time.sleep(self.wait_after_write) if count: + self.empty_line() msg = ngettext( "Removed one structural entry in target LDAP instance.", "Removed {no} structural entries in target LDAP instance.", @@ -544,7 +577,7 @@ class MirrorLdapApplication(BaseLdapApplication): # ------------------------------------------------------------------------- def mirror_struct_entries(self): - """Mirroring all structurale entries.""" + """Mirroring all structural entries.""" self.empty_line() self.line(color='CYAN') LOG.info(_("Mirroring structural entries from source to target LDAP instance.")) @@ -561,9 +594,18 @@ class MirrorLdapApplication(BaseLdapApplication): if self.verbose: self.empty_line() + + if dn in self.keep_entry_dns: + LOG.debug(_("Entry {!r} is set to be kept.").format(dn)) + continue + LOG.debug(_("Mirroring entry {!r} ...").format(dn)) src_entry = self.get_entry(dn, self.src_instance, attributes) + if not src_entry: + msg = _("Did not found {!r} in the source LDAP.").format(dn) + LOG.warn(msg) + continue src_attribs = self.normalized_attributes(src_entry) src_oclasses = src_attribs['objectClass'].as_list() src_attribs_dict = src_attribs.dict() @@ -591,6 +633,9 @@ class MirrorLdapApplication(BaseLdapApplication): self.modify_entry(self.tgt_instance, dn, changes) self.mirrored_entries += 1 count += 1 + self.total_updated += 1 + if self.wait_after_write and not self.simulate: + time.sleep(self.wait_after_write) else: LOG.debug(_("No changes necessary on DN {!r}.").format(dn)) continue @@ -607,11 +652,14 @@ class MirrorLdapApplication(BaseLdapApplication): self.add_entry(self.tgt_instance, dn, object_classes, target_entry) self.mirrored_entries += 1 count += 1 + if self.wait_after_write and not self.simulate: + time.sleep(self.wait_after_write) if self.limit and self.mirrored_entries >= self.limit: break if count: + self.empty_line() msg = ngettext( "Mirrored one structural entry in target LDAP instance.", "Mirrored {no} structural entries to target LDAP instance.", @@ -620,6 +668,102 @@ class MirrorLdapApplication(BaseLdapApplication): msg = _("Mirrored no structural entries to target LDAP instance.") LOG.info(msg) + # ------------------------------------------------------------------------- + def mirror_non_struct_entries(self): + """Mirroring all non-structural entries.""" + self.empty_line() + self.line(color='CYAN') + LOG.info(_("Mirroring non-structural entries from source to target LDAP instance.")) + if not self.quiet: + time.sleep(2) + + dns = sorted(list(self.src_dns.keys()), key=cmp_to_key(self.compare_ldap_dns)) + + count = 0 + + attributes = [ALL_ATTRIBUTES, 'aci'] + + for dn in dns: + + if dn in self.src_struct_dns: + continue + + if dn in self.keep_entry_dns: + LOG.debug(_("Entry {!r} is set to be kept.").format(dn)) + continue + + if self.verbose: + self.empty_line() + LOG.debug(_("Mirroring entry {!r} ...").format(dn)) + + src_entry = self.get_entry(dn, self.src_instance, attributes) + if not src_entry: + msg = _("Did not found {!r} in the source LDAP.").format(dn) + LOG.warn(msg) + continue + src_attribs = self.normalized_attributes(src_entry) + src_oclasses = src_attribs['objectClass'].as_list() + src_attribs_dict = src_attribs.dict() + src_attribs_dict['objectClass'] = src_oclasses + + if self.verbose > 1: + LOG.debug("Got source entry:\n" + pp(src_attribs_dict)) + + tgt_entry = self.get_entry(dn, self.tgt_instance, attributes) + if tgt_entry: + tgt_attribs = self.normalized_attributes(tgt_entry) + tgt_oclasses = tgt_attribs['objectClass'].as_list() + tgt_attribs_dict = tgt_attribs.dict() + tgt_attribs_dict['objectClass'] = tgt_oclasses + + if self.verbose > 1: + LOG.debug("Got target entry:\n" + pp(tgt_attribs_dict)) + + changes = self.generate_modify_data(dn, src_attribs, tgt_attribs) + if changes: + self.empty_line() + LOG.info(_("Modifying entry {!r} ...").format(dn)) + msg = _("Got modify data for DN {!r}:").format(dn) + LOG.debug(msg + '\n' + pp(changes)) + self.modify_entry(self.tgt_instance, dn, changes) + self.mirrored_entries += 1 + count += 1 + self.total_updated += 1 + if self.wait_after_write and not self.simulate: + time.sleep(self.wait_after_write) + else: + LOG.debug(_("No changes necessary on DN {!r}.").format(dn)) + continue + + else: + LOG.debug(_("Target entry {!r} not found.").format(dn)) + (object_classes, target_entry) = self.generate_create_entry(src_attribs) + self.empty_line() + LOG.info(_("Creating entry {!r} ...").format(dn)) + msg = _("Got create data for DN {!r}:").format(dn) + msg += '\nobjectClasses:\n' + pp(object_classes) + msg += "\nAttributes:\n" + pp(target_entry) + LOG.debug(msg) + self.add_entry(self.tgt_instance, dn, object_classes, target_entry) + self.mirrored_entries += 1 + count += 1 + self.total_created += 1 + if self.wait_after_write and not self.simulate: + time.sleep(self.wait_after_write) + + if self.limit and self.mirrored_entries >= self.limit: + break + + if count: + self.empty_line() + msg = ngettext( + "Mirrored one none structural entry in target LDAP instance.", + "Mirrored {no} none structural entries to target LDAP instance.", + count).format(no=count) + else: + msg = _("Mirrored non structural entries to target LDAP instance.") + LOG.info(msg) + # ============================================================================= if __name__ == "__main__": -- 2.39.5