# rom ..config.ldap import DEFAULT_PORT_LDAP, DEFAULT_PORT_LDAPS
from ..config.ldap import DEFAULT_TIMEOUT
-__version__ = '0.9.0'
+__version__ = '0.10.0'
LOG = logging.getLogger(__name__)
_ = XLATOR.gettext
if result:
result = sorted(result, key=cmp_to_key(self.compare_ldap_dns))
- if self.verbose > 2 and result:
+ if self.verbose > 3 and result:
LOG.debug(_("Result:") + '\n' + pp(result))
return result
+ # -------------------------------------------------------------------------
+ def get_all_entry_dns_hash(self, inst):
+ """Get Object classes and DNs of all entries in the given LDAP instance."""
+
+ connect_info = self.cfg.ldap_connection[inst]
+ base_dn = connect_info.base_dn
+ ldap = self.ldap_connection[inst]
+
+ result = CIDict()
+ attributes = ['objectClass']
+ ldap_filter = '(objectClass=*)'
+
+ req_status, req_result, req_response, req_whatever = ldap.search(
+ search_base=base_dn, search_scope=SUBTREE, search_filter=ldap_filter,
+ get_operational_attributes=False, attributes=attributes,
+ time_limit=self.cfg.ldap_timeout)
+
+ if req_status:
+ if self.verbose > 5:
+ msg = _("Result of searching for DNs of all entries:")
+ LOG.debug(msg + '\n' + pp(req_result))
+ for entry in req_response:
+ if self.verbose > 4:
+ LOG.debug(_("Got a response entry:") + ' ' + pp(entry))
+
+ dn = entry['dn']
+ object_classes = FrozenCIStringSet(entry['attributes']['objectClass'])
+ result[dn] = {
+ 'childs': CIStringSet(),
+ 'dn': dn,
+ 'object_classes': object_classes,
+ 'path': list(reversed(self.re_dn_separator.split(dn))),
+ }
+
+ else:
+ LOG.warn("Got no entry DNs.")
+
+ return result
+
# -------------------------------------------------------------------------
def get_user_dn(self, user, inst):
# Standard modules
import sys
import logging
+import copy
+import time
+
+from functools import cmp_to_key
# Third party modules
# from ldap3 import MODIFY_REPLACE, MODIFY_ADD, MODIFY_DELETE
# Own modules
# from fb_tools.common import to_bool, is_sequence, pp
-# from fb_tools.common import pp
+from fb_tools.common import pp
+# from fb_tools.collections import FrozenCIStringSet, CIStringSet, CIDict
+from fb_tools.collections import CIDict, CIStringSet
from ..xlate import XLATOR
from ..argparse_actions import NonNegativeItegerOptionAction
from ..argparse_actions import LimitedFloatOptionAction
-__version__ = '0.3.0'
+__version__ = '0.4.0'
LOG = logging.getLogger(__name__)
_ = XLATOR.gettext
self.tgt_connect_info = None
self.src_dns = []
- self.tgt_dns_current = []
+ self.tgt_dns_current = CIDict()
+ self.registered_tgt_dns = CIDict()
self.limit = 0
self.wait_after_write = self.default_wait_after_write
self.empty_line()
LOG.info("I'm walking, yes indeed I'm walking ...")
- self.clean_target_instance()
+ try:
+ self.clean_target_instance()
+
+ except KeyboardInterrupt:
+ msg = _("Got a {}:").format('KeyboardInterrupt') + ' ' + _("Interrupted on demand.")
+ LOG.error(msg)
+ self.exit(10)
# -------------------------------------------------------------------------
def clean_target_instance(self):
"(except the base DN entry, of course)."))
self.get_current_tgt_entries()
+ self.clean_tgt_non_struct_entries()
+ self.clean_tgt_struct_entries()
# -------------------------------------------------------------------------
def get_current_tgt_entries(self):
LOG.debug(_("Trying to get DNs of all entries in the target LDAP instance."))
- result = self.get_all_entry_dns(self.tgt_instance)
+ self.tgt_dns_current = self.get_all_entry_dns_hash(self.tgt_instance)
+
+ for dn in sorted(list(self.tgt_dns_current.keys()), key=cmp_to_key(self.compare_ldap_dns)):
+ self.register_dn_tokens(dn, self.tgt_dns_current[dn], self.tgt_dns_current)
+
+ if self.verbose > 4:
+ LOG.debug("Current target entries:\n" + pp(self.tgt_dns_current.dict()))
+
+ # -------------------------------------------------------------------------
+ def register_dn_tokens(self, dn, entry, registry):
+
+ if self.verbose > 4:
+ LOG.debug("Trying to register DN {!r} ...".format(dn))
+
+ parent_tokens = copy.copy(entry['path'])[0:-1]
+ if not parent_tokens:
+ registry[dn]['parent'] = None
+ return
+ parent_dn = ','.join(reversed(parent_tokens))
+ if self.verbose > 4:
+ LOG.debug("Parent DN: {!r}.".format(parent_dn))
+ registry[dn]['parent'] = parent_dn
+ if parent_dn not in registry:
+ if self.verbose > 1:
+ LOG.debug("Entry {!r} seems to be a the root DN.".format(dn))
+ return
+
+ if not 'childs' not in registry[parent_dn]:
+ registry[parent_dn]['childs'] = CIStringSet()
+ registry[parent_dn]['childs'].add(dn)
+
+ # -------------------------------------------------------------------------
+ def clean_tgt_non_struct_entries(self):
+ """Removing all non structural entries in target instance.
+
+ Structural entries are entries without any childs.
+ """
+
+ self.empty_line()
+ LOG.info(_("Removing all non structural entries from target LDAP instance."))
+ if not self.quiet:
+ time.sleep(2)
+ self.empty_line()
+
+ for dn in sorted(list(self.tgt_dns_current.keys()), key=cmp_to_key(self.compare_ldap_dns)):
+ entry = self.tgt_dns_current[dn]
+ if 'childs' not in entry:
+ LOG.error("Found entry {dn!r}:\n{e}".format(dn=dn, e=pp(entry)))
+ self.exit(5)
+ if entry['childs']:
+ if self.verbose > 1:
+ LOG.debug(_(
+ "Entry {!r} is a structural entry, will not be removed "
+ "at this point.").format(dn))
+ continue
+ self.delete_entry(self.tgt_instance, dn)
+ if self.wait_after_write and not self.simulate:
+ time.sleep(self.wait_after_write)
+
+ # -------------------------------------------------------------------------
+ def clean_tgt_struct_entries(self):
+ """Removing all structural entries in target instance.
+
+ Structural entries are entries without any childs.
+ """
+
+ self.empty_line()
+ LOG.info(_("Removing all structural entries from target LDAP instance."))
+ if not self.quiet:
+ time.sleep(2)
+ self.empty_line()
+
+ dns = sorted(list(self.tgt_dns_current.keys()), key=cmp_to_key(self.compare_ldap_dns))
+
+ for dn in list(reversed(dns[1:])):
+ entry = self.tgt_dns_current[dn]
+ if not entry['childs']:
+ continue
+ self.delete_entry(self.tgt_instance, dn)
+ if self.wait_after_write and not self.simulate:
+ time.sleep(self.wait_after_write)
# =============================================================================