Changeset 1966

Show
Ignore:
Timestamp:
12/02/04 13:34:00 (20 years ago)
Author:
jalet
Message:

Now automates LDAP reconnections if the server dropped the connection due
to a timeout.

Location:
pykota/trunk
Files:
3 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/NEWS

    r1962 r1966  
    2222PyKota NEWS : 
    2323 
     24    - 1.21alpha10 : 
     25     
     26        - Now automatically reconnects up to three times if the LDAP server 
     27          returned an error, possibly due to a timeout. 
     28           
    2429    - 1.21alpha9 : 
    2530     
  • pykota/trunk/pykota/storages/ldapstorage.py

    r1875 r1966  
    2222# 
    2323# $Log$ 
     24# Revision 1.87  2004/12/02 12:34:00  jalet 
     25# Now automates LDAP reconnections if the server dropped the connection due 
     26# to a timeout. 
     27# 
    2428# Revision 1.86  2004/10/25 14:12:25  jalet 
    2529# For URGENT legal reasons (Italy), a new "privacy" directive was added to pykota.conf 
     
    329333except ImportError :     
    330334    import sys 
    331     # TODO : to translate or not to translate ? 
    332335    raise PyKotaStorageError, "This python version (%s) doesn't seem to have the python-ldap module installed correctly." % sys.version.split()[0] 
    333336     
     
    335338    def __init__(self, pykotatool, host, dbname, user, passwd) : 
    336339        """Opens the LDAP connection.""" 
    337         BaseStorage.__init__(self, pykotatool) 
    338         self.info = pykotatool.config.getLDAPInfo() 
    339         try : 
    340             self.database = ldap.initialize(host)  
    341             self.database.simple_bind_s(user, passwd) 
    342             self.basedn = dbname 
    343         except ldap.SERVER_DOWN :     
    344             raise PyKotaStorageError, "LDAP backend for PyKota seems to be down !" # TODO : translate 
    345         except ldap.LDAPError :     
    346             raise PyKotaStorageError, "Unable to connect to LDAP server %s as %s." % (host, user) # TODO : translate 
    347         else :     
    348             self.useldapcache = pykotatool.config.getLDAPCache() 
    349             if self.useldapcache : 
    350                 self.tool.logdebug("Low-Level LDAP Caching enabled.") 
    351                 self.ldapcache = {} # low-level cache specific to LDAP backend 
    352             self.closed = 0 
    353             self.tool.logdebug("Database opened (host=%s, dbname=%s, user=%s)" % (host, dbname, user)) 
     340        self.savedtool = pykotatool 
     341        self.savedhost = host 
     342        self.saveddbname = dbname 
     343        self.saveduser = user 
     344        self.savedpasswd = passwd 
     345        self.secondStageInit() 
     346         
     347    def secondStageInit(self) :     
     348        """Second stage initialisation.""" 
     349        BaseStorage.__init__(self, self.savedtool) 
     350        self.info = self.tool.config.getLDAPInfo() 
     351        message = "" 
     352        for tryit in range(3) : 
     353            try : 
     354                self.database = ldap.initialize(self.savedhost)  
     355                self.database.simple_bind_s(self.saveduser, self.savedpasswd) 
     356                self.basedn = self.saveddbname 
     357            except ldap.SERVER_DOWN :     
     358                message = "LDAP backend for PyKota seems to be down !" 
     359                self.tool.printInfo("%s" % message, "error") 
     360                self.tool.printInfo("Trying again in 2 seconds...", "warn") 
     361                time.sleep(2) 
     362            except ldap.LDAPError :     
     363                message = "Unable to connect to LDAP server %s as %s." % (self.savedhost, self.saveduser) 
     364                self.tool.printInfo("%s" % message, "error") 
     365                self.tool.printInfo("Trying again in 2 seconds...", "warn") 
     366                time.sleep(2) 
     367            else :     
     368                self.useldapcache = self.tool.config.getLDAPCache() 
     369                if self.useldapcache : 
     370                    self.tool.logdebug("Low-Level LDAP Caching enabled.") 
     371                    self.ldapcache = {} # low-level cache specific to LDAP backend 
     372                self.closed = 0 
     373                self.tool.logdebug("Database opened (host=%s, dbname=%s, user=%s)" % (self.savedhost, self.saveddbname, self.saveduser)) 
     374                return # All is fine here. 
     375        raise PyKotaStorageError, message          
    354376             
    355377    def close(self) :     
     
    391413    def doSearch(self, key, fields=None, base="", scope=ldap.SCOPE_SUBTREE, flushcache=0) : 
    392414        """Does an LDAP search query.""" 
    393         try : 
    394             base = base or self.basedn 
    395             if self.useldapcache : 
    396                 # Here we overwrite the fields the app want, to try and 
    397                 # retrieve ALL user defined attributes ("*") 
    398                 # + the createTimestamp attribute, needed by job history 
    399                 #  
    400                 # This may not work with all LDAP servers 
    401                 # but works at least in OpenLDAP (2.1.25)  
    402                 # and iPlanet Directory Server (5.1 SP3) 
    403                 fields = ["*", "createTimestamp"]          
    404                  
    405             if self.useldapcache and (not flushcache) and (scope == ldap.SCOPE_BASE) and self.ldapcache.has_key(base) : 
    406                 entry = self.ldapcache[base] 
    407                 self.tool.logdebug("LDAP cache hit %s => %s" % (base, entry)) 
    408                 result = [(base, entry)] 
    409             else : 
    410                 self.tool.logdebug("QUERY : Filter : %s, BaseDN : %s, Scope : %s, Attributes : %s" % (key, base, scope, fields)) 
    411                 result = self.database.search_s(base, scope, key, fields) 
    412         except ldap.NO_SUCH_OBJECT, msg :         
    413             raise PyKotaStorageError, (_("Search base %s doesn't seem to exist. Probable misconfiguration. Please double check /etc/pykota/pykota.conf : %s") % (base, msg)) 
    414         except ldap.LDAPError, msg :     
    415             raise PyKotaStorageError, (_("Search for %s(%s) from %s(scope=%s) returned no answer.") % (key, fields, base, scope)) + " : %s" % str(msg) 
    416         else :      
    417             self.tool.logdebug("QUERY : Result : %s" % result) 
    418             if self.useldapcache : 
    419                 for (dn, attributes) in result : 
    420                     self.tool.logdebug("LDAP cache store %s => %s" % (dn, attributes)) 
    421                     self.ldapcache[dn] = attributes 
    422             return result 
     415        message = "" 
     416        for tryit in range(3) : 
     417            try : 
     418                base = base or self.basedn 
     419                if self.useldapcache : 
     420                    # Here we overwrite the fields the app want, to try and 
     421                    # retrieve ALL user defined attributes ("*") 
     422                    # + the createTimestamp attribute, needed by job history 
     423                    #  
     424                    # This may not work with all LDAP servers 
     425                    # but works at least in OpenLDAP (2.1.25)  
     426                    # and iPlanet Directory Server (5.1 SP3) 
     427                    fields = ["*", "createTimestamp"]          
     428                     
     429                if self.useldapcache and (not flushcache) and (scope == ldap.SCOPE_BASE) and self.ldapcache.has_key(base) : 
     430                    entry = self.ldapcache[base] 
     431                    self.tool.logdebug("LDAP cache hit %s => %s" % (base, entry)) 
     432                    result = [(base, entry)] 
     433                else : 
     434                    self.tool.logdebug("QUERY : Filter : %s, BaseDN : %s, Scope : %s, Attributes : %s" % (key, base, scope, fields)) 
     435                    result = self.database.search_s(base, scope, key, fields) 
     436            except ldap.NO_SUCH_OBJECT, msg :         
     437                raise PyKotaStorageError, (_("Search base %s doesn't seem to exist. Probable misconfiguration. Please double check /etc/pykota/pykota.conf : %s") % (base, msg)) 
     438            except ldap.LDAPError, msg :     
     439                message = (_("Search for %s(%s) from %s(scope=%s) returned no answer.") % (key, fields, base, scope)) + " : %s" % str(msg) 
     440                self.tool.printInfo("LDAP error : %s" % message, "error") 
     441                self.tool.printInfo("LDAP connection will be closed and reopened.", "warn") 
     442                self.close() 
     443                self.secondStageInit() 
     444            else :      
     445                self.tool.logdebug("QUERY : Result : %s" % result) 
     446                if self.useldapcache : 
     447                    for (dn, attributes) in result : 
     448                        self.tool.logdebug("LDAP cache store %s => %s" % (dn, attributes)) 
     449                        self.ldapcache[dn] = attributes 
     450                return result 
     451        raise PyKotaStorageError, message 
    423452             
    424453    def doAdd(self, dn, fields) : 
    425454        """Adds an entry in the LDAP directory.""" 
    426         # TODO : store into LDAP specific cache 
    427455        fields = self.normalizeFields(fields) 
    428         try : 
    429             self.tool.logdebug("QUERY : ADD(%s, %s)" % (dn, str(fields))) 
    430             entry = ldap.modlist.addModlist(fields) 
    431             self.tool.logdebug("%s" % entry) 
    432             self.database.add_s(dn, entry) 
    433         except ldap.LDAPError, msg : 
    434             raise PyKotaStorageError, (_("Problem adding LDAP entry (%s, %s)") % (dn, str(fields))) + " : %s" % str(msg) 
    435         else : 
    436             if self.useldapcache : 
    437                 self.tool.logdebug("LDAP cache add %s => %s" % (dn, fields)) 
    438                 self.ldapcache[dn] = fields 
    439             return dn 
     456        message = "" 
     457        for tryit in range(3) : 
     458            try : 
     459                self.tool.logdebug("QUERY : ADD(%s, %s)" % (dn, str(fields))) 
     460                entry = ldap.modlist.addModlist(fields) 
     461                self.tool.logdebug("%s" % entry) 
     462                self.database.add_s(dn, entry) 
     463            except ldap.LDAPError, msg : 
     464                message = (_("Problem adding LDAP entry (%s, %s)") % (dn, str(fields))) + " : %s" % str(msg) 
     465                self.tool.printInfo("LDAP error : %s" % message, "error") 
     466                self.tool.printInfo("LDAP connection will be closed and reopened.", "warn") 
     467                self.close() 
     468                self.secondStageInit() 
     469            else : 
     470                if self.useldapcache : 
     471                    self.tool.logdebug("LDAP cache add %s => %s" % (dn, fields)) 
     472                    self.ldapcache[dn] = fields 
     473                return dn 
     474        raise PyKotaStorageError, message 
    440475             
    441476    def doDelete(self, dn) : 
    442477        """Deletes an entry from the LDAP directory.""" 
    443         # TODO : delete from LDAP specific cache 
    444         try : 
    445             self.tool.logdebug("QUERY : Delete(%s)" % dn) 
    446             self.database.delete_s(dn) 
    447         except ldap.LDAPError, msg : 
    448             raise PyKotaStorageError, (_("Problem deleting LDAP entry (%s)") % dn) + " : %s" % str(msg) 
    449         else :     
    450             if self.useldapcache : 
    451                 try : 
    452                     self.tool.logdebug("LDAP cache del %s" % dn) 
    453                     del self.ldapcache[dn] 
    454                 except KeyError :     
    455                     pass 
     478        message = "" 
     479        for tryit in range(3) : 
     480            try : 
     481                self.tool.logdebug("QUERY : Delete(%s)" % dn) 
     482                self.database.delete_s(dn) 
     483            except ldap.LDAPError, msg : 
     484                message = (_("Problem deleting LDAP entry (%s)") % dn) + " : %s" % str(msg) 
     485                self.tool.printInfo("LDAP error : %s" % message, "error") 
     486                self.tool.printInfo("LDAP connection will be closed and reopened.", "warn") 
     487                self.close() 
     488                self.secondStageInit() 
     489            else :     
     490                if self.useldapcache : 
     491                    try : 
     492                        self.tool.logdebug("LDAP cache del %s" % dn) 
     493                        del self.ldapcache[dn] 
     494                    except KeyError :     
     495                        pass 
     496                return         
     497        raise PyKotaStorageError, message 
    456498             
    457499    def doModify(self, dn, fields, ignoreold=1, flushcache=0) : 
    458500        """Modifies an entry in the LDAP directory.""" 
    459         try : 
    460             # TODO : take care of, and update LDAP specific cache 
    461             if self.useldapcache and not flushcache : 
    462                 if self.ldapcache.has_key(dn) : 
    463                     old = self.ldapcache[dn] 
    464                     self.tool.logdebug("LDAP cache hit %s => %s" % (dn, old)) 
    465                     oldentry = {} 
    466                     for (k, v) in old.items() : 
    467                         if k != "createTimestamp" : 
    468                             oldentry[k] = v 
    469                 else :     
    470                     self.tool.logdebug("LDAP cache miss %s" % dn) 
    471                     oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE)[0][1] 
    472             else :         
    473                 oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE, flushcache=flushcache)[0][1] 
    474             for (k, v) in fields.items() : 
    475                 if type(v) == type({}) : 
    476                     try : 
    477                         oldvalue = v["convert"](oldentry.get(k, [0])[0]) 
    478                     except ValueError :     
    479                         self.tool.logdebug("Error converting %s with %s(%s)" % (oldentry.get(k), k, v)) 
    480                         oldvalue = 0 
    481                     if v["operator"] == '+' : 
    482                         newvalue = oldvalue + v["value"] 
     501        for tryit in range(3) : 
     502            try : 
     503                # TODO : take care of, and update LDAP specific cache 
     504                if self.useldapcache and not flushcache : 
     505                    if self.ldapcache.has_key(dn) : 
     506                        old = self.ldapcache[dn] 
     507                        self.tool.logdebug("LDAP cache hit %s => %s" % (dn, old)) 
     508                        oldentry = {} 
     509                        for (k, v) in old.items() : 
     510                            if k != "createTimestamp" : 
     511                                oldentry[k] = v 
    483512                    else :     
    484                         newvalue = oldvalue - v["value"] 
    485                     fields[k] = str(newvalue) 
    486             fields = self.normalizeFields(fields) 
    487             self.tool.logdebug("QUERY : Modify(%s, %s ==> %s)" % (dn, oldentry, fields)) 
    488             entry = ldap.modlist.modifyModlist(oldentry, fields, ignore_oldexistent=ignoreold) 
    489             modentry = [] 
    490             for (mop, mtyp, mval) in entry : 
    491                 if mtyp != "createTimestamp" : 
    492                     modentry.append((mop, mtyp, mval)) 
    493             self.tool.logdebug("MODIFY : %s ==> %s ==> %s" % (fields, entry, modentry)) 
    494             if modentry : 
    495                 self.database.modify_s(dn, modentry) 
    496         except ldap.LDAPError, msg : 
    497             raise PyKotaStorageError, (_("Problem modifying LDAP entry (%s, %s)") % (dn, fields)) + " : %s" % str(msg) 
    498         else : 
    499             if self.useldapcache : 
    500                 cachedentry = self.ldapcache[dn] 
     513                        self.tool.logdebug("LDAP cache miss %s" % dn) 
     514                        oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE)[0][1] 
     515                else :         
     516                    oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE, flushcache=flushcache)[0][1] 
     517                for (k, v) in fields.items() : 
     518                    if type(v) == type({}) : 
     519                        try : 
     520                            oldvalue = v["convert"](oldentry.get(k, [0])[0]) 
     521                        except ValueError :     
     522                            self.tool.logdebug("Error converting %s with %s(%s)" % (oldentry.get(k), k, v)) 
     523                            oldvalue = 0 
     524                        if v["operator"] == '+' : 
     525                            newvalue = oldvalue + v["value"] 
     526                        else :     
     527                            newvalue = oldvalue - v["value"] 
     528                        fields[k] = str(newvalue) 
     529                fields = self.normalizeFields(fields) 
     530                self.tool.logdebug("QUERY : Modify(%s, %s ==> %s)" % (dn, oldentry, fields)) 
     531                entry = ldap.modlist.modifyModlist(oldentry, fields, ignore_oldexistent=ignoreold) 
     532                modentry = [] 
    501533                for (mop, mtyp, mval) in entry : 
    502                     if mop in (ldap.MOD_ADD, ldap.MOD_REPLACE) : 
    503                         cachedentry[mtyp] = mval 
    504                     else : 
    505                         try : 
    506                             del cachedentry[mtyp] 
    507                         except KeyError :     
    508                             pass 
    509                 self.tool.logdebug("LDAP cache update %s => %s" % (dn, cachedentry)) 
    510             return dn 
     534                    if mtyp != "createTimestamp" : 
     535                        modentry.append((mop, mtyp, mval)) 
     536                self.tool.logdebug("MODIFY : %s ==> %s ==> %s" % (fields, entry, modentry)) 
     537                if modentry : 
     538                    self.database.modify_s(dn, modentry) 
     539            except ldap.LDAPError, msg : 
     540                message = (_("Problem modifying LDAP entry (%s, %s)") % (dn, fields)) + " : %s" % str(msg) 
     541                self.tool.printInfo("LDAP error : %s" % message, "error") 
     542                self.tool.printInfo("LDAP connection will be closed and reopened.", "warn") 
     543                self.close() 
     544                self.secondStageInit() 
     545            else : 
     546                if self.useldapcache : 
     547                    cachedentry = self.ldapcache[dn] 
     548                    for (mop, mtyp, mval) in entry : 
     549                        if mop in (ldap.MOD_ADD, ldap.MOD_REPLACE) : 
     550                            cachedentry[mtyp] = mval 
     551                        else : 
     552                            try : 
     553                                del cachedentry[mtyp] 
     554                            except KeyError :     
     555                                pass 
     556                    self.tool.logdebug("LDAP cache update %s => %s" % (dn, cachedentry)) 
     557                return dn 
     558        raise PyKotaStorageError, message 
    511559             
    512560    def getAllPrintersNames(self) :     
  • pykota/trunk/pykota/version.py

    r1961 r1966  
    2222# 
    2323 
    24 __version__ = "1.21alpha9_unofficial" 
     24__version__ = "1.21alpha10_unofficial" 
    2525 
    2626__doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng."""