diff --git a/lc_ldap.py b/lc_ldap.py index 7959f4f..6309e1b 100644 --- a/lc_ldap.py +++ b/lc_ldap.py @@ -166,16 +166,16 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): login = self.current_login (dn, ldif)= self.ressuscite_build_ldif(ldif_file) # On définit de nouveaux dn si ceux-ci sont déjà pris + lockId = self.lockholder.newid() try: if self.search(dn=dn): for id in ["aid", "mid", "fid", "cid", "xid"]: if dn.startswith("%s=" % id): - ldif[id]=[str(self._find_id(id))] + ldif[id]=[str(self._find_id(id, lockId=lockId))] dn="%s=%s,%s" % (id, ldif[id][0], dn.split(',',1)[1]) except ldap.NO_SUCH_OBJECT: pass - obj = objets.new_cransldapobject(self, dn, mode='rw', uldif=ldif_to_uldif(ldif)) - # La vérification des attibuts uniques et de l'existance du dn est faite en appelant create() + obj = self._create_entity(dn, ldif_to_uldif(ldif), lockId) obj.history_add(login, u"resurrection") return obj @@ -281,6 +281,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): login = self.current_login #adm, serveurs, bornes, wifi, adherents, gratuit ou personnel-ens""" owner = self.search(u'objectClass=*', dn=parent, scope=0)[0] + lockId = self.lockholder.newid() if realm in ["adm", "serveurs", "serveurs-v6", "adm-v6"]: uldif['objectClass'] = [u'machineCrans'] @@ -303,7 +304,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): # On récupère le premier id libre dans la plages s'il n'est pas # déjà précisé dans le ldif - rid = uldif.setdefault('rid', [unicode(self._find_id('rid', realm))]) + rid = uldif.setdefault('rid', [unicode(self._find_id('rid', realm, lockId=lockId))]) # La machine peut-elle avoir une ipv4 ? if 'v6' not in realm: @@ -311,10 +312,10 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): uldif['ip6HostNumber'] = [ unicode(crans_utils.ip6_of_mac(uldif['macAddress'][0], int(rid[0]))) ] # Mid - uldif['mid'] = [ unicode(self._find_id('mid')) ] + uldif['mid'] = [ unicode(self._find_id('mid', lockId=lockId)) ] # Tout doit disparaître !! - machine = self._create_entity('mid=%s,%s' % (uldif['mid'][0], parent), uldif) + machine = self._create_entity('mid=%s,%s' % (uldif['mid'][0], parent), uldif, lockId) if machine.may_be(variables.created, self.droits + self._check_parent(machine.dn)): return machine else: @@ -322,10 +323,11 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): def newAdherent(self, aldif): """Crée un nouvel adhérent""" + lockId = self.lockholder.newid() uldif = copy.deepcopy(aldif) - aid = uldif.setdefault('aid', [ unicode(self._find_id('aid')) ]) + aid = uldif.setdefault('aid', [ unicode(self._find_id('aid', lockId=lockId)) ]) uldif['objectClass'] = [u'adherent'] - adherent = self._create_entity('aid=%s,%s' % (aid[0], variables.base_dn), uldif) + adherent = self._create_entity('aid=%s,%s' % (aid[0], variables.base_dn), uldif, lockId) if adherent.may_be(variables.created, self.droits): return adherent else: @@ -333,10 +335,11 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): def newClub(self, cldif): """Crée un nouveau club""" + lockId = self.lockholder.newid() uldif = copy.deepcopy(cldif) - cid = uldif.setdefault('cid', [ unicode(self._find_id('cid')) ]) + cid = uldif.setdefault('cid', [ unicode(self._find_id('cid', lockId=lockId)) ]) uldif['objectClass'] = [u'club'] - club = self._create_entity('cid=%s,%s' % (cid[0], variables.base_dn), uldif) + club = self._create_entity('cid=%s,%s' % (cid[0], variables.base_dn), uldif, lockId) if club.may_be(variables.created, self.droits): return club else: @@ -344,11 +347,12 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): def newFacture(self, parent, fldif): """Crée une nouvelle facture""" + lockId = self.lockholder.newid() uldif = copy.deepcopy(fldif) # fid - uldif['fid'] = [ unicode(self._find_id('fid')) ] + uldif['fid'] = [ unicode(self._find_id('fid', lockId=lockId)) ] uldif['objectClass'] = [u'facture'] - facture = self._create_entity('fid=%s,%s' % (uldif['fid'][0], parent), uldif) + facture = self._create_entity('fid=%s,%s' % (uldif['fid'][0], parent), uldif, lockId) if facture.may_be(variables.created, self.droits + self._check_parent(facture.dn)): return facture else: @@ -356,41 +360,45 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): def newCertificat(self, parent, xldif): """Crée un nouveau certificat x509""" + lockId = self.lockholder.newid() uldif = copy.deepcopy(xldif) # xid - uldif['xid'] = [ unicode(self._find_id('xid')) ] + uldif['xid'] = [ unicode(self._find_id('xid', lockId=lockId)) ] uldif['objectClass'] = [u'baseCert'] - baseCert = self._create_entity('xid=%s,%s' % (uldif['xid'][0], parent), uldif) + baseCert = self._create_entity('xid=%s,%s' % (uldif['xid'][0], parent), uldif, lockId) if baseCert.may_be(variables.created, self.droits + self._check_parent(baseCert.dn)): return baseCert else: raise EnvironmentError("Vous n'avez pas le droit de créer ce certiticat.") - def _create_entity(self, dn, uldif): + def _create_entity(self, dn, uldif, lockId): '''Crée une nouvelle entité ldap avec le dn ``dn`` et les attributs de ``ldif``. Attention, ldif doit contenir des données encodées.''' # Ajout des locks, on instancie les attributs qui ne sont pas # des id, ceux-ci étant déjà lockés. - Id=None try: - 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: if attribut.unique: - self.lockholder.addlock(key, str(attribut), Id=Id) - return obj + try: + self.lockholder.addlock(key, str(attribut), Id=lockId) + except ldap_locks.LdapLockedByMySelf: + # On vient juste d'acquérir le lock dans _find_id, ça n'est pas grâve + pass + return objets.new_cransldapobject(self, dn, 'rw', uldif, lockId=lockId) except ldap_locks.LockError: # On supprime seulement les locks que l'on vient de poser - if Id is not None: - self.lockholder.purge(Id) + self.lockholder.purge(lockId) raise - def _find_id(self, attr, realm=None): + def _find_id(self, attr, realm=None, lockId=None): '''Trouve un id libre. Si une plage est fournie, cherche l'id dans celle-ci, sinon, prend le plus élevé possible.''' + if lockId is None: + raise ValueError("lockId ne devrait pas être à None") + plage = None # On récupère la plage des ids if realm != None: @@ -440,10 +448,9 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): continue else: try: - self.lockholder.addlock(attr, str(i)) - self.lockholder.removelock(attr, str(i)) + self.lockholder.addlock(attr, str(i), lockId) break - except: + except ldap_locks.LockError: continue else: raise EnvironmentError('Aucun %s libre dans la plage [%d, %d]' % @@ -452,13 +459,10 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): i = last_id + 1 while True: try: - self.lockholder.addlock(attr, str(i)) - self.lockholder.removelock(attr, str(i)) + self.lockholder.addlock(attr, str(i), lockId) break except ldap_locks.LockError: i += 1 - except Exception: - raise return i diff --git a/objets.py b/objets.py index 13f33fb..7f7c326 100644 --- a/objets.py +++ b/objets.py @@ -72,7 +72,7 @@ crans_account_attribs = [attributs.uid, attributs.canonicalAlias, attributs.sold attributs.mailAlias, attributs.cn, attributs.rewriteMailHeaders, attributs.mailExt, attributs.compteWiki, attributs.droits] -def new_cransldapobject(conn, dn, mode='ro', uldif=None): +def new_cransldapobject(conn, dn, mode='ro', uldif=None, lockId=None): """Crée un objet :py:class:`CransLdapObject` en utilisant la classe correspondant à l'``objectClass`` du ``ldif`` --pour usage interne à la librairie uniquement !""" @@ -92,7 +92,7 @@ def new_cransldapobject(conn, dn, mode='ro', uldif=None): _, attrs = res[0] classe = ObjectFactory.get(attrs['objectClass'][0]) - return classe(conn, dn, mode, uldif) + return classe(conn, dn, mode, uldif, lockId=lockId) class CransLdapObject(object): """Classe de base des objets :py:class:`CransLdap`. @@ -106,11 +106,7 @@ class CransLdapObject(object): attribs = [] - @property - def lockId(self): - return '%s_%s' % (os.getpid(), id(self)) - - def __init__(self, conn, dn, mode='ro', uldif=None): + def __init__(self, conn, dn, mode='ro', uldif=None, lockId=None): ''' Créée une instance d'un objet Crans (machine, adhérent, etc...) à ce ``dn``, si ``uldif`` est précisé, n'effectue pas de @@ -122,6 +118,11 @@ class CransLdapObject(object): self.in_context = False self.conn = conn + if lockId: + self.lockId = lockId + else: + self.lockId = '%s_%s' % (os.getpid(), id(self)) + self.attrs = attributs.AttrsDict(conn, Parent=self) # Contient un dico ldif qui doit représenter ce qui # est dans la base. On attrify paresseusement au moment où on utilise un attribut @@ -469,7 +470,8 @@ class CransLdapObject(object): return attributs.AttrsList(self, attr, [ v for v in self.attrs[attr] ]) elif self.has_key(attr): return attributs.AttrsList(self, attr, []) if default is None else default - raise KeyError(attr) + else: + raise KeyError(attr) def has_key(self, attr): """Est-ce que notre objet a l'attribut en question ?""" @@ -648,8 +650,8 @@ class proprio(CransLdapObject): def __repr__(self): return str(self.__class__) + " : nom=" + str(self['nom'][0]) - def __init__(self, conn, dn, mode='ro', ldif=None): - super(proprio, self).__init__(conn, dn, mode, ldif) + def __init__(self, *args, **kwargs): + super(proprio, self).__init__(*args, **kwargs) self._machines = None self._factures = None if u'cransAccount' in self['objectClass']: @@ -756,8 +758,8 @@ class machine(CransLdapObject): def __repr__(self): return str(self.__class__) + " : host=" + str(self['host'][0]) - def __init__(self, conn, dn, mode='ro', ldif = None): - super(machine, self).__init__(conn, dn, mode, ldif) + def __init__(self, *args, **kwargs): + super(machine, self).__init__(*args, **kwargs) self._proprio = None self._certificats = None @@ -946,8 +948,8 @@ class adherent(proprio): def __repr__(self): return str(self.__class__) + " : aid=" + str(self['aid'][0]) - def __init__(self, conn, dn, mode='ro', ldif=None): - super(adherent, self).__init__(conn, dn, mode, ldif) + def __init__(self, *args, **kwargs): + super(adherent, self).__init__(*args, **kwargs) self.full = False self._clubs = None self._imprimeur_clubs = None @@ -1125,8 +1127,8 @@ class facture(CransLdapObject): return str(self.__class__) + " : fid=" + str(self['fid'][0]) #### GROS HACK pour rester comptatible avec ldap_crans où l'article representant les frais n'est ajouté qu'une fois le paiement reçu - def __init__(self, conn, dn, mode='ro', ldif=None): - super(facture, self).__init__(conn, dn, mode, ldif) + def __init__(self, *args, **kwargs): + super(facture, self).__init__(*args, **kwargs) self._frais = [] if not self.get('recuPaiement', []): if str(self['modePaiement'][0]) == 'paypal': @@ -1177,13 +1179,13 @@ class baseCert(CransLdapObject): def __repr__(self): return str(self.__class__) + " : xid=" + str(self['xid'][0]) - def __init__(self, conn, dn, mode='ro', ldif=None): - super(baseCert, self).__init__(conn, dn, 'ro', ldif) + def __init__(self, *args, **kwargs): + super(baseCert, self).__init__(*args, **kwargs) if "TLSACert" in self['objectClass']: self.attribs.extend(self.tlsa_attribs) if 'x509Cert' in self['objectClass']: self.attribs.extend(self.x509_attribs) - super(baseCert, self).__init__(conn, dn, mode, ldif) + super(baseCert, self).__init__(*args, **kwargs) def _check_setitem(self, attr, values): """