class RSSCache( object ): ''' Cache with purgeThread integrated ''' def __init__( self, lifeTime, updateFunc = None, cacheHistoryLifeTime = None ): ''' Constructor ''' self.__lifeTime = lifeTime # lifetime of the history on hours self.__cacheHistoryLifeTime = ( 1 and cacheHistoryLifeTime ) or 24 self.__updateFunc = updateFunc # RSSCache self.__rssCache = DictCache() self.__rssCacheStatus = [] # ( updateTime, message ) self.__rssCacheLock = threading.Lock() # Create purgeThread self.__refreshStop = False self.__refreshThread = threading.Thread( target = self.__refreshCacheThreadRun ) self.__refreshThread.setDaemon( True ) def startRefreshThread( self ): ''' Run refresh thread. ''' self.__refreshThread.start() def stopRefreshThread( self ): ''' Stop refresh thread. ''' self.__refreshStop = True def isCacheAlive( self ): ''' Returns status of the cache refreshing thread ''' return S_OK( self.__refreshThread.isAlive() ) def setLifeTime( self, lifeTime ): ''' Set cache life time ''' self.__lifeTime = lifeTime def setCacheHistoryLifeTime( self, cacheHistoryLifeTime ): ''' Set cache life time ''' self.__cacheHistoryLifeTime = cacheHistoryLifeTime def getCacheKeys( self ): ''' List all the keys stored in the cache. ''' self.__rssCacheLock.acquire() keys = self.__rssCache.getKeys() self.__rssCacheLock.release() return S_OK( keys ) def acquireLock( self ): ''' Acquires RSSCache lock ''' self.__rssCacheLock.acquire() def releaseLock( self ): ''' Releases RSSCache lock ''' self.__rssCacheLock.release() def getCacheStatus( self ): ''' Return the latest cache status ''' self.__rssCacheLock.acquire() if self.__rssCacheStatus: res = dict( [ self.__rssCacheStatus[ 0 ] ] ) else: res = {} self.__rssCacheLock.release() return S_OK( res ) def getCacheHistory( self ): ''' Return the cache updates history ''' self.__rssCacheLock.acquire() res = dict( self.__rssCacheStatus ) self.__rssCacheLock.release() return S_OK( res ) def get( self, resourceKey ): ''' Gets the resource(s) status(es). Every resource can have multiple statuses, so in order to speed up things, we store them on the cache as follows:: { (<resourceName>,<resourceStatusType0>) : whatever0, (<resourceName>,<resourceStatusType1>) : whatever1, } ''' #cacheKey = '%s#%s' % ( resourceName, resourceStatusType ) self.__rssCacheLock.acquire() resourceStatus = self.__rssCache.get( resourceKey ) self.__rssCacheLock.release() if resourceStatus: return S_OK( { resourceKey : resourceStatus } ) return S_ERROR( 'Cannot get %s' % resourceKey ) def getBulk( self, resourceKeys ): ''' Gets values for resourceKeys in one ATOMIC operation. ''' result = {} self.__rssCacheLock.acquire() for resourceKey in resourceKeys: resourceRow = self.__rssCache.get( resourceKey ) if not resourceRow: return S_ERROR( 'Cannot get %s' % resourceKey ) result.update( { resourceKey : resourceRow } ) self.__rssCacheLock.release() return S_OK( result ) def resetCache( self ): ''' Reset cache. ''' self.__rssCacheLock.acquire() self.__rssCache.purgeAll() self.__rssCacheLock.release() return S_OK() def refreshCache( self ): ''' Clears the cache and gets its latest version, not Thread safe ! Acquire a lock before using it ! ( and release it afterwards ! ) ''' self.__rssCache.purgeAll() if self.__updateFunc is None: return S_ERROR( 'RSSCache has no updateFunction' ) newCache = self.__updateFunc() if not newCache[ 'OK' ]: return newCache itemsAdded = self.__updateCache( newCache[ 'Value' ] ) return itemsAdded def refreshCacheAndHistory( self ): ''' Method that refreshes the cache and updates the history. Not thread safe, you must acquire a lock before using it, and release it right after ! ''' refreshResult = self.refreshCache() now = datetime.datetime.utcnow() if self.__rssCacheStatus: # Check oldest record dateInserted, _message = self.__rssCacheStatus[ -1 ] if dateInserted < now - datetime.timedelta( hours = self.__cacheHistoryLifeTime ): self.__rssCacheStatus.pop() self.__rssCacheStatus.insert( 0, ( now, refreshResult ) ) ################################################################################ # Private methods def __updateCache( self, newCache ): ''' The new cache must be a dictionary, which should look like:: { ( <resourceName>,<resourceStatusType0>) : whatever0, ( <resourceName>,<resourceStatusType1>) : whatever1, } ''' itemsCounter = 0 for cacheKey, cacheValue in newCache.items(): self.__rssCache.add( cacheKey, self.__lifeTime, value = cacheValue ) itemsCounter += 1 return S_OK( itemsCounter ) def __refreshCacheThreadRun( self ): ''' Method that refreshes periodically the cache. ''' while not self.__refreshStop: self.__rssCacheLock.acquire() self.refreshCacheAndHistory() self.__rssCacheLock.release() time.sleep( self.__lifeTime ) self.__refreshStop = False
class RSSCache: """ Cache with purgeThread integrated """ def __init__(self, lifeTime, updateFunc=None, cacheHistoryLifeTime=None): """ Constructor """ self.__lifeTime = lifeTime # lifetime of the history on hours self.__cacheHistoryLifeTime = (1 and cacheHistoryLifeTime) or 24 self.__updateFunc = updateFunc # RSSCache self.__rssCache = DictCache() self.__rssCacheStatus = [] # ( updateTime, message ) self.__rssCacheLock = threading.Lock() # Create purgeThread self.__refreshStop = False self.__refreshThread = threading.Thread( target=self.__refreshCacheThreadRun) self.__refreshThread.setDaemon(True) def startRefreshThread(self): """ Run refresh thread. """ self.__refreshThread.start() def stopRefreshThread(self): """ Stop refresh thread. """ self.__refreshStop = True def isCacheAlive(self): """ Returns status of the cache refreshing thread """ return S_OK(self.__refreshThread.is_alive()) def setLifeTime(self, lifeTime): """ Set cache life time """ self.__lifeTime = lifeTime def setCacheHistoryLifeTime(self, cacheHistoryLifeTime): """ Set cache life time """ self.__cacheHistoryLifeTime = cacheHistoryLifeTime def getCacheKeys(self): """ List all the keys stored in the cache. """ self.__rssCacheLock.acquire() keys = self.__rssCache.getKeys() self.__rssCacheLock.release() return S_OK(keys) def acquireLock(self): """ Acquires RSSCache lock """ self.__rssCacheLock.acquire() def releaseLock(self): """ Releases RSSCache lock """ self.__rssCacheLock.release() def getCacheStatus(self): """ Return the latest cache status """ self.__rssCacheLock.acquire() if self.__rssCacheStatus: res = dict([self.__rssCacheStatus[0]]) else: res = {} self.__rssCacheLock.release() return S_OK(res) def getCacheHistory(self): """ Return the cache updates history """ self.__rssCacheLock.acquire() res = dict(self.__rssCacheStatus) self.__rssCacheLock.release() return S_OK(res) def get(self, resourceKey): """ Gets the resource(s) status(es). Every resource can have multiple statuses, so in order to speed up things, we store them on the cache as follows:: { (<resourceName>,<resourceStatusType0>) : whatever0, (<resourceName>,<resourceStatusType1>) : whatever1, } """ # cacheKey = '%s#%s' % ( resourceName, resourceStatusType ) self.__rssCacheLock.acquire() resourceStatus = self.__rssCache.get(resourceKey) self.__rssCacheLock.release() if resourceStatus: return S_OK({resourceKey: resourceStatus}) return S_ERROR("Cannot get %s" % resourceKey) def getBulk(self, resourceKeys): """ Gets values for resourceKeys in one ATOMIC operation. """ result = {} self.__rssCacheLock.acquire() for resourceKey in resourceKeys: resourceRow = self.__rssCache.get(resourceKey) if not resourceRow: return S_ERROR("Cannot get %s" % resourceKey) result.update({resourceKey: resourceRow}) self.__rssCacheLock.release() return S_OK(result) def resetCache(self): """ Reset cache. """ self.__rssCacheLock.acquire() self.__rssCache.purgeAll() self.__rssCacheLock.release() return S_OK() def refreshCache(self): """ Clears the cache and gets its latest version, not Thread safe ! Acquire a lock before using it ! ( and release it afterwards ! ) """ self.__rssCache.purgeAll() if self.__updateFunc is None: return S_ERROR("RSSCache has no updateFunction") newCache = self.__updateFunc() if not newCache["OK"]: return newCache itemsAdded = self.__updateCache(newCache["Value"]) return itemsAdded def refreshCacheAndHistory(self): """ Method that refreshes the cache and updates the history. Not thread safe, you must acquire a lock before using it, and release it right after ! """ refreshResult = self.refreshCache() now = datetime.datetime.utcnow() if self.__rssCacheStatus: # Check oldest record dateInserted, _message = self.__rssCacheStatus[-1] if dateInserted < now - datetime.timedelta( hours=self.__cacheHistoryLifeTime): self.__rssCacheStatus.pop() self.__rssCacheStatus.insert(0, (now, refreshResult)) ################################################################################ # Private methods def __updateCache(self, newCache): """ The new cache must be a dictionary, which should look like:: { ( <resourceName>,<resourceStatusType0>) : whatever0, ( <resourceName>,<resourceStatusType1>) : whatever1, } """ itemsCounter = 0 for cacheKey, cacheValue in newCache.items(): self.__rssCache.add(cacheKey, self.__lifeTime, value=cacheValue) itemsCounter += 1 return S_OK(itemsCounter) def __refreshCacheThreadRun(self): """ Method that refreshes periodically the cache. """ while not self.__refreshStop: self.__rssCacheLock.acquire() self.refreshCacheAndHistory() self.__rssCacheLock.release() time.sleep(self.__lifeTime) self.__refreshStop = False
class Cache(object): """ Cache basic class. WARNING: None of its methods is thread safe. Acquire / Release lock when using them ! """ def __init__(self, lifeTime, updateFunc): """ Constructor :Parameters: **lifeTime** - `int` Lifetime of the elements in the cache ( seconds ! ) **updateFunc** - `function` This function MUST return a S_OK | S_ERROR object. In the case of the first, its value must be a dictionary. """ # We set a 20% of the lifetime randomly, so that if we have thousands of jobs # starting at the same time, all the caches will not end at the same time. randomLifeTimeBias = 0.2 * random.random() self.log = gLogger.getSubLogger(self.__class__.__name__) self.__lifeTime = int(lifeTime * (1 + randomLifeTimeBias)) self.__updateFunc = updateFunc # The records returned from the cache must be valid at least 30 seconds. self.__validSeconds = 30 # Cache self.__cache = DictCache() self.__cacheLock = LockRing() self.__cacheLock.getLock(self.__class__.__name__) #............................................................................. # internal cache object getter def cacheKeys(self): """ Cache keys getter :returns: list with valid keys on the cache """ return self.__cache.getKeys(validSeconds=self.__validSeconds) #............................................................................. # acquire / release Locks def acquireLock(self): """ Acquires Cache lock """ self.__cacheLock.acquire(self.__class__.__name__) def releaseLock(self): """ Releases Cache lock """ self.__cacheLock.release(self.__class__.__name__) #............................................................................. # Cache getters def get(self, cacheKeys): """ Gets values for cacheKeys given, if all are found ( present on the cache and valid ), returns S_OK with the results. If any is not neither present not valid, returns S_ERROR. :Parameters: **cacheKeys** - `list` list of keys to be extracted from the cache :return: S_OK | S_ERROR """ result = {} for cacheKey in cacheKeys: cacheRow = self.__cache.get(cacheKey, validSeconds=self.__validSeconds) if not cacheRow: self.log.error(str(cacheKey)) return S_ERROR('Cannot get %s' % str(cacheKey)) result.update({cacheKey: cacheRow}) return S_OK(result) #............................................................................. # Cache refreshers def refreshCache(self): """ Purges the cache and gets fresh data from the update function. :return: S_OK | S_ERROR. If the first, its content is the new cache. """ self.log.verbose('refreshing...') self.__cache.purgeAll() newCache = self.__updateFunc() if not newCache['OK']: self.log.error(newCache['Message']) return newCache newCache = self.__updateCache(newCache['Value']) self.log.verbose('refreshed') return newCache #............................................................................. # Private methods def __updateCache(self, newCache): """ Given the new cache dictionary, updates the internal cache with it. It sets a duration to the entries of <self.__lifeTime> seconds. :Parameters: **newCache** - `dict` dictionary containing a new cache :return: dictionary. It is newCache argument. """ for cacheKey, cacheValue in newCache.items(): self.__cache.add(cacheKey, self.__lifeTime, value=cacheValue) # We are assuming nothing will fail while inserting in the cache. There is # no apparent reason to suspect from that piece of code. return S_OK(newCache)
class ProxyManagerClient(object): def __init__(self): self.__usersCache = DictCache() self.__proxiesCache = DictCache() self.__vomsProxiesCache = DictCache() self.__pilotProxiesCache = DictCache() self.__filesCache = DictCache(self.__deleteTemporalFile) def __deleteTemporalFile(self, filename): """ Delete temporal file :param str filename: path to file """ try: os.remove(filename) except Exception: pass def clearCaches(self): """ Clear caches """ self.__usersCache.purgeAll() self.__proxiesCache.purgeAll() self.__vomsProxiesCache.purgeAll() self.__pilotProxiesCache.purgeAll() def __getSecondsLeftToExpiration(self, expiration, utc=True): """ Get time left to expiration in a seconds :param datetime expiration: :param boolean utc: time in utc :return: datetime """ if utc: td = expiration - datetime.datetime.utcnow() else: td = expiration - datetime.datetime.now() return td.days * 86400 + td.seconds def __refreshUserCache(self, validSeconds=0): """ Refresh user cache :param int validSeconds: required seconds the proxy is valid for :return: S_OK()/S_ERROR() """ rpcClient = RPCClient("Framework/ProxyManager", timeout=120) retVal = rpcClient.getRegisteredUsers(validSeconds) if not retVal['OK']: return retVal data = retVal['Value'] # Update the cache for record in data: cacheKey = (record['DN'], record['group']) self.__usersCache.add(cacheKey, self.__getSecondsLeftToExpiration(record['expirationtime']), record) return S_OK() @gUsersSync def userHasProxy(self, userDN, userGroup, validSeconds=0): """ Check if a user(DN-group) has a proxy in the proxy management Updates internal cache if needed to minimize queries to the service :param str userDN: user DN :param str userGroup: user group :param int validSeconds: proxy valid time in a seconds :return: S_OK()/S_ERROR() """ # For backward compatibility reasons with versions prior to v7r1 # we need to check for proxy with a group # AND for groupless proxy even if not specified cacheKeys = ((userDN, userGroup), (userDN, '')) for cacheKey in cacheKeys: if self.__usersCache.exists(cacheKey, validSeconds): return S_OK(True) # Get list of users from the DB with proxys at least 300 seconds gLogger.verbose("Updating list of users in proxy management") retVal = self.__refreshUserCache(validSeconds) if not retVal['OK']: return retVal for cacheKey in cacheKeys: if self.__usersCache.exists(cacheKey, validSeconds): return S_OK(True) return S_OK(False) @gUsersSync def getUserPersistence(self, userDN, userGroup, validSeconds=0): """ Check if a user(DN-group) has a proxy in the proxy management Updates internal cache if needed to minimize queries to the service :param str userDN: user DN :param str userGroup: user group :param int validSeconds: proxy valid time in a seconds :return: S_OK()/S_ERROR() """ cacheKey = (userDN, userGroup) userData = self.__usersCache.get(cacheKey, validSeconds) if userData: if userData['persistent']: return S_OK(True) # Get list of users from the DB with proxys at least 300 seconds gLogger.verbose("Updating list of users in proxy management") retVal = self.__refreshUserCache(validSeconds) if not retVal['OK']: return retVal userData = self.__usersCache.get(cacheKey, validSeconds) if userData: return S_OK(userData['persistent']) return S_OK(False) def setPersistency(self, userDN, userGroup, persistent): """ Set the persistency for user/group :param str userDN: user DN :param str userGroup: user group :param boolean persistent: presistent flag :return: S_OK()/S_ERROR() """ # Hack to ensure bool in the rpc call persistentFlag = True if not persistent: persistentFlag = False rpcClient = RPCClient("Framework/ProxyManager", timeout=120) retVal = rpcClient.setPersistency(userDN, userGroup, persistentFlag) if not retVal['OK']: return retVal # Update internal persistency cache cacheKey = (userDN, userGroup) record = self.__usersCache.get(cacheKey, 0) if record: record['persistent'] = persistentFlag self.__usersCache.add(cacheKey, self.__getSecondsLeftToExpiration(record['expirationtime']), record) return retVal def uploadProxy(self, proxy=None, restrictLifeTime=0, rfcIfPossible=False): """ Upload a proxy to the proxy management service using delegation :param X509Chain proxy: proxy as a chain :param int restrictLifeTime: proxy live time in a seconds :param boolean rfcIfPossible: make rfc proxy if possible :return: S_OK(dict)/S_ERROR() -- dict contain proxies """ # Discover proxy location if isinstance(proxy, X509Chain): chain = proxy proxyLocation = "" else: if not proxy: proxyLocation = Locations.getProxyLocation() if not proxyLocation: return S_ERROR("Can't find a valid proxy") elif isinstance(proxy, six.string_types): proxyLocation = proxy else: return S_ERROR("Can't find a valid proxy") chain = X509Chain() result = chain.loadProxyFromFile(proxyLocation) if not result['OK']: return S_ERROR("Can't load %s: %s " % (proxyLocation, result['Message'])) # Make sure it's valid if chain.hasExpired().get('Value'): return S_ERROR("Proxy %s has expired" % proxyLocation) if chain.getDIRACGroup().get('Value') or chain.isVOMS().get('Value'): return S_ERROR("Cannot upload proxy with DIRAC group or VOMS extensions") rpcClient = RPCClient("Framework/ProxyManager", timeout=120) # Get a delegation request result = rpcClient.requestDelegationUpload(chain.getRemainingSecs()['Value']) if not result['OK']: return result reqDict = result['Value'] # Generate delegated chain chainLifeTime = chain.getRemainingSecs()['Value'] - 60 if restrictLifeTime and restrictLifeTime < chainLifeTime: chainLifeTime = restrictLifeTime retVal = chain.generateChainFromRequestString(reqDict['request'], lifetime=chainLifeTime, rfc=rfcIfPossible) if not retVal['OK']: return retVal # Upload! result = rpcClient.completeDelegationUpload(reqDict['id'], retVal['Value']) if not result['OK']: return result return S_OK(result.get('proxies') or result['Value']) @gProxiesSync def downloadProxy(self, userDN, userGroup, limited=False, requiredTimeLeft=1200, cacheTime=14400, proxyToConnect=None, token=None): """ Get a proxy Chain from the proxy management :param str userDN: user DN :param str userGroup: user group :param boolean limited: if need limited proxy :param int requiredTimeLeft: required proxy live time in a seconds :param int cacheTime: store in a cache time in a seconds :param X509Chain proxyToConnect: proxy as a chain :param str token: valid token to get a proxy :return: S_OK(X509Chain)/S_ERROR() """ cacheKey = (userDN, userGroup) if self.__proxiesCache.exists(cacheKey, requiredTimeLeft): return S_OK(self.__proxiesCache.get(cacheKey)) req = X509Request() req.generateProxyRequest(limited=limited) if proxyToConnect: rpcClient = RPCClient("Framework/ProxyManager", proxyChain=proxyToConnect, timeout=120) else: rpcClient = RPCClient("Framework/ProxyManager", timeout=120) if token: retVal = rpcClient.getProxyWithToken(userDN, userGroup, req.dumpRequest()['Value'], int(cacheTime + requiredTimeLeft), token) else: retVal = rpcClient.getProxy(userDN, userGroup, req.dumpRequest()['Value'], int(cacheTime + requiredTimeLeft)) if not retVal['OK']: return retVal chain = X509Chain(keyObj=req.getPKey()) retVal = chain.loadChainFromString(retVal['Value']) if not retVal['OK']: return retVal self.__proxiesCache.add(cacheKey, chain.getRemainingSecs()['Value'], chain) return S_OK(chain) def downloadProxyToFile(self, userDN, userGroup, limited=False, requiredTimeLeft=1200, cacheTime=14400, filePath=None, proxyToConnect=None, token=None): """ Get a proxy Chain from the proxy management and write it to file :param str userDN: user DN :param str userGroup: user group :param boolean limited: if need limited proxy :param int requiredTimeLeft: required proxy live time in a seconds :param int cacheTime: store in a cache time in a seconds :param str filePath: path to save proxy :param X509Chain proxyToConnect: proxy as a chain :param str token: valid token to get a proxy :return: S_OK(X509Chain)/S_ERROR() """ retVal = self.downloadProxy(userDN, userGroup, limited, requiredTimeLeft, cacheTime, proxyToConnect, token) if not retVal['OK']: return retVal chain = retVal['Value'] retVal = self.dumpProxyToFile(chain, filePath) if not retVal['OK']: return retVal retVal['chain'] = chain return retVal @gVOMSProxiesSync def downloadVOMSProxy(self, userDN, userGroup, limited=False, requiredTimeLeft=1200, cacheTime=14400, requiredVOMSAttribute=None, proxyToConnect=None, token=None): """ Download a proxy if needed and transform it into a VOMS one :param str userDN: user DN :param str userGroup: user group :param boolean limited: if need limited proxy :param int requiredTimeLeft: required proxy live time in a seconds :param int cacheTime: store in a cache time in a seconds :param str requiredVOMSAttribute: VOMS attr to add to the proxy :param X509Chain proxyToConnect: proxy as a chain :param str token: valid token to get a proxy :return: S_OK(X509Chain)/S_ERROR() """ cacheKey = (userDN, userGroup, requiredVOMSAttribute, limited) if self.__vomsProxiesCache.exists(cacheKey, requiredTimeLeft): return S_OK(self.__vomsProxiesCache.get(cacheKey)) req = X509Request() req.generateProxyRequest(limited=limited) if proxyToConnect: rpcClient = RPCClient("Framework/ProxyManager", proxyChain=proxyToConnect, timeout=120) else: rpcClient = RPCClient("Framework/ProxyManager", timeout=120) if token: retVal = rpcClient.getVOMSProxyWithToken(userDN, userGroup, req.dumpRequest()['Value'], int(cacheTime + requiredTimeLeft), token, requiredVOMSAttribute) else: retVal = rpcClient.getVOMSProxy(userDN, userGroup, req.dumpRequest()['Value'], int(cacheTime + requiredTimeLeft), requiredVOMSAttribute) if not retVal['OK']: return retVal chain = X509Chain(keyObj=req.getPKey()) retVal = chain.loadChainFromString(retVal['Value']) if not retVal['OK']: return retVal self.__vomsProxiesCache.add(cacheKey, chain.getRemainingSecs()['Value'], chain) return S_OK(chain) def downloadVOMSProxyToFile(self, userDN, userGroup, limited=False, requiredTimeLeft=1200, cacheTime=14400, requiredVOMSAttribute=None, filePath=None, proxyToConnect=None, token=None): """ Download a proxy if needed, transform it into a VOMS one and write it to file :param str userDN: user DN :param str userGroup: user group :param boolean limited: if need limited proxy :param int requiredTimeLeft: required proxy live time in a seconds :param int cacheTime: store in a cache time in a seconds :param str requiredVOMSAttribute: VOMS attr to add to the proxy :param str filePath: path to save proxy :param X509Chain proxyToConnect: proxy as a chain :param str token: valid token to get a proxy :return: S_OK(X509Chain)/S_ERROR() """ retVal = self.downloadVOMSProxy(userDN, userGroup, limited, requiredTimeLeft, cacheTime, requiredVOMSAttribute, proxyToConnect, token) if not retVal['OK']: return retVal chain = retVal['Value'] retVal = self.dumpProxyToFile(chain, filePath) if not retVal['OK']: return retVal retVal['chain'] = chain return retVal def getPilotProxyFromDIRACGroup(self, userDN, userGroup, requiredTimeLeft=43200, proxyToConnect=None): """ Download a pilot proxy with VOMS extensions depending on the group :param str userDN: user DN :param str userGroup: user group :param int requiredTimeLeft: required proxy live time in seconds :param X509Chain proxyToConnect: proxy as a chain :return: S_OK(X509Chain)/S_ERROR() """ # Assign VOMS attribute vomsAttr = Registry.getVOMSAttributeForGroup(userGroup) if not vomsAttr: gLogger.warn("No voms attribute assigned to group %s when requested pilot proxy" % userGroup) return self.downloadProxy(userDN, userGroup, limited=False, requiredTimeLeft=requiredTimeLeft, proxyToConnect=proxyToConnect) else: return self.downloadVOMSProxy(userDN, userGroup, limited=False, requiredTimeLeft=requiredTimeLeft, requiredVOMSAttribute=vomsAttr, proxyToConnect=proxyToConnect) def getPilotProxyFromVOMSGroup(self, userDN, vomsAttr, requiredTimeLeft=43200, proxyToConnect=None): """ Download a pilot proxy with VOMS extensions depending on the group :param str userDN: user DN :param str vomsAttr: VOMS attribute :param int requiredTimeLeft: required proxy live time in a seconds :param X509Chain proxyToConnect: proxy as a chain :return: S_OK(X509Chain)/S_ERROR() """ groups = Registry.getGroupsWithVOMSAttribute(vomsAttr) if not groups: return S_ERROR("No group found that has %s as voms attrs" % vomsAttr) for userGroup in groups: result = self.downloadVOMSProxy(userDN, userGroup, limited=False, requiredTimeLeft=requiredTimeLeft, requiredVOMSAttribute=vomsAttr, proxyToConnect=proxyToConnect) if result['OK']: return result return result def getPayloadProxyFromDIRACGroup(self, userDN, userGroup, requiredTimeLeft, token=None, proxyToConnect=None): """ Download a payload proxy with VOMS extensions depending on the group :param str userDN: user DN :param str userGroup: user group :param int requiredTimeLeft: required proxy live time in a seconds :param str token: valid token to get a proxy :param X509Chain proxyToConnect: proxy as a chain :return: S_OK(X509Chain)/S_ERROR() """ # Assign VOMS attribute vomsAttr = Registry.getVOMSAttributeForGroup(userGroup) if not vomsAttr: gLogger.verbose("No voms attribute assigned to group %s when requested payload proxy" % userGroup) return self.downloadProxy(userDN, userGroup, limited=True, requiredTimeLeft=requiredTimeLeft, proxyToConnect=proxyToConnect, token=token) else: return self.downloadVOMSProxy(userDN, userGroup, limited=True, requiredTimeLeft=requiredTimeLeft, requiredVOMSAttribute=vomsAttr, proxyToConnect=proxyToConnect, token=token) def getPayloadProxyFromVOMSGroup(self, userDN, vomsAttr, token, requiredTimeLeft, proxyToConnect=None): """ Download a payload proxy with VOMS extensions depending on the VOMS attr :param str userDN: user DN :param str vomsAttr: VOMS attribute :param str token: valid token to get a proxy :param int requiredTimeLeft: required proxy live time in a seconds :param X509Chain proxyToConnect: proxy as a chain :return: S_OK(X509Chain)/S_ERROR() """ groups = Registry.getGroupsWithVOMSAttribute(vomsAttr) if not groups: return S_ERROR("No group found that has %s as voms attrs" % vomsAttr) userGroup = groups[0] return self.downloadVOMSProxy(userDN, userGroup, limited=True, requiredTimeLeft=requiredTimeLeft, requiredVOMSAttribute=vomsAttr, proxyToConnect=proxyToConnect, token=token) def dumpProxyToFile(self, chain, destinationFile=None, requiredTimeLeft=600): """ Dump a proxy to a file. It's cached so multiple calls won't generate extra files :param X509Chain chain: proxy as a chain :param str destinationFile: path to store proxy :param int requiredTimeLeft: required proxy live time in a seconds :return: S_OK(str)/S_ERROR() """ result = chain.hash() if not result['OK']: return result cHash = result['Value'] if self.__filesCache.exists(cHash, requiredTimeLeft): filepath = self.__filesCache.get(cHash) if filepath and os.path.isfile(filepath): return S_OK(filepath) self.__filesCache.delete(cHash) retVal = chain.dumpAllToFile(destinationFile) if not retVal['OK']: return retVal filename = retVal['Value'] self.__filesCache.add(cHash, chain.getRemainingSecs()['Value'], filename) return S_OK(filename) def deleteGeneratedProxyFile(self, chain): """ Delete a file generated by a dump :param X509Chain chain: proxy as a chain :return: S_OK() """ self.__filesCache.delete(chain) return S_OK() def deleteProxyBundle(self, idList): """ delete a list of id's :param list,tuple idList: list of identity numbers :return: S_OK(int)/S_ERROR() """ rpcClient = RPCClient("Framework/ProxyManager", timeout=120) return rpcClient.deleteProxyBundle(idList) def requestToken(self, requesterDN, requesterGroup, numUses=1): """ Request a number of tokens. usesList must be a list of integers and each integer is the number of uses a token must have :param str requesterDN: user DN :param str requesterGroup: user group :param int numUses: number of uses :return: S_OK(tuple)/S_ERROR() -- tuple contain token, number uses """ rpcClient = RPCClient("Framework/ProxyManager", timeout=120) return rpcClient.generateToken(requesterDN, requesterGroup, numUses) def renewProxy(self, proxyToBeRenewed=None, minLifeTime=3600, newProxyLifeTime=43200, proxyToConnect=None): """ Renew a proxy using the ProxyManager :param X509Chain proxyToBeRenewed: proxy to renew :param int minLifeTime: if proxy life time is less than this, renew. Skip otherwise :param int newProxyLifeTime: life time of new proxy :param X509Chain proxyToConnect: proxy to use for connecting to the service :return: S_OK(X509Chain)/S_ERROR() """ retVal = multiProxyArgument(proxyToBeRenewed) if not retVal['Value']: return retVal proxyToRenewDict = retVal['Value'] secs = proxyToRenewDict['chain'].getRemainingSecs()['Value'] if secs > minLifeTime: deleteMultiProxy(proxyToRenewDict) return S_OK() if not proxyToConnect: proxyToConnectDict = {'chain': False, 'tempFile': False} else: retVal = multiProxyArgument(proxyToConnect) if not retVal['Value']: deleteMultiProxy(proxyToRenewDict) return retVal proxyToConnectDict = retVal['Value'] userDN = proxyToRenewDict['chain'].getIssuerCert()['Value'].getSubjectDN()['Value'] retVal = proxyToRenewDict['chain'].getDIRACGroup() if not retVal['OK']: deleteMultiProxy(proxyToRenewDict) deleteMultiProxy(proxyToConnectDict) return retVal userGroup = retVal['Value'] limited = proxyToRenewDict['chain'].isLimitedProxy()['Value'] voms = VOMS() retVal = voms.getVOMSAttributes(proxyToRenewDict['chain']) if not retVal['OK']: deleteMultiProxy(proxyToRenewDict) deleteMultiProxy(proxyToConnectDict) return retVal vomsAttrs = retVal['Value'] if vomsAttrs: retVal = self.downloadVOMSProxy(userDN, userGroup, limited=limited, requiredTimeLeft=newProxyLifeTime, requiredVOMSAttribute=vomsAttrs[0], proxyToConnect=proxyToConnectDict['chain']) else: retVal = self.downloadProxy(userDN, userGroup, limited=limited, requiredTimeLeft=newProxyLifeTime, proxyToConnect=proxyToConnectDict['chain']) deleteMultiProxy(proxyToRenewDict) deleteMultiProxy(proxyToConnectDict) if not retVal['OK']: return retVal chain = retVal['Value'] if not proxyToRenewDict['tempFile']: return chain.dumpAllToFile(proxyToRenewDict['file']) return S_OK(chain) def getDBContents(self, condDict={}, sorting=[['UserDN', 'DESC']], start=0, limit=0): """ Get the contents of the db :param dict condDict: search condition :return: S_OK(dict)/S_ERROR() -- dict contain fields, record list, total records """ rpcClient = RPCClient("Framework/ProxyManager", timeout=120) return rpcClient.getContents(condDict, sorting, start, limit) def getVOMSAttributes(self, chain): """ Get the voms attributes for a chain :param X509Chain chain: proxy as a chain :return: S_OK(str)/S_ERROR() """ return VOMS().getVOMSAttributes(chain) def getUploadedProxyLifeTime(self, DN, group): """ Get the remaining seconds for an uploaded proxy :param str DN: user DN :param str group: group :return: S_OK(int)/S_ERROR() """ result = self.getDBContents({'UserDN': [DN], 'UserGroup': [group]}) if not result['OK']: return result data = result['Value'] if len(data['Records']) == 0: return S_OK(0) pNames = list(data['ParameterNames']) dnPos = pNames.index('UserDN') groupPos = pNames.index('UserGroup') expiryPos = pNames.index('ExpirationTime') for row in data['Records']: if DN == row[dnPos] and group == row[groupPos]: td = row[expiryPos] - datetime.datetime.utcnow() secondsLeft = td.days * 86400 + td.seconds return S_OK(max(0, secondsLeft)) return S_OK(0) def getUserProxiesInfo(self): """ Get the user proxies uploaded info :return: S_OK(dict)/S_ERROR() """ result = RPCClient("Framework/ProxyManager", timeout=120).getUserProxiesInfo() if 'rpcStub' in result: result.pop('rpcStub') return result
class ProxyManagerClient: __metaclass__ = DIRACSingleton.DIRACSingleton def __init__(self): self.__usersCache = DictCache() self.__proxiesCache = DictCache() self.__vomsProxiesCache = DictCache() self.__pilotProxiesCache = DictCache() self.__filesCache = DictCache(self.__deleteTemporalFile) def __deleteTemporalFile(self, filename): try: os.unlink(filename) except: pass def clearCaches(self): self.__usersCache.purgeAll() self.__proxiesCache.purgeAll() self.__vomsProxiesCache.purgeAll() self.__pilotProxiesCache.purgeAll() def __getSecondsLeftToExpiration(self, expiration, utc=True): if utc: td = expiration - datetime.datetime.utcnow() else: td = expiration - datetime.datetime.now() return td.days * 86400 + td.seconds def __refreshUserCache(self, validSeconds=0): rpcClient = RPCClient("Framework/ProxyManager", timeout=120) retVal = rpcClient.getRegisteredUsers(validSeconds) if not retVal['OK']: return retVal data = retVal['Value'] #Update the cache for record in data: cacheKey = (record['DN'], record['group']) self.__usersCache.add( cacheKey, self.__getSecondsLeftToExpiration(record['expirationtime']), record) return S_OK() @gUsersSync def userHasProxy(self, userDN, userGroup, validSeconds=0): """ Check if a user(DN-group) has a proxy in the proxy management - Updates internal cache if needed to minimize queries to the service """ cacheKey = (userDN, userGroup) if self.__usersCache.exists(cacheKey, validSeconds): return S_OK(True) #Get list of users from the DB with proxys at least 300 seconds gLogger.verbose("Updating list of users in proxy management") retVal = self.__refreshUserCache(validSeconds) if not retVal['OK']: return retVal return S_OK(self.__usersCache.exists(cacheKey, validSeconds)) @gUsersSync def getUserPersistence(self, userDN, userGroup, validSeconds=0): """ Check if a user(DN-group) has a proxy in the proxy management - Updates internal cache if needed to minimize queries to the service """ cacheKey = (userDN, userGroup) userData = self.__usersCache.get(cacheKey, validSeconds) if userData: if userData['persistent']: return S_OK(True) #Get list of users from the DB with proxys at least 300 seconds gLogger.verbose("Updating list of users in proxy management") retVal = self.__refreshUserCache(validSeconds) if not retVal['OK']: return retVal userData = self.__usersCache.get(cacheKey, validSeconds) if userData: return S_OK(userData['persistent']) return S_OK(False) def setPersistency(self, userDN, userGroup, persistent): """ Set the persistency for user/group """ #Hack to ensure bool in the rpc call persistentFlag = True if not persistent: persistentFlag = False rpcClient = RPCClient("Framework/ProxyManager", timeout=120) retVal = rpcClient.setPersistency(userDN, userGroup, persistentFlag) if not retVal['OK']: return retVal #Update internal persistency cache cacheKey = (userDN, userGroup) record = self.__usersCache.get(cacheKey, 0) if record: record['persistent'] = persistentFlag self.__usersCache.add( cacheKey, self.__getSecondsLeftToExpiration(record['expirationtime']), record) return retVal def uploadProxy(self, proxy=False, diracGroup=False, chainToConnect=False, restrictLifeTime=0): """ Upload a proxy to the proxy management service using delgation """ #Discover proxy location if type(proxy) == g_X509ChainType: chain = proxy proxyLocation = "" else: if not proxy: proxyLocation = Locations.getProxyLocation() if not proxyLocation: return S_ERROR("Can't find a valid proxy") elif type(proxy) in (types.StringType, types.UnicodeType): proxyLocation = proxy else: return S_ERROR("Can't find a valid proxy") chain = X509Chain() result = chain.loadProxyFromFile(proxyLocation) if not result['OK']: return S_ERROR("Can't load %s: %s " % (proxyLocation, result['Message'])) if not chainToConnect: chainToConnect = chain #Make sure it's valid if chain.hasExpired()['Value']: return S_ERROR("Proxy %s has expired" % proxyLocation) #rpcClient = RPCClient( "Framework/ProxyManager", proxyChain = chainToConnect ) rpcClient = RPCClient("Framework/ProxyManager", timeout=120) #Get a delegation request result = rpcClient.requestDelegationUpload( chain.getRemainingSecs()['Value'], diracGroup) if not result['OK']: return result #Check if the delegation has been granted if 'Value' not in result or not result['Value']: if 'proxies' in result: return S_OK(result['proxies']) else: return S_OK() reqDict = result['Value'] #Generate delegated chain chainLifeTime = chain.getRemainingSecs()['Value'] - 60 if restrictLifeTime and restrictLifeTime < chainLifeTime: chainLifeTime = restrictLifeTime retVal = chain.generateChainFromRequestString(reqDict['request'], lifetime=chainLifeTime, diracGroup=diracGroup) if not retVal['OK']: return retVal #Upload! result = rpcClient.completeDelegationUpload(reqDict['id'], retVal['Value']) if not result['OK']: return result if 'proxies' in result: return S_OK(result['proxies']) return S_OK() @gProxiesSync def downloadProxy(self, userDN, userGroup, limited=False, requiredTimeLeft=1200, cacheTime=43200, proxyToConnect=False, token=False): """ Get a proxy Chain from the proxy management """ cacheKey = (userDN, userGroup) if self.__proxiesCache.exists(cacheKey, requiredTimeLeft): return S_OK(self.__proxiesCache.get(cacheKey)) req = X509Request() req.generateProxyRequest(limited=limited) if proxyToConnect: rpcClient = RPCClient("Framework/ProxyManager", proxyChain=proxyToConnect, timeout=120) else: rpcClient = RPCClient("Framework/ProxyManager", timeout=120) if token: retVal = rpcClient.getProxyWithToken( userDN, userGroup, req.dumpRequest()['Value'], long(cacheTime + requiredTimeLeft), token) else: retVal = rpcClient.getProxy(userDN, userGroup, req.dumpRequest()['Value'], long(cacheTime + requiredTimeLeft)) if not retVal['OK']: return retVal chain = X509Chain(keyObj=req.getPKey()) retVal = chain.loadChainFromString(retVal['Value']) if not retVal['OK']: return retVal self.__proxiesCache.add(cacheKey, chain.getRemainingSecs()['Value'], chain) return S_OK(chain) def downloadProxyToFile(self, userDN, userGroup, limited=False, requiredTimeLeft=1200, cacheTime=43200, filePath=False, proxyToConnect=False, token=False): """ Get a proxy Chain from the proxy management and write it to file """ retVal = self.downloadProxy(userDN, userGroup, limited, requiredTimeLeft, cacheTime, proxyToConnect, token) if not retVal['OK']: return retVal chain = retVal['Value'] retVal = self.dumpProxyToFile(chain, filePath) if not retVal['OK']: return retVal retVal['chain'] = chain return retVal @gVOMSProxiesSync def downloadVOMSProxy(self, userDN, userGroup, limited=False, requiredTimeLeft=1200, cacheTime=43200, requiredVOMSAttribute=False, proxyToConnect=False, token=False): """ Download a proxy if needed and transform it into a VOMS one """ cacheKey = (userDN, userGroup, requiredVOMSAttribute, limited) if self.__vomsProxiesCache.exists(cacheKey, requiredTimeLeft): return S_OK(self.__vomsProxiesCache.get(cacheKey)) req = X509Request() req.generateProxyRequest(limited=limited) if proxyToConnect: rpcClient = RPCClient("Framework/ProxyManager", proxyChain=proxyToConnect, timeout=120) else: rpcClient = RPCClient("Framework/ProxyManager", timeout=120) if token: retVal = rpcClient.getVOMSProxyWithToken( userDN, userGroup, req.dumpRequest()['Value'], long(cacheTime + requiredTimeLeft), token, requiredVOMSAttribute) else: retVal = rpcClient.getVOMSProxy(userDN, userGroup, req.dumpRequest()['Value'], long(cacheTime + requiredTimeLeft), requiredVOMSAttribute) if not retVal['OK']: return retVal chain = X509Chain(keyObj=req.getPKey()) retVal = chain.loadChainFromString(retVal['Value']) if not retVal['OK']: return retVal self.__vomsProxiesCache.add(cacheKey, chain.getRemainingSecs()['Value'], chain) return S_OK(chain) def downloadVOMSProxyToFile(self, userDN, userGroup, limited=False, requiredTimeLeft=1200, cacheTime=43200, requiredVOMSAttribute=False, filePath=False, proxyToConnect=False, token=False): """ Download a proxy if needed, transform it into a VOMS one and write it to file """ retVal = self.downloadVOMSProxy(userDN, userGroup, limited, requiredTimeLeft, cacheTime, requiredVOMSAttribute, proxyToConnect, token) if not retVal['OK']: return retVal chain = retVal['Value'] retVal = self.dumpProxyToFile(chain, filePath) if not retVal['OK']: return retVal retVal['chain'] = chain return retVal def getPilotProxyFromDIRACGroup(self, userDN, userGroup, requiredTimeLeft=43200, proxyToConnect=False): """ Download a pilot proxy with VOMS extensions depending on the group """ #Assign VOMS attribute vomsAttr = CS.getVOMSAttributeForGroup(userGroup) if not vomsAttr: gLogger.verbose( "No voms attribute assigned to group %s when requested pilot proxy" % userGroup) return self.downloadProxy(userDN, userGroup, limited=False, requiredTimeLeft=requiredTimeLeft, proxyToConnect=proxyToConnect) else: return self.downloadVOMSProxy(userDN, userGroup, limited=False, requiredTimeLeft=requiredTimeLeft, requiredVOMSAttribute=vomsAttr, proxyToConnect=proxyToConnect) def getPilotProxyFromVOMSGroup(self, userDN, vomsAttr, requiredTimeLeft=43200, proxyToConnect=False): """ Download a pilot proxy with VOMS extensions depending on the group """ groups = CS.getGroupsWithVOMSAttribute(vomsAttr) if not groups: return S_ERROR("No group found that has %s as voms attrs" % vomsAttr) for userGroup in groups: result = self.downloadVOMSProxy(userDN, userGroup, limited=False, requiredTimeLeft=requiredTimeLeft, requiredVOMSAttribute=vomsAttr, proxyToConnect=proxyToConnect) if result['OK']: return result return result def getPayloadProxyFromDIRACGroup(self, userDN, userGroup, requiredTimeLeft, token=False, proxyToConnect=False): """ Download a payload proxy with VOMS extensions depending on the group """ #Assign VOMS attribute vomsAttr = CS.getVOMSAttributeForGroup(userGroup) if not vomsAttr: gLogger.verbose( "No voms attribute assigned to group %s when requested payload proxy" % userGroup) return self.downloadProxy(userDN, userGroup, limited=True, requiredTimeLeft=requiredTimeLeft, proxyToConnect=proxyToConnect, token=token) else: return self.downloadVOMSProxy(userDN, userGroup, limited=True, requiredTimeLeft=requiredTimeLeft, requiredVOMSAttribute=vomsAttr, proxyToConnect=proxyToConnect, token=token) def getPayloadProxyFromVOMSGroup(self, userDN, vomsAttr, token, requiredTimeLeft, proxyToConnect=False): """ Download a payload proxy with VOMS extensions depending on the VOMS attr """ groups = CS.getGroupsWithVOMSAttribute(vomsAttr) if not groups: return S_ERROR("No group found that has %s as voms attrs" % vomsAttr) userGroup = groups[0] return self.downloadVOMSProxy(userDN, userGroup, limited=True, requiredTimeLeft=requiredTimeLeft, requiredVOMSAttribute=vomsAttr, proxyToConnect=proxyToConnect, token=token) def dumpProxyToFile(self, chain, destinationFile=False, requiredTimeLeft=600): """ Dump a proxy to a file. It's cached so multiple calls won't generate extra files """ result = chain.hash() if not result['OK']: return result hash = result['Value'] if self.__filesCache.exists(hash, requiredTimeLeft): filepath = self.__filesCache.get(hash) if os.path.isfile(filepath): return S_OK(filepath) self.__filesCache.delete(hash) retVal = chain.dumpAllToFile(destinationFile) if not retVal['OK']: return retVal filename = retVal['Value'] self.__filesCache.add(hash, chain.getRemainingSecs()['Value'], filename) return S_OK(filename) def deleteGeneratedProxyFile(self, chain): """ Delete a file generated by a dump """ self.__filesCache.delete(chain) return S_OK() def requestToken(self, requesterDN, requesterGroup, numUses=1): """ Request a number of tokens. usesList must be a list of integers and each integer is the number of uses a token must have """ rpcClient = RPCClient("Framework/ProxyManager", timeout=120) return rpcClient.generateToken(requesterDN, requesterGroup, numUses) def renewProxy(self, proxyToBeRenewed=False, minLifeTime=3600, newProxyLifeTime=43200, proxyToConnect=False): """ Renew a proxy using the ProxyManager Arguments: proxyToBeRenewed : proxy to renew minLifeTime : if proxy life time is less than this, renew. Skip otherwise newProxyLifeTime : life time of new proxy proxyToConnect : proxy to use for connecting to the service """ retVal = File.multiProxyArgument(proxyToBeRenewed) if not retVal['Value']: return retVal proxyToRenewDict = retVal['Value'] secs = proxyToRenewDict['chain'].getRemainingSecs()['Value'] if secs > minLifeTime: File.deleteMultiProxy(proxyToRenewDict) return S_OK() if not proxyToConnect: proxyToConnectDict = {'chain': False, 'tempFile': False} else: retVal = File.multiProxyArgument(proxyToConnect) if not retVal['Value']: File.deleteMultiProxy(proxyToRenewDict) return retVal proxyToConnectDict = retVal['Value'] userDN = proxyToRenewDict['chain'].getIssuerCert( )['Value'].getSubjectDN()['Value'] retVal = proxyToRenewDict['chain'].getDIRACGroup() if not retVal['OK']: File.deleteMultiProxy(proxyToRenewDict) File.deleteMultiProxy(proxyToConnectDict) return retVal userGroup = retVal['Value'] limited = proxyToRenewDict['chain'].isLimitedProxy()['Value'] voms = VOMS() retVal = voms.getVOMSAttributes(proxyToRenewDict['chain']) if not retVal['OK']: File.deleteMultiProxy(proxyToRenewDict) File.deleteMultiProxy(proxyToConnectDict) return retVal vomsAttrs = retVal['Value'] if vomsAttrs: retVal = self.downloadVOMSProxy( userDN, userGroup, limited=limited, requiredTimeLeft=newProxyLifeTime, requiredVOMSAttribute=vomsAttrs[0], proxyToConnect=proxyToConnectDict['chain']) else: retVal = self.downloadProxy( userDN, userGroup, limited=limited, requiredTimeLeft=newProxyLifeTime, proxyToConnect=proxyToConnectDict['chain']) File.deleteMultiProxy(proxyToRenewDict) File.deleteMultiProxy(proxyToConnectDict) if not retVal['OK']: return retVal chain = retVal['Value'] if not proxyToRenewDict['tempFile']: return chain.dumpAllToFile(proxyToRenewDict['file']) return S_OK(chain) def getDBContents(self, condDict={}): """ Get the contents of the db """ rpcClient = RPCClient("Framework/ProxyManager", timeout=120) return rpcClient.getContents(condDict, [['UserDN', 'DESC']], 0, 0) def getVOMSAttributes(self, chain): """ Get the voms attributes for a chain """ return VOMS().getVOMSAttributes(chain) def getUploadedProxyLifeTime(self, DN, group): """ Get the remaining seconds for an uploaded proxy """ result = self.getDBContents({'UserDN': [DN], 'UserGroup': [group]}) if not result['OK']: return result data = result['Value'] if len(data['Records']) == 0: return S_OK(0) pNames = list(data['ParameterNames']) dnPos = pNames.index('UserDN') groupPos = pNames.index('UserGroup') expiryPos = pNames.index('ExpirationTime') for row in data['Records']: if DN == row[dnPos] and group == row[groupPos]: td = row[expiryPos] - datetime.datetime.utcnow() secondsLeft = td.days * 86400 + td.seconds return S_OK(max(0, secondsLeft)) return S_OK(0) def getUserProxiesInfo(self): """ Get the user proxies uploaded info """ result = RPCClient("Framework/ProxyManager", timeout=120).getUserProxiesInfo() if 'rpcStub' in result: result.pop('rpcStub') return result
class Cache( object ): """ Cache basic class. WARNING: None of its methods is thread safe. Acquire / Release lock when using them ! """ def __init__( self, lifeTime, updateFunc ): """ Constructor :Parameters: **lifeTime** - `int` Lifetime of the elements in the cache ( seconds ! ) **updateFunc** - `function` This function MUST return a S_OK | S_ERROR object. In the case of the first, its value must be a dictionary. """ # We set a 20% of the lifetime randomly, so that if we have thousands of jobs # starting at the same time, all the caches will not end at the same time. randomLifeTimeBias = 0.2 * random.random() self.log = gLogger.getSubLogger( self.__class__.__name__ ) self.__lifeTime = int( lifeTime * ( 1 + randomLifeTimeBias ) ) self.__updateFunc = updateFunc # The records returned from the cache must be valid at least 10 seconds. self.__validSeconds = 10 # Cache self.__cache = DictCache() self.__cacheLock = LockRing() self.__cacheLock.getLock( self.__class__.__name__ ) #............................................................................. # internal cache object getter def cacheKeys( self ): """ Cache keys getter :returns: list with valid keys on the cache """ return self.__cache.getKeys( validSeconds = self.__validSeconds ) #............................................................................. # acquire / release Locks def acquireLock( self ): """ Acquires Cache lock """ self.__cacheLock.acquire( self.__class__.__name__ ) def releaseLock( self ): """ Releases Cache lock """ self.__cacheLock.release( self.__class__.__name__) #............................................................................. # Cache getters def get( self, cacheKeys ): """ Gets values for cacheKeys given, if all are found ( present on the cache and valid ), returns S_OK with the results. If any is not neither present not valid, returns S_ERROR. :Parameters: **cacheKeys** - `list` list of keys to be extracted from the cache :return: S_OK | S_ERROR """ result = {} for cacheKey in cacheKeys: cacheRow = self.__cache.get( cacheKey, validSeconds = self.__validSeconds ) if not cacheRow: self.log.error( str( cacheKey ) ) return S_ERROR( 'Cannot get %s' % str( cacheKey ) ) result.update( { cacheKey : cacheRow } ) return S_OK( result ) #............................................................................. # Cache refreshers def refreshCache( self ): """ Purges the cache and gets fresh data from the update function. :return: S_OK | S_ERROR. If the first, its content is the new cache. """ self.log.verbose( 'refreshing...' ) self.__cache.purgeAll() newCache = self.__updateFunc() if not newCache[ 'OK' ]: self.log.error( newCache[ 'Message' ] ) return newCache newCache = self.__updateCache( newCache[ 'Value' ] ) self.log.verbose( 'refreshed' ) return newCache #............................................................................. # Private methods def __updateCache( self, newCache ): """ Given the new cache dictionary, updates the internal cache with it. It sets a duration to the entries of <self.__lifeTime> seconds. :Parameters: **newCache** - `dict` dictionary containing a new cache :return: dictionary. It is newCache argument. """ for cacheKey, cacheValue in newCache.items(): self.__cache.add( cacheKey, self.__lifeTime, value = cacheValue ) # We are assuming nothing will fail while inserting in the cache. There is # no apparent reason to suspect from that piece of code. return S_OK( newCache )
class ProxyManagerClient: __metaclass__ = DIRACSingleton.DIRACSingleton def __init__( self ): self.__usersCache = DictCache() self.__proxiesCache = DictCache() self.__vomsProxiesCache = DictCache() self.__pilotProxiesCache = DictCache() self.__filesCache = DictCache( self.__deleteTemporalFile ) def __deleteTemporalFile( self, filename ): try: os.unlink( filename ) except: pass def clearCaches( self ): self.__usersCache.purgeAll() self.__proxiesCache.purgeAll() self.__vomsProxiesCache.purgeAll() self.__pilotProxiesCache.purgeAll() def __getSecondsLeftToExpiration( self, expiration, utc = True ): if utc: td = expiration - datetime.datetime.utcnow() else: td = expiration - datetime.datetime.now() return td.days * 86400 + td.seconds def __refreshUserCache( self, validSeconds = 0 ): rpcClient = RPCClient( "Framework/ProxyManager", timeout = 120 ) retVal = rpcClient.getRegisteredUsers( validSeconds ) if not retVal[ 'OK' ]: return retVal data = retVal[ 'Value' ] #Update the cache for record in data: cacheKey = ( record[ 'DN' ], record[ 'group' ] ) self.__usersCache.add( cacheKey, self.__getSecondsLeftToExpiration( record[ 'expirationtime' ] ), record ) return S_OK() @gUsersSync def userHasProxy( self, userDN, userGroup, validSeconds = 0 ): """ Check if a user(DN-group) has a proxy in the proxy management - Updates internal cache if needed to minimize queries to the service """ cacheKey = ( userDN, userGroup ) if self.__usersCache.exists( cacheKey, validSeconds ): return S_OK( True ) #Get list of users from the DB with proxys at least 300 seconds gLogger.verbose( "Updating list of users in proxy management" ) retVal = self.__refreshUserCache( validSeconds ) if not retVal[ 'OK' ]: return retVal return S_OK( self.__usersCache.exists( cacheKey, validSeconds ) ) @gUsersSync def getUserPersistence( self, userDN, userGroup, validSeconds = 0 ): """ Check if a user(DN-group) has a proxy in the proxy management - Updates internal cache if needed to minimize queries to the service """ cacheKey = ( userDN, userGroup ) userData = self.__usersCache.get( cacheKey, validSeconds ) if userData: if userData[ 'persistent' ]: return S_OK( True ) #Get list of users from the DB with proxys at least 300 seconds gLogger.verbose( "Updating list of users in proxy management" ) retVal = self.__refreshUserCache( validSeconds ) if not retVal[ 'OK' ]: return retVal userData = self.__usersCache.get( cacheKey, validSeconds ) if userData: return S_OK( userData[ 'persistent' ] ) return S_OK( False ) def setPersistency( self, userDN, userGroup, persistent ): """ Set the persistency for user/group """ #Hack to ensure bool in the rpc call persistentFlag = True if not persistent: persistentFlag = False rpcClient = RPCClient( "Framework/ProxyManager", timeout = 120 ) retVal = rpcClient.setPersistency( userDN, userGroup, persistentFlag ) if not retVal[ 'OK' ]: return retVal #Update internal persistency cache cacheKey = ( userDN, userGroup ) record = self.__usersCache.get( cacheKey, 0 ) if record: record[ 'persistent' ] = persistentFlag self.__usersCache.add( cacheKey, self.__getSecondsLeftToExpiration( record[ 'expirationtime' ] ), record ) return retVal def uploadProxy( self, proxy = False, diracGroup = False, chainToConnect = False, restrictLifeTime = 0, rfcIfPossible = False ): """ Upload a proxy to the proxy management service using delegation """ #Discover proxy location if type( proxy ) == g_X509ChainType: chain = proxy proxyLocation = "" else: if not proxy: proxyLocation = Locations.getProxyLocation() if not proxyLocation: return S_ERROR( "Can't find a valid proxy" ) elif isinstance( proxy, basestring ): proxyLocation = proxy else: return S_ERROR( "Can't find a valid proxy" ) chain = X509Chain() result = chain.loadProxyFromFile( proxyLocation ) if not result[ 'OK' ]: return S_ERROR( "Can't load %s: %s " % ( proxyLocation, result[ 'Message' ] ) ) if not chainToConnect: chainToConnect = chain #Make sure it's valid if chain.hasExpired()[ 'Value' ]: return S_ERROR( "Proxy %s has expired" % proxyLocation ) #rpcClient = RPCClient( "Framework/ProxyManager", proxyChain = chainToConnect ) rpcClient = RPCClient( "Framework/ProxyManager", timeout = 120 ) #Get a delegation request result = rpcClient.requestDelegationUpload( chain.getRemainingSecs()['Value'], diracGroup ) if not result[ 'OK' ]: return result #Check if the delegation has been granted if 'Value' not in result or not result[ 'Value' ]: if 'proxies' in result: return S_OK( result[ 'proxies' ] ) else: return S_OK() reqDict = result[ 'Value' ] #Generate delegated chain chainLifeTime = chain.getRemainingSecs()[ 'Value' ] - 60 if restrictLifeTime and restrictLifeTime < chainLifeTime: chainLifeTime = restrictLifeTime retVal = chain.generateChainFromRequestString( reqDict[ 'request' ], lifetime = chainLifeTime, diracGroup = diracGroup, rfc = rfcIfPossible) if not retVal[ 'OK' ]: return retVal #Upload! result = rpcClient.completeDelegationUpload( reqDict[ 'id' ], retVal[ 'Value' ] ) if not result[ 'OK' ]: return result if 'proxies' in result: return S_OK( result[ 'proxies' ] ) return S_OK() @gProxiesSync def downloadProxy( self, userDN, userGroup, limited = False, requiredTimeLeft = 1200, cacheTime = 43200, proxyToConnect = False, token = False ): """ Get a proxy Chain from the proxy management """ cacheKey = ( userDN, userGroup ) if self.__proxiesCache.exists( cacheKey, requiredTimeLeft ): return S_OK( self.__proxiesCache.get( cacheKey ) ) req = X509Request() req.generateProxyRequest( limited = limited ) if proxyToConnect: rpcClient = RPCClient( "Framework/ProxyManager", proxyChain = proxyToConnect, timeout = 120 ) else: rpcClient = RPCClient( "Framework/ProxyManager", timeout = 120 ) if token: retVal = rpcClient.getProxyWithToken( userDN, userGroup, req.dumpRequest()['Value'], long( cacheTime + requiredTimeLeft ), token ) else: retVal = rpcClient.getProxy( userDN, userGroup, req.dumpRequest()['Value'], long( cacheTime + requiredTimeLeft ) ) if not retVal[ 'OK' ]: return retVal chain = X509Chain( keyObj = req.getPKey() ) retVal = chain.loadChainFromString( retVal[ 'Value' ] ) if not retVal[ 'OK' ]: return retVal self.__proxiesCache.add( cacheKey, chain.getRemainingSecs()['Value'], chain ) return S_OK( chain ) def downloadProxyToFile( self, userDN, userGroup, limited = False, requiredTimeLeft = 1200, cacheTime = 43200, filePath = False, proxyToConnect = False, token = False ): """ Get a proxy Chain from the proxy management and write it to file """ retVal = self.downloadProxy( userDN, userGroup, limited, requiredTimeLeft, cacheTime, proxyToConnect, token ) if not retVal[ 'OK' ]: return retVal chain = retVal[ 'Value' ] retVal = self.dumpProxyToFile( chain, filePath ) if not retVal[ 'OK' ]: return retVal retVal[ 'chain' ] = chain return retVal @gVOMSProxiesSync def downloadVOMSProxy( self, userDN, userGroup, limited = False, requiredTimeLeft = 1200, cacheTime = 43200, requiredVOMSAttribute = False, proxyToConnect = False, token = False ): """ Download a proxy if needed and transform it into a VOMS one """ cacheKey = ( userDN, userGroup, requiredVOMSAttribute, limited ) if self.__vomsProxiesCache.exists( cacheKey, requiredTimeLeft ): return S_OK( self.__vomsProxiesCache.get( cacheKey ) ) req = X509Request() req.generateProxyRequest( limited = limited ) if proxyToConnect: rpcClient = RPCClient( "Framework/ProxyManager", proxyChain = proxyToConnect, timeout = 120 ) else: rpcClient = RPCClient( "Framework/ProxyManager", timeout = 120 ) if token: retVal = rpcClient.getVOMSProxyWithToken( userDN, userGroup, req.dumpRequest()['Value'], long( cacheTime + requiredTimeLeft ), token, requiredVOMSAttribute ) else: retVal = rpcClient.getVOMSProxy( userDN, userGroup, req.dumpRequest()['Value'], long( cacheTime + requiredTimeLeft ), requiredVOMSAttribute ) if not retVal[ 'OK' ]: return retVal chain = X509Chain( keyObj = req.getPKey() ) retVal = chain.loadChainFromString( retVal[ 'Value' ] ) if not retVal[ 'OK' ]: return retVal self.__vomsProxiesCache.add( cacheKey, chain.getRemainingSecs()['Value'], chain ) return S_OK( chain ) def downloadVOMSProxyToFile( self, userDN, userGroup, limited = False, requiredTimeLeft = 1200, cacheTime = 43200, requiredVOMSAttribute = False, filePath = False, proxyToConnect = False, token = False ): """ Download a proxy if needed, transform it into a VOMS one and write it to file """ retVal = self.downloadVOMSProxy( userDN, userGroup, limited, requiredTimeLeft, cacheTime, requiredVOMSAttribute, proxyToConnect, token ) if not retVal[ 'OK' ]: return retVal chain = retVal[ 'Value' ] retVal = self.dumpProxyToFile( chain, filePath ) if not retVal[ 'OK' ]: return retVal retVal[ 'chain' ] = chain return retVal def getPilotProxyFromDIRACGroup( self, userDN, userGroup, requiredTimeLeft = 43200, proxyToConnect = False ): """ Download a pilot proxy with VOMS extensions depending on the group """ #Assign VOMS attribute vomsAttr = CS.getVOMSAttributeForGroup( userGroup ) if not vomsAttr: gLogger.verbose( "No voms attribute assigned to group %s when requested pilot proxy" % userGroup ) return self.downloadProxy( userDN, userGroup, limited = False, requiredTimeLeft = requiredTimeLeft, proxyToConnect = proxyToConnect ) else: return self.downloadVOMSProxy( userDN, userGroup, limited = False, requiredTimeLeft = requiredTimeLeft, requiredVOMSAttribute = vomsAttr, proxyToConnect = proxyToConnect ) def getPilotProxyFromVOMSGroup( self, userDN, vomsAttr, requiredTimeLeft = 43200, proxyToConnect = False ): """ Download a pilot proxy with VOMS extensions depending on the group """ groups = CS.getGroupsWithVOMSAttribute( vomsAttr ) if not groups: return S_ERROR( "No group found that has %s as voms attrs" % vomsAttr ) for userGroup in groups: result = self.downloadVOMSProxy( userDN, userGroup, limited = False, requiredTimeLeft = requiredTimeLeft, requiredVOMSAttribute = vomsAttr, proxyToConnect = proxyToConnect ) if result['OK']: return result return result def getPayloadProxyFromDIRACGroup( self, userDN, userGroup, requiredTimeLeft, token = False, proxyToConnect = False ): """ Download a payload proxy with VOMS extensions depending on the group """ #Assign VOMS attribute vomsAttr = CS.getVOMSAttributeForGroup( userGroup ) if not vomsAttr: gLogger.verbose( "No voms attribute assigned to group %s when requested payload proxy" % userGroup ) return self.downloadProxy( userDN, userGroup, limited = True, requiredTimeLeft = requiredTimeLeft, proxyToConnect = proxyToConnect, token = token ) else: return self.downloadVOMSProxy( userDN, userGroup, limited = True, requiredTimeLeft = requiredTimeLeft, requiredVOMSAttribute = vomsAttr, proxyToConnect = proxyToConnect, token = token ) def getPayloadProxyFromVOMSGroup( self, userDN, vomsAttr, token, requiredTimeLeft, proxyToConnect = False ): """ Download a payload proxy with VOMS extensions depending on the VOMS attr """ groups = CS.getGroupsWithVOMSAttribute( vomsAttr ) if not groups: return S_ERROR( "No group found that has %s as voms attrs" % vomsAttr ) userGroup = groups[0] return self.downloadVOMSProxy( userDN, userGroup, limited = True, requiredTimeLeft = requiredTimeLeft, requiredVOMSAttribute = vomsAttr, proxyToConnect = proxyToConnect, token = token ) def dumpProxyToFile( self, chain, destinationFile = False, requiredTimeLeft = 600 ): """ Dump a proxy to a file. It's cached so multiple calls won't generate extra files """ result = chain.hash() if not result[ 'OK' ]: return result cHash = result[ 'Value' ] if self.__filesCache.exists( cHash, requiredTimeLeft ): filepath = self.__filesCache.get( cHash ) if filepath and os.path.isfile( filepath ): return S_OK( filepath ) self.__filesCache.delete( cHash ) retVal = chain.dumpAllToFile( destinationFile ) if not retVal[ 'OK' ]: return retVal filename = retVal[ 'Value' ] self.__filesCache.add( cHash, chain.getRemainingSecs()['Value'], filename ) return S_OK( filename ) def deleteGeneratedProxyFile( self, chain ): """ Delete a file generated by a dump """ self.__filesCache.delete( chain ) return S_OK() def requestToken( self, requesterDN, requesterGroup, numUses = 1 ): """ Request a number of tokens. usesList must be a list of integers and each integer is the number of uses a token must have """ rpcClient = RPCClient( "Framework/ProxyManager", timeout = 120 ) return rpcClient.generateToken( requesterDN, requesterGroup, numUses ) def renewProxy( self, proxyToBeRenewed = False, minLifeTime = 3600, newProxyLifeTime = 43200, proxyToConnect = False ): """ Renew a proxy using the ProxyManager Arguments: proxyToBeRenewed : proxy to renew minLifeTime : if proxy life time is less than this, renew. Skip otherwise newProxyLifeTime : life time of new proxy proxyToConnect : proxy to use for connecting to the service """ retVal = multiProxyArgument( proxyToBeRenewed ) if not retVal[ 'Value' ]: return retVal proxyToRenewDict = retVal[ 'Value' ] secs = proxyToRenewDict[ 'chain' ].getRemainingSecs()[ 'Value' ] if secs > minLifeTime: deleteMultiProxy( proxyToRenewDict ) return S_OK() if not proxyToConnect: proxyToConnectDict = { 'chain': False, 'tempFile': False } else: retVal = multiProxyArgument( proxyToConnect ) if not retVal[ 'Value' ]: deleteMultiProxy( proxyToRenewDict ) return retVal proxyToConnectDict = retVal[ 'Value' ] userDN = proxyToRenewDict[ 'chain' ].getIssuerCert()[ 'Value' ].getSubjectDN()[ 'Value' ] retVal = proxyToRenewDict[ 'chain' ].getDIRACGroup() if not retVal[ 'OK' ]: deleteMultiProxy( proxyToRenewDict ) deleteMultiProxy( proxyToConnectDict ) return retVal userGroup = retVal[ 'Value' ] limited = proxyToRenewDict[ 'chain' ].isLimitedProxy()[ 'Value' ] voms = VOMS() retVal = voms.getVOMSAttributes( proxyToRenewDict[ 'chain' ] ) if not retVal[ 'OK' ]: deleteMultiProxy( proxyToRenewDict ) deleteMultiProxy( proxyToConnectDict ) return retVal vomsAttrs = retVal[ 'Value' ] if vomsAttrs: retVal = self.downloadVOMSProxy( userDN, userGroup, limited = limited, requiredTimeLeft = newProxyLifeTime, requiredVOMSAttribute = vomsAttrs[0], proxyToConnect = proxyToConnectDict[ 'chain' ] ) else: retVal = self.downloadProxy( userDN, userGroup, limited = limited, requiredTimeLeft = newProxyLifeTime, proxyToConnect = proxyToConnectDict[ 'chain' ] ) deleteMultiProxy( proxyToRenewDict ) deleteMultiProxy( proxyToConnectDict ) if not retVal[ 'OK' ]: return retVal chain = retVal['Value'] if not proxyToRenewDict[ 'tempFile' ]: return chain.dumpAllToFile( proxyToRenewDict[ 'file' ] ) return S_OK( chain ) def getDBContents( self, condDict = {} ): """ Get the contents of the db """ rpcClient = RPCClient( "Framework/ProxyManager", timeout = 120 ) return rpcClient.getContents( condDict, [ [ 'UserDN', 'DESC' ] ], 0, 0 ) def getVOMSAttributes( self, chain ): """ Get the voms attributes for a chain """ return VOMS().getVOMSAttributes( chain ) def getUploadedProxyLifeTime( self, DN, group ): """ Get the remaining seconds for an uploaded proxy """ result = self.getDBContents( { 'UserDN' : [ DN ], 'UserGroup' : [ group ] } ) if not result[ 'OK' ]: return result data = result[ 'Value' ] if len( data[ 'Records' ] ) == 0: return S_OK( 0 ) pNames = list( data[ 'ParameterNames' ] ) dnPos = pNames.index( 'UserDN' ) groupPos = pNames.index( 'UserGroup' ) expiryPos = pNames.index( 'ExpirationTime' ) for row in data[ 'Records' ]: if DN == row[ dnPos ] and group == row[ groupPos ]: td = row[ expiryPos ] - datetime.datetime.utcnow() secondsLeft = td.days * 86400 + td.seconds return S_OK( max( 0, secondsLeft ) ) return S_OK( 0 ) def getUserProxiesInfo( self ): """ Get the user proxies uploaded info """ result = RPCClient( "Framework/ProxyManager", timeout = 120 ).getUserProxiesInfo() if 'rpcStub' in result: result.pop( 'rpcStub' ) return result
class Cache: """ Cache basic class. WARNING: None of its methods is thread safe. Acquire / Release lock when using them ! """ def __init__(self, lifeTime, updateFunc): """ Constructor :Parameters: **lifeTime** - `int` Lifetime of the elements in the cache ( seconds ! ) **updateFunc** - `function` This function MUST return a S_OK | S_ERROR object. In the case of the first, its value must be a dictionary. """ # We set a 20% of the lifetime randomly, so that if we have thousands of jobs # starting at the same time, all the caches will not end at the same time. randomLifeTimeBias = 0.2 * random.random() self.log = gLogger.getSubLogger(self.__class__.__name__) self.__lifeTime = int(lifeTime * (1 + randomLifeTimeBias)) self.__updateFunc = updateFunc # The records returned from the cache must be valid at least 30 seconds. self.__validSeconds = 30 # Cache self.__cache = DictCache() self.__cacheLock = LockRing() self.__cacheLock.getLock(self.__class__.__name__) # internal cache object getter def cacheKeys(self): """ Cache keys getter :returns: list with keys in the cache valid for at least twice the validity period of the element """ # Here we need to have more than the validity period because of the logic of the matching: # * get all the keys with validity T # * for each key K, get the element K with validity T # This logic fails for elements just at the limit of the required time return self.__cache.getKeys(validSeconds=self.__validSeconds * 2) # acquire / release Locks def acquireLock(self): """ Acquires Cache lock """ self.__cacheLock.acquire(self.__class__.__name__) def releaseLock(self): """ Releases Cache lock """ self.__cacheLock.release(self.__class__.__name__) # Cache getters def get(self, cacheKeys): """ Gets values for cacheKeys given, if all are found ( present on the cache and valid ), returns S_OK with the results. If any is not neither present not valid, returns S_ERROR. :Parameters: **cacheKeys** - `list` list of keys to be extracted from the cache :return: S_OK | S_ERROR """ result = {} for cacheKey in cacheKeys: cacheRow = self.__cache.get(cacheKey, validSeconds=self.__validSeconds) if not cacheRow: return S_ERROR("Cannot get %s" % str(cacheKey)) result.update({cacheKey: cacheRow}) return S_OK(result) def check(self, cacheKeys, vO): """ Modified get() method. Attempts to find keys with a vO value appended or 'all' value appended. The cacheKeys passed in are 'flattened' cache keys (no vO) Gets values for cacheKeys given, if all are found ( present on the cache and valid ), returns S_OK with the results. If any is not neither present not valid, returns S_ERROR. :Parameters: **cacheKeys** - `list` list of keys to be extracted from the cache :return: S_OK | S_ERROR """ result = {} for cacheKey in cacheKeys: longCacheKey = cacheKey + ("all", ) cacheRow = self.__cache.get(longCacheKey, validSeconds=self.__validSeconds) if not cacheRow: longCacheKey = cacheKey + (vO, ) cacheRow = self.__cache.get(longCacheKey, validSeconds=self.__validSeconds) if not cacheRow: return S_ERROR( 'Cannot get extended %s (neither for VO = %s nor for "all" Vos)' % (str(cacheKey), vO)) result.update({longCacheKey: cacheRow}) return S_OK(result) # Cache refreshers def refreshCache(self): """ Purges the cache and gets fresh data from the update function. :return: S_OK | S_ERROR. If the first, its content is the new cache. """ self.log.verbose("refreshing...") self.__cache.purgeAll() newCache = self.__updateFunc() if not newCache["OK"]: self.log.error(newCache["Message"]) return newCache newCache = self.__updateCache(newCache["Value"]) self.log.verbose("refreshed") return newCache # Private methods def __updateCache(self, newCache): """ Given the new cache dictionary, updates the internal cache with it. It sets a duration to the entries of <self.__lifeTime> seconds. :Parameters: **newCache** - `dict` dictionary containing a new cache :return: dictionary. It is newCache argument. """ for cacheKey, cacheValue in newCache.items(): self.__cache.add(cacheKey, self.__lifeTime, value=cacheValue) # We are assuming nothing will fail while inserting in the cache. There is # no apparent reason to suspect from that piece of code. return S_OK(newCache)