From 4f88ae824a473ec6fbd6be4ad716c82f97a46165 Mon Sep 17 00:00:00 2001 From: Valentin Samir Date: Mon, 24 Feb 2014 21:31:16 +0100 Subject: [PATCH] [ldap_locks] Renommage des erreurs ldap en erreur de Lock, factorisation, reformatage --- ldap_locks.py | 106 +++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/ldap_locks.py b/ldap_locks.py index 76bce28..2c414dd 100644 --- a/ldap_locks.py +++ b/ldap_locks.py @@ -53,6 +53,12 @@ class LdapLockedByYou(LockError): """ pass +class LdapLockedByMySelf(LockError): + """ + Classe d'erreur pour les locks par l'idlock courant + """ + pass + class LdapLockedByOther(LockError): """ Erreur car le lock est occupé par un autre process. @@ -65,6 +71,9 @@ class LockFormatError(LockError): """ pass +class LockNotFound(LockError): + """Le lock n'a pas été trouvé""" + LOCKS_DN = 'ou=lock,dc=crans,dc=org' class LdapLockHolder: @@ -75,36 +84,38 @@ class LdapLockHolder: """ On crée la connexion, et on crée un dico vide. """ - self.locks = collections.defaultdict(dict, {}) + # On crée un Id => (item => set()) + self.locks = collections.defaultdict(lambda:collections.defaultdict(set)) self.host = socket.gethostname() self.pid = os.getpid() self.conn = conn - self.time = 600.0 + self.timeout = 600.0 - def purge(self, Id=None): + def newid(self): + id = 'id_%s' % time.time() + if id in self.locks: + return self.newid() + else: + return id + + def purge(self, Id, purgeAll=False): """ On essaye de détruire tous les verrous hébergés par l'objet. """ - if Id == None: - for key, subdict in self.locks['default'].items(): - for item, value in subdict.items(): - try: - self.removelock(item, value, key) - except: - pass - else: - for item, value in self.locks[Id].items(): - try: + if not purgeAll: + for item, values in self.locks[Id].items(): + for value in values.copy(): self.removelock(item, value, Id) - except: - pass + else: + for Id in self.locks: + self.purge(Id) def __del__(self): """ En cas de destruction du lockholder """ - self.purge() + self.purge(purgeAll=True) def addlock(self, item, value, Id='default'): """ @@ -119,27 +130,23 @@ class LdapLockHolder: """ try: host, pid, begin = self.getlock(item, value) - if time.time() - begin >= self.time: + time_left = self.timeout - (time.time() - begin) + if time_left <= 0: self.removelock(item, value, Id, True) + elif Id!='default' and str(value) in self.locks[Id][item]: + raise LdapLockedByMySelf("La donnée %r=%r est lockée par vous-même pour encore %ds." % (item, value, time_left)) elif host == self.host and pid == self.pid: - time_left = self.time - (time.time() - begin) raise LdapLockedByYou("La donnée %r=%r est lockée par vous-même pour encore %ds." % (item, value, time_left)) elif host == self.host: status = crans_utils.process_status(pid) if status: - time_left = self.time - (time.time() - begin) raise LdapLockedByOther("La donnée %r=%r est lockée par un processus actif pour encore %ds." % (item, value, time_left)) else: self.removelock(item, value, Id, True) else: - time_left = self.time - (time.time() - begin) raise LdapLockedByOther("La donnée %r=%r est lockée depuis une autre machine pour encore %ds." % (item, value, time_left)) - except ldap.NO_SUCH_OBJECT: + except LockNotFound: pass - except LockFormatError: - self.removelock(item, value, Id) - except Exception: - raise dn = "%s=%s,%s" % (item, value, LOCKS_DN) lockid = "%s-%s-%s" % (self.host, self.pid, time.time()) @@ -151,36 +158,28 @@ class LdapLockHolder: try: self.conn.add_s(dn, modlist) - self.locks[Id][item] = value + self.locks[Id][item].add(str(value)) except ldap.ALREADY_EXISTS: - status = crans_utils.process_status(pid) - if status: - raise LdapLockedByOther("La donnée %r=%r est lockée par un processus actif." % (item, value)) - else: - self.removelock(item, value, Id) - try: - self.conn.add_s(dn, modlist) - - # Si on a un pointeur vers l'id de l'objet, on en tient compte - self.locks[Id][item] = value - except: - raise StandardError("Quelque chose a planté durant la pose du lock %s=%s" % (item, value)) + # Quelqu'un à eu le lock avant nous, on réessaye + # S'il a été libéré, banzai, sinon, ça lèvera une exception + return self.addlock(item, value, Id) def removelock(self, item, value, Id='default', force=False): """ Libère le lock "$item=$value,$LOCKS_DN". """ - if not force: - if self.locks[Id].get(item, "") == str(value): - _ = self.locks[Id].pop(item) + try: + if force or str(value) in self.locks[Id][item]: self.conn.delete_s("%s=%s,%s" % (item, value, LOCKS_DN)) - else: - pass - else: + except ldap.NO_SUCH_OBJECT: + pass + finally: try: - self.conn.delete_s("%s=%s,%s" % (item, value, LOCKS_DN)) - except: + self.locks[Id][item].remove(value) + except KeyError: pass + if not self.locks[Id][item]: + self.locks[Id].pop(item) def getlock(self, item, value): """ @@ -188,13 +187,12 @@ class LdapLockHolder: via un triplet host, pid, begin """ - result = self.conn.search_s('%s=%s,%s' % (item, value, LOCKS_DN), 0) try: - try: - host, pid, begin = result[0][1]['lockid'][0].split('-') - except: - host, pid = result[0][1]['lockid'][0].split('-') - begin = time.time() + result = self.conn.search_s('%s=%s,%s' % (item, value, LOCKS_DN), 0) + host, pid, begin = result[0][1]['lockid'][0].split('-') return host, int(pid), float(begin) - except: - raise LockFormatError + except ldap.NO_SUCH_OBJECT: + raise LockNotFound() + except ValueError as e: + self.removelock(item, value, Id, force=True) + raise LockNotFound()