diff --git a/attributs.py b/attributs.py index accbcc6..3501199 100644 --- a/attributs.py +++ b/attributs.py @@ -141,7 +141,7 @@ class AttrsDict(dict): def __getitem__(self, attr): values = super(AttrsDict, self).__getitem__(attr) if not isinstance(values, list): - values = [ values ] + values = [values] output = [] for val in values: output.append(attrify(val, attr, self._conn, self._parent)) @@ -699,11 +699,9 @@ class ip6HostNumber(Attr): ldap_name = "ip6HostNumber" def parse_value(self, ip6): - if self.parent != None: - ip = ip6_of_mac(str(self.parent['macAddress'][0]), int(str(self.parent['rid'][0]))) - else: - ip = ip6 - self.value = netaddr.ip.IPAddress(ip) + if ip6 == '': + ip6 = ip6_of_mac(str(self.parent['macAddress'][0]), int(str(self.parent['rid'][0]))) + self.value = netaddr.IPAddress(ip6) def __unicode__(self): return unicode(self.value) @@ -726,7 +724,7 @@ class mid(Attr): @crans_attribute class rid(Attr): singlevalue = True - optional = False + optional = True unique = True legend = u"Identifiant réseau de machine" category = 'id' diff --git a/crans_utils.py b/crans_utils.py index a487484..b47f4d9 100644 --- a/crans_utils.py +++ b/crans_utils.py @@ -70,6 +70,8 @@ def ip4_of_rid(rid): """Convertit un rid en son IP associée""" # Au cas où rid = int(rid) + if rid == -1: + return u"" net, plage = find_rid_plage(rid) if net == 'Inconnu': @@ -83,10 +85,13 @@ def ip4_of_rid(rid): try: return netaddr.IPAddress(netaddr.IPNetwork(config.NETs[net][0]).first + rid - plage[0]) except KeyError: - raise EnvironmentError("Les machines v6-only ne peuvent pas avoir d'ipv4 (%s)" % (net)) + return u"" def rid_of_ip4(ipv4): """Convertit une ipv4 en rid, si possible""" + if ipv4 == "": + return -1 + # Est-ce une machine spéciale ? for (rid, ip) in config.rid_machines_speciales.iteritems(): if str(ipv4) == ip: @@ -111,8 +116,8 @@ def prefixev6_of_rid(rid): if net == 'Inconnu': raise ValueError("Rid dans aucune plage: %d" % rid) - # fil-v6 ou wifi-v6, we don't care - return netaddr.IPAddress(netaddr.IPNetwork(config.prefix['fil-v6'][0]).first + 2**64*rid) + # adherents-v6 ou wifi-adh-v6, we don't care + return netaddr.IPAddress(netaddr.IPNetwork(config.prefix['adherents-v6'][0]).first + 2**64*rid) def ip6_of_mac(mac, rid): """ @@ -120,6 +125,8 @@ def ip6_of_mac(mac, rid): """ # Au cas où rid = int(rid) + if rid == -1: + return u"" net, plage = find_rid_plage(rid) if net == 'Inconnu': @@ -132,7 +139,7 @@ def ip6_of_mac(mac, rid): # hex retourne un str, donc on concatène, suivant la RFC euid64v6 = hex(int(mac[:2], 16)^0b00000010) + mac[2:6] + u'fffe' + mac[6:12] - # fil-v6 ou wifi-v6, we don't care + # adherents-v6 ou wifi-adh-v6, we don't care if net != "special": return netaddr.IPAddress(netaddr.IPNetwork(config.prefix[net][0]).first + int(euid64v6, 16)) else: diff --git a/lc_ldap.py b/lc_ldap.py index 0bbde0c..afaf01f 100644 --- a/lc_ldap.py +++ b/lc_ldap.py @@ -225,7 +225,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): def newMachine(self, parent, realm, mldif, login=None): """Crée une nouvelle machine: ``realm`` peut être: - fil, fil-v6, wifi, wifi-v6, adm, gratuit, personnel-ens, special + fil, adherents-v6, wifi, wifi-adh-v6, adm, gratuit, personnel-ens, special mldif est un uldif pour la machine --Partiellement implémenté""" # On ne veut pas modifier mldif directement @@ -243,22 +243,20 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): uldif['objectClass'] = [u'borneWifi'] assert isinstance(owner, objets.AssociationCrans) - elif realm in ["wifi-adh", "wifi-v6"]: + elif realm in ["wifi-adh", "wifi-adh-v6"]: uldif['objectClass'] = [u'machineWifi'] assert isinstance(owner, objets.adherent) or isinstance(owner, objets.club) - elif realm in ["adherents", "fil-v6", "personnel-ens"]: + elif realm in ["adherents", "adherents-v6", "personnel-ens"]: uldif['objectClass'] = [u'machineFixe'] assert isinstance(owner, objets.adherent) or isinstance(owner, objets.club) else: raise ValueError("Realm inconnu: %r" % realm) - # On récupère la plage des mids - plage = itertools.chain(*[xrange(a,b+1) for (a,b) in config.rid_primaires[realm]]) # 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', plage)) ]) + rid = uldif.setdefault('rid', [unicode(self._find_id('rid', realm))]) # La machine peut-elle avoir une ipv4 ? if 'v6' not in realm: @@ -307,9 +305,9 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): '''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. for key, values in uldif.iteritems(): - if key.endswith('id'): - continue attribs = [attributs.attrify(val, key, self) for val in values] for attribut in attribs: if attribut.unique: @@ -323,12 +321,17 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): self.lockholder.removelock(key, str(attribut)) raise - def _find_id(self, attr, plage=None): + def _find_id(self, attr, realm=None): '''Trouve un id libre. Si une plage est fournie, cherche l'id dans celle-ci, sinon, prend le plus élevé possible.''' res = self.search_s(variables.base_dn, ldap.SCOPE_SUBTREE, '%s=*' % attr, attrlist = [attr]) nonfree = [ int(r[1].get(attr)[0]) for r in res if r[1].get(attr) ] nonfree.sort() + + plage = None + # On récupère la plage des mids + if realm != None: + plage = itertools.chain(*[xrange(a,b+1) for (a,b) in config.rid_primaires[realm]]) if plage != None: for i in plage: @@ -342,6 +345,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): else: try: self.lockholder.addlock(attr, str(i)) + self.lockholder.removelock(attr, str(i)) break except: continue @@ -353,6 +357,7 @@ class lc_ldap(ldap.ldapobject.LDAPObject, object): while True: try: self.lockholder.addlock(attr, str(i)) + self.lockholder.removelock(attr, str(i)) break except ldap_locks.LockError: i += 1 diff --git a/objets.py b/objets.py index 7879dbb..119d92d 100644 --- a/objets.py +++ b/objets.py @@ -190,6 +190,18 @@ class CransLdapObject(object): faite""" pass + def check_changes(self): + """ + Vérifie la consistence d'un objet + """ + pass + + def validate_changes(self): + """ + Après vérification, harmonise l'objet + """ + pass + def create(self, login=None): """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 @@ -263,9 +275,12 @@ 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() except: # On nettoie les locks self.conn.lockholder.purge(id(self)) + self.conn.lockholder.purge() self._modifs = self.attrs raise EnvironmentError("Impossible de modifier l'objet, peut-être n'existe-t-il pas ?") @@ -350,15 +365,16 @@ class CransLdapObject(object): # sert à permettre les vérifications de cardinalité # (on peut pas utiliser self._modifs, car il ne faut # faire le changement que si on peut) - attrs_before_verif = [ attributs.attrify(val, attr, self.conn, Parent=self) for val in values ] if attr in self.attrs.keys(): for attribut in attrs_before_verif: - attribut.check_uniqueness([content.value for content in self.attrs[attr]]) + attribut.check_uniqueness([unicode(content) for content in self.attrs[attr]]) # On groupe les attributs précédents, et les nouveaux mixed_attrs = attrs_before_verif + self.attrs[attr] else: + for attribut in attrs_before_verif: + attribut.check_uniqueness([]) mixed_attrs = attrs_before_verif # Si c'est vide, on fait pas de vérifs, on avait une liste # vide avant, puis on en a une nouvelle après. @@ -369,7 +385,11 @@ class CransLdapObject(object): self._modifs[attr] = attrs_before_verif for attribut in attrs_before_verif: if attribut.unique: - self.conn.lockholder.addlock(attr, str(attribut), id(self)) + try: + self.conn.lockholder.addlock(attr, str(attribut), id(self)) + except: + self._modifs[attr] = list(self.attrs[attr]) + raise def search_historique(self, ign_fields=HIST_IGNORE_FIELDS): u"""Récupère l'historique @@ -632,8 +652,85 @@ class machine(CransLdapObject): if self._proprio._machines is not None: self._proprio._machines.remove(self) + def check_changes(self): + old = {} + new = {} + sbm = {'rid' : (), 'ipHostNumber' : (), 'ip6HostNumber' : ()} + default = {'rid': -1, 'ipHostNumber': u'', 'macAddress': u''} + for i in ['rid', 'ipHostNumber', 'macAddress']: + try: + old[i] = self.attrs[i][0].value + except: + old[i] = default[i] + try: + new[i] = self._modifs[i][0].value + except: + new[i] = default[i] + if old['rid'] != new['rid']: + nip4 = unicode(crans_utils.ip4_of_rid(new['rid'])) + oip4 = unicode(new['ipHostNumber']) + if oip4 != nip4: + sbm['ipHostNumber'] = (oip4, nip4) + nip6 = unicode(crans_utils.ip6_of_mac(new['macAddress'], new['rid'])) + try: + oip6 = unicode(self._modifs['ip6HostNumber'][0]) + except: + oip6 = u"" + if oip6 != nip6: + sbm['ip6HostNumber'] = (oip6, nip6) + return sbm + elif unicode(old['ipHostNumber']) != unicode(new['ipHostNumber']): + nrid = crans_utils.rid_of_ip4(new['ipHostNumber']) + orid = new['rid'] + if nrid != orid: + sbm['rid'] = (orid, nrid) + return sbm + elif old['macAddress'] != new['macAddress']: + nip6 = unicode(crans_utils.ip6_of_mac(new['macAddress'], new['rid'])) + try: + oip6 = unicode(self._modifs['ip6HostNumber'][0]) + except: + oip6 = u"" + if oip6 != nip6: + sbm['ip6HostNumber'] = (oip6, nip6) + return sbm + + def validate_changes(self): + sbm = self.check_changes() + if sbm['rid']: + if sbm['rid'][1] == -1: + try: + ip6 = unicode(self._modifs['ip6HostNumber'][0]) + except: + ip6 = u"" + if ip6 != u"": + realm = crans_utils.find_rid_plage(sbm['rid'][0])[0] + if 'v6' not in realm: + realm = realm + "-v6" + self['rid'] = [unicode(self.conn._find_id('rid', realm))] + self['ip6HostNumber'] = [unicode(crans_utils.ip6_of_mac(self['macAddress'][0].value, self['rid'][0].value))] + else: + self['ipHostNumber'] = [] + self['ip6HostNumber'] = [] + else: + if unicode(self['ipHostNumber'][0]) != unicode(ip4_of_rid(sbm['rid'][1])): + raise ValueError("L'ipv4 et le rid ne concordent pas !") + self['ip6HostNumber'] = [unicode(crans_utils.ip6_of_mac(self['macAddress'][0].value, self['rid'][0].value))] + if sbm['ipHostNumber']: + if sbm['ipHostNumber'][1] == u"": + ip4 = [] + else: + ip4 = sbm['ipHostNumber'][1] + self['ipHostNumber'] = ip4 + if sbm['ip6HostNumber']: + if sbm['ip6HostNumber'][1] == u"": + ip6 = [] + else: + ip6 = sbm['ip6HostNumber'][1] + self['ip6HostNumber'] = ip6 + class AssociationCrans(proprio): - u""" Association crans (propriétaire particulier).""" + """ Association crans (propriétaire particulier).""" def save(self): pass @@ -647,8 +744,6 @@ class BaseInvites(proprio): u"""Un artefact de la base ldap""" def delete(self, comm, login): raise EnvironmentError("Les pauvres invites") - pass - @crans_object class adherent(proprio): diff --git a/test.py b/test.py index 0947720..a8e7d52 100755 --- a/test.py +++ b/test.py @@ -201,8 +201,8 @@ except Exception: else: print OK - tests_machines(adherent.dn, ["adherents", "fil-v6", "personnel-ens"]) - tests_machines(adherent.dn, ["wifi-adh", "wifi-v6"], ipsec=True) + tests_machines(adherent.dn, ["adherents", "adherents-v6", "personnel-ens"]) + tests_machines(adherent.dn, ["wifi-adh", "wifi-adh-v6"], ipsec=True)