diff --git a/lc_ldap.py b/lc_ldap.py index 8c136a4..4d1c28f 100644 --- a/lc_ldap.py +++ b/lc_ldap.py @@ -372,18 +372,20 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): données encodées.''' # Ajout des locks, on instancie les attributs qui ne sont pas # des id, ceux-ci étant déjà lockés. - for key, values in uldif.iteritems(): - attribs = [attributs.attrify(val, key, self) for val in values] - for attribut in attribs: - if attribut.unique: - self.lockholder.addlock(key, str(attribut)) + Id=None try: - return objets.new_cransldapobject(self, dn, 'rw', uldif) - except ldap_locks.LockError: + obj = objets.new_cransldapobject(self, dn, 'rw', uldif) + Id = obj.lockId for key, values in uldif.iteritems(): attribs = [attributs.attrify(val, key, self) for val in values] for attribut in attribs: - self.lockholder.removelock(key, str(attribut)) + if attribut.unique: + self.lockholder.addlock(key, str(attribut), Id=Id) + return obj + except ldap_locks.LockError: + # On supprime seulement les locks que l'on vient de poser + if Id is not None: + self.lockholder.purge(Id) raise def _find_id(self, attr, realm=None): diff --git a/objets.py b/objets.py index 1282233..13f33fb 100644 --- a/objets.py +++ b/objets.py @@ -60,6 +60,7 @@ if not "/usr/scripts/" in sys.path: sys.path.append('/usr/scripts/') import gestion.config as config +import cranslib.deprecated #: Champs à ignorer dans l'historique HIST_IGNORE_FIELDS = ["modifiersName", "entryCSN", "modifyTimestamp", "historique"] @@ -105,6 +106,10 @@ class CransLdapObject(object): attribs = [] + @property + def lockId(self): + return '%s_%s' % (os.getpid(), id(self)) + def __init__(self, conn, dn, mode='ro', uldif=None): ''' Créée une instance d'un objet Crans (machine, adhérent, @@ -114,6 +119,7 @@ class CransLdapObject(object): if not isinstance(conn, lc_ldap.lc_ldap): raise TypeError("conn doit être une instance de lc_ldap") + self.in_context = False self.conn = conn self.attrs = attributs.AttrsDict(conn, Parent=self) # Contient un dico ldif qui doit représenter ce qui @@ -162,6 +168,27 @@ class CransLdapObject(object): nvals = [nldif[attr][vals.index(v)] for v in vals ] raise EnvironmentError("λv. str(Attr(v)) n'est peut-être pas une projection (ie non idempotente):", attr, nvals, vals) + + def _out_of_context(self, *args, **kwargs): + raise EnvironmentError("Hors du context, impossible de faire des écritures") + + def __exit__(self, exc_type, exc_value, traceback): + # Sortie du context manager + self.in_context = False + # On rend les écriture impossible + self.save = self._out_of_context + self.create = self._out_of_context + self.delete = self._out_of_context + # On retombe en read only + self.mode = 'ro' + # On purge les lock de l'objet + self.conn.lockholder.purge(self.lockId) + + def __enter__(self): + # On est dans un context, normalement, c'est locksafe + self.in_context = True + return self + def __ne__(self, obj): return not self == obj def __eq__(self, obj): @@ -268,6 +295,9 @@ class CransLdapObject(object): """Crée l'objet dans la base ldap, cette méthode vise à faire en sorte que l'objet se crée lui-même, si celui qui essaye de le modifier a les droits de le faire.""" + if not self.in_context: + # forcer l'utilisation d'un context manager permet d'être certain que les locks seront libéré quoi qu'il arrive + cranslib.deprecated.usage("Les nouveaux objets ne devrait être initialisés qu'avec des contexts managers (with func() as variable)", level=2) try: if login is None: login = self.conn.current_login @@ -302,13 +332,7 @@ class CransLdapObject(object): return finally: # On nettoie les locks - for key, values in self._modifs.to_ldif().iteritems(): - for value in values: - try: - self.conn.lockholder.removelock(key, value) - except: - pass - self.conn.lockholder.purge(id(self)) + self.conn.lockholder.purge(self.lockId) # Services à relancer services.services_to_restart(self.conn, {}, self._modifs, created_object=[self]) @@ -344,9 +368,12 @@ class CransLdapObject(object): raise EnvironmentError("Objet en lecture seule, réessayer en lecture/écriture") if not self.may_be(variables.deleted, self.conn.droits): raise EnvironmentError("Vous n'avez pas le droit de supprimer %s." % self.dn) + if not self.in_context: + # forcer l'utilisation d'un context manager permet d'être certain que les locks seront libéré quoi qu'il arrive + cranslib.deprecated.usage("La suppression d'un objet ne devrait être faite qu'avec des contexts managers (with func() as variable)", level=2) self.bury(comm, login) self.conn.delete_s(self.dn) - self.conn.lockholder.purge(id(self)) + self.conn.lockholder.purge(self.lockId) self._post_deletion() services.services_to_restart(self.conn, self.attrs, {}, deleted_object=[self]) @@ -356,6 +383,9 @@ class CransLdapObject(object): enregistre les modifications.""" if self.mode not in ['w', 'rw']: raise EnvironmentError("Objet en lecture seule, réessayer en lecture/écriture") + if not self.in_context: + # forcer l'utilisation d'un context manager permet d'être certain que les locks seront libéré quoi qu'il arrive + cranslib.deprecated.usage("Les écritures sur un objet ne devrait être faite qu'avec des contexts managers (with func() as variable)", level=2) self._check_optionnal(comment="modifiez") @@ -363,12 +393,10 @@ class CransLdapObject(object): modlist = self.get_modlist() try: self.conn.modify_s(self.dn, modlist) - self.conn.lockholder.purge(id(self)) - self.conn.lockholder.purge() + self.conn.lockholder.purge(self.lockId) except Exception as error: # On nettoie les locks - self.conn.lockholder.purge(id(self)) - self.conn.lockholder.purge() + self.conn.lockholder.purge(self.lockId) self._modifs = self.attrs raise EnvironmentError("Impossible de modifier l'objet, peut-être n'existe-t-il pas ? %r" % error) @@ -492,7 +520,7 @@ class CransLdapObject(object): for attribut in attrs_before_verif: if attribut.unique: try: - self.conn.lockholder.addlock(attr, str(attribut), id(self)) + self.conn.lockholder.addlock(attr, str(attribut), self.lockId) except: self._modifs[attr] = list(self.attrs[attr]) raise