]> Frank Brehm's Git Trees - pixelpark/pp-admin-tools.git/commitdiff
Nearly finished bin/mirror-ldap-instance
authorFrank Brehm <frank@brehm-online.com>
Tue, 1 Nov 2022 14:44:15 +0000 (15:44 +0100)
committerFrank Brehm <frank@brehm-online.com>
Tue, 1 Nov 2022 14:44:15 +0000 (15:44 +0100)
etc/mirror-ldap.yaml
lib/pp_admintools/app/mirror_ldap.py

index 9e95101c7b753cd862855d990ef1fa9b73df2e08..0750388a876e8e65aeaeb65e2043b76637ed24c1 100644 (file)
@@ -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
index d33c4b35e660c76ed95d399315de7d82b53fedbf..111a2ef868a2359835ed83ebfeba8af218b0a00a 100644 (file)
@@ -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__":