def check_all(cls): '''Perform consistency checks. Each LDAP class may define a method check(). When this method is run, all of the check() methods appropriate to each object in the tree are run. If any "assert"s fail, a detailed error message is given''' try: lc.search(cls.cls_dn_str, ldap.SCOPE_BASE, None, []) except Exception, e: lerr("LDAP object of class %s does not exist: %s" % (cls, e))
method(obj) except Exception, e: lerr("%s check of object %s\ failed: %s" % (c, obj, e)) if isinstance(e, AssertionError): try: import traceback (file, line, meth, src) = \ traceback.extract_tb( sys.exc_info()[2])[-1] lerr(" %s:%d: %s failed" % (file, line, src)) except Exception, e: pass except Exception, e: lerr("Can't iterate over objects of class %s: %s" % (cls, e)) def all_objs(cls): '''Return all instances of this class (or subclasses) stored in the LDAP tree''' return cls.search(SearchFilter.match_everything()) def __len__(cls): '''Number of instances of this class (or subclasses) stored in the LDAP tree''' return sum(1 for x in cls.all_objs()) def __iter__(cls): '''Iterate over instances of this class (or subclasses) stored in the LDAP tree''' for i in cls.all_objs():
class LDAPClass(type): '''Classes which are mapped to LDAP. LDAPClass is used as a metaclass for LDAP-mapped classes. So, a class User might subclass LDAPObject and would be an instance of LDAPClass. This metaclass defines operations which can be performed on LDAP *classes*, such as creation (not only must Python constructors run, but LDAP operations must be performed), searching (searching for all users is a static method of User which is defined in LDAPClass), and so on.''' _classmap = {} def __init__(cls, name, bases, dict): '''The constructor. Since this is a metaclass, this method is called when new *classes* are created, such as during subclassing. This method ensures single-inheritance and keeps track of the tree of subclasses and where they fit into the LDAP tree''' if len(bases) != 1: raise TypeError("LDAP-mapped classes only\ support single-inheritance") super(LDAPClass, cls).__init__(name, bases, dict) # skip this for LDAPObject if bases[0] is not object: # rebuild DN -> class mapping LDAPClass._classmap = {} LDAPObject._update_class_tree(()) def _update_class_tree(cls, dnsuffix): if cls is LDAPObject: assert (len(dnsuffix) == 0) dn = () elif 'base_dn' in cls.__dict__ and cls.base_dn is not None: dn = dn_to_tuple(cls.base_dn) # dn must end with dnsuffix if len(dnsuffix) > 0 and not dn[-len(dnsuffix):] == dnsuffix: raise TypeError("Invalid DN %s for class %s\ (must be under %s)" % (dn, cls.__name__, dnsuffix)) else: # classes are mapped to organizationalUnits dn = (("ou", cls.__name__), ) + dnsuffix cmap = LDAPClass._classmap if dn in cmap: raise TypeError("Duplicate classes (%s and %s)\ for DN %s" % (cls.__name__, cmap[dn], dn)) ldebug("rebuilding classmap: %s has base dn %s" % (cls.__name__, dn)) cmap[dn] = cls for subclass in cls.__subclasses__(): subclass._update_class_tree(dn) cls.cls_dn_tuple = dn cls.cls_dn_str = tuple_to_dn(dn) @staticmethod def classmap_as_graphviz(): cmap = LDAPClass._classmap parent = {} name = {} maxid = 1 id = {} for dn in cmap: if dn == (): continue for i in range(1, len(dn)): if dn[i:] in cmap: parent[dn] = dn[i:] name[dn] = tuple_to_dn(dn[0:i]) id[dn] = maxid maxid += 1 break else: name[dn] = tuple_to_dn(dn) id[dn] = maxid maxid += 1 retdata = [] p = retdata.append p("digraph {") for dn in cmap: if dn != (): p('n%d [label="%s",shape=box]' % (id[dn], name[dn])) for dn in cmap: if dn in parent: p("n%d -> n%d" % (id[parent[dn]], id[dn])) p("}") return "\n".join(retdata) @staticmethod def get_class_by_dn(dn_str): '''Given a DN in string form, determine the class of the object with that DN''' dn = dn_to_tuple(dn_str) # we check all suffixes of the dn while len(dn) > 0: if dn in LDAPClass._classmap: return LDAPClass._classmap[dn] dn = dn[1:] raise TypeError("No class found for DN %s" % dn_str) @staticmethod def get_object_by_dn(dn_str): '''Given a DN in string form, return the Python object referring to it''' # we get the class and call the constructor, passing the DN return LDAPClass.get_class_by_dn(dn_str)(obj_dn=dn_str) def check_all(cls): '''Perform consistency checks. Each LDAP class may define a method check(). When this method is run, all of the check() methods appropriate to each object in the tree are run. If any "assert"s fail, a detailed error message is given''' try: lc.search(cls.cls_dn_str, ldap.SCOPE_BASE, None, []) except Exception, e: lerr("LDAP object of class %s does not exist: %s" % (cls, e)) try: for obj in cls.all_objs(): # Call *all* the check methods, # not just the most recently-defined for c in type(obj).mro(): method = c.__dict__.get("check") if method: try: method(obj) except Exception, e: lerr("%s check of object %s\ failed: %s" % (c, obj, e)) if isinstance(e, AssertionError): try: import traceback (file, line, meth, src) = \ traceback.extract_tb( sys.exc_info()[2])[-1] lerr(" %s:%d: %s failed" % (file, line, src)) except Exception, e: pass