def lock_and_update_flag(cls, __ctx, dictData): ''' update data to an existing context :param contextKey: first hash level :param value: second hash level :param dictData: a dictionary of data ( resulting from a memorize() evaluation ) ''' try: with cls.mutex: __ctx=cls.hash[__ctx.contextKey][__ctx.contextValue] if not __ctx.isLocked(): return False asynclog.log(pos='%s.__manage' %(cls.__name__), key=__ctx.contextKey,value=__ctx.contextValue, ctx=__ctx, msg='LOCK IS UP' ) __ctx.setFlag() # update dictRuntime data __ctx.getCacheKeys().update_keys(dictData) asynclog.log(pos='%s.update_and_flag' %(cls.__name__), key=__ctx.contextKey,value=__ctx.contextValue, msg='updated with notification data',ctx=__ctx ) return True except Exception, e: asynclog.logError(pos='%s.update_and_flag' %(cls.__name__), key=__ctx.contextKey,value=__ctx.contextValue, msg='update notification data permanently',err=str(e) ) raise e
def update(cls, key, value, dictData): ''' update data to an existing context :param contextKey: first hash level :param value: second hash level :param dictData: a dictionary of data ( resulting from a memorize() evaluation ) ''' try: with cls.mutex: cls.hash[key][value].getCacheKeys().update_keys_permanently(dictData) asynclog.log(pos='%s.update' %(cls.__name__), key=key,value=value, msg='update notification data permanently' ) except Exception, e: asynclog.logError(pos='%s.update' %(cls.__name__), key=key,value=value, msg='update notification data permanently',err=str(e) ) raise e
def updateCache(cls,__ctx): key,value=__ctx.contextKey,__ctx.contextValue try: with cls.mutex: # get DictRuntime keys __data=cls.hash[key][value].getCacheKeys().get() if logger.isTraceEnabled(): asynclog.logTrace(pos='%s.updateCache' %(cls.__name__), key=key,value=value, msg='update with callback cache. data: %s' % (__data) ) # Merge with current context REF cache __ctx.getCacheKeys().update_asyncData_permanently(__data) return cls.hash[key][value].getCacheKeys() except Exception, e: asynclog.logError(pos='%s.updateCache' %(cls.__name__), key=key,value=value, msg='update callback cache',err=str(e) )
def pop_ctx_value(cls, key,value): ''' find a Context() identified by its 2 levels if found, remove from the Context from the hash :param contextKey: first level :param value: second level ''' try: with cls.mutex: asynclog.log(pos='%s.pop_ctx_value' %(cls.__name__), key=key,value=value, msg='') return cls.hash[key].pop(value) except KeyError: if not key in cls.hash: logger.error('Key "%s" not defined, check what happens for the value "%s" in the asynchronous.call_after()' % (key,value)) else: logger.error('Key "%s" exists but value "%s" not found - check asynchronous.call_after() at the add() context call' % (key,value)) return None except Exception,e: asynclog.logError(pos='%s.pop_ctx_value' %(cls.__name__), key=key,value=value, msg='',err=str(e) ) return None
def add_ctx(cls, key, value, clonedContext): ''' Add a Context() instance to the hash of hash :param contextKey: first hash level :param value: second hash level :param contextInstance: the value for the second level of type Context() ''' try: with cls.mutex: if not key in cls.hash: cls.hash[key] = {} # Set a functional lock from callback_before() clonedContext.setLock() cls.hash[key][value] = clonedContext asynclog.log(pos='%s.add_ctx' %(cls.__name__), key=key,value=value, msg='storing context',ctx=clonedContext ) # No ERROR logger trace here, as it is reported one level up except Exception, e: asynclog.logError(pos='%s.add' %(cls.__name__), key=key,value=value, msg='storing context failed',err=str(e),ctx=clonedContext ) raise e
class ContextIdentifier: hash={} mutex=Condition() reaperThread = None stopReaperThread=False threadStarted=False @classmethod def start(cls): cls.reaperThread = Thread( target=cls.reaperLoop, name="ContextIdentifierReaperThread") cls.stopReaperThread=False cls.reaperThread.start() cls.threadStarted=True @classmethod def stop(cls): if cls.reaperThread is not None: cls.stopReaperThread=True @classmethod def isLocked(cls, key, value): ''' check if cache resource is locked ''' try: with cls.mutex: asynclog.log(pos='%s.isLocked' %(cls.__name__), key=key,value=value, msg='lock flag set' ) return cls.hash[key][value].isLocked() except KeyError,e: # This could happen if entry was removed from cache asynclog.logWarning(pos='%s.isLocked' %(cls.__name__), key=key,value=value, msg='unlock flag', err=str(e)) pass except Exception, e: asynclog.logError(pos='%s.isLocked' %(cls.__name__), key=key,value=value, msg='unlock flag', err=str(e))
return False @classmethod def unlock(cls, key, value): ''' check if cache resource is locked ''' try: with cls.mutex: asynclog.log(pos='%s.unlock' %(cls.__name__), key=key,value=value, msg='unlock flag' ) return cls.hash[key][value].unlock() except KeyError, e: # This could happen if entry was removed from cache asynclog.logWarning(pos='%s.unlock' %(cls.__name__), key=key,value=value, msg='unlock flag', err=str(e)) except Exception, e: asynclog.logError(pos='%s.unlock' %(cls.__name__), key=key,value=value, msg='unlock flag', err=str(e)) # if the key/value doesn't exists - it has been swept return False @classmethod def add_ctx(cls, key, value, clonedContext): ''' Add a Context() instance to the hash of hash :param contextKey: first hash level :param value: second hash level :param contextInstance: the value for the second level of type Context() ''' try: with cls.mutex:
return except Exception, e: logger.error( '<<ASYNC>>[%s=%s][%s.__manage] context management ,reason: %s' % (contextKey, contextValue, self.__class__.__name__, e)) raise (e) # # Anomaly : If we are here, it means, the context is no more in the cache # Either it's real problem either the context has expired # try: asynclog.logError( pos='%s.__manage' % (self.__class__.__name__), key=contextKey, value=contextValue, msg= 'IGNORED Unknown EVENT (Expiration occurred or real processing error)', err='Unknown EVENT received') if logger.isTraceEnabled(): logger.trace(ContextIdentifier.printall()) except Exception, e: logger.error( '<<ASYNC>>[%s=%s][%s.__manage] No ctx found () !, reason: %s' % (contextKey, contextValue, self.__class__.__name__, e)) raise (e) def handle(self, httpExchange): try: method = httpExchange.getRequestMethod() requestHeaders = httpExchange.getRequestHeaders()
def call_before(cls, __ctx): # ZERO, increase the number of context waiting (should be clean up in case of expiration) # *** This Object is useful for Validation mode *** # TODO: check this is not useless - as the number of waiting context is in the ContextIdentifier class if __ctx.line.asyncBlocking(): ContextLock.contextIncrease() if logger.isTraceEnabled(): logger.trace( '<<ASYNC>> asynchronous.call_before() - contextIncrease() & lock() - [ContextLock=%d]' % (ContextLock.count())) if not __ctx.line.multipleCaller: # This is the expiration criteria in ContextIdentifier reaper thread # __ctx.line.timeout = int(__ctx.line.timeout) # expiration date in milli seconds (-1 means NO expiration) expirationTime = ((__ctx.line.timeout + time.time()) * 1000 if __ctx.line.timeout != -1 else -1) __ctx.setExpirationTime(expirationTime) # # FIRST, get the context identifier value # 3 cases: # value: ${VAR} # value: xxxx => a fixed literal value # "value" keyword is not defined => get the current value from the contextKey in the running context # # Remember: line is only a definition (Immutable) contextKeyValue = __ctx.line.contextValue if __ctx.line.contextValue else __ctx.line.contextKey __ctx.contextKey = __ctx.line.contextKey __ctx.contextValue = __ctx.getCacheKeys().getValue(contextKeyValue) asynclog.logTrace(pos='%s.call_before' % (cls.__name__), msg='Initializing key/value', key=__ctx.contextKey, value=__ctx.contextValue) if not __ctx.contextValue: asynclog.logError(pos='%s.call_before' % (cls.__name__), key=__ctx.contextKey, value=__ctx.contextValue, msg='\ncache=%s' % (__ctx.getCacheKeys().dictRuntime), err='No value found in cache') raise SyntaxError( '[Asynchronous step] contextKey "%s" must have a value in the context or have a "value","contextValue" defined in the scenation' % (__ctx.contextKey)) asynclog.log(pos='%s.callBefore' % (cls.__name__), key=__ctx.contextKey, value=contextKeyValue, msg='initial async identifiers') # SECOND, send the JSON message to the ContextManager process #--------------------------------------------------------------- # TODO: manage the case there is no context router ! # TODO: manage the case we have only SMPP callback jsonMessage = None if __ctx.line.use_contextManager: try: # client call to the router # Time expressed in milliseconds jsonMessage = '{"contextKey" : "%s", "value" : "%s", "host" : "%s", "port" : "%s", "expirationtime": "%s", "count": "%d"}' % ( __ctx.contextKey, __ctx.contextValue, HTTPServerCallback.getHost(), HTTPServerCallback.getPort(), expirationTime, __ctx.line.callbackCount) Configuration.getrouterContextClient().postJsonMessage( jsonMessage, Configuration.getrouterContextClient().getCreateUri()) logger.debug( 'CREATE - Posting the callback router [message: %s]' % (jsonMessage)) except Exception, e: logger.error( 'CREATE - Error posting message to the contextRouter, stacktrace=%s' % (str(e))) raise Exception( 'CREATE - Error posting message to the contextRouter, stacktrace=%s' % (str(e))) # bet that the call will succeed - so we store the contextKey, value doubleton in ContextIdentifier cache # We bet the oneWay will succeed, so we store the context for the callback asynclog.logInfo(pos='%s.callBefore' % (cls.__name__), key=__ctx.contextKey, value=__ctx.contextValue, msg='Storing a cloned context') with cls.mutex: cloneContext = Context(__ctx.macrosAllScenario) cloneContext.copy(__ctx) asynclog.logTrace( pos='%s.callBefore' % cls.__name__, key=__ctx.contextKey, value=__ctx.contextValue, msg= 'Cloned object address: %s, original address=%s, context=%s' % (hex(id(cloneContext)), hex(id(__ctx)), cloneContext)) try: ContextIdentifier.add_ctx(__ctx.contextKey, __ctx.contextValue, cloneContext) except Exception, e: raise (e)
def process_callback(cls, __ctx, __data): __key, __value = __ctx.contextKey, __ctx.contextValue # Decrease all asynchronous counters & print value if trace level ContextLock.decreaseAllCounters('<<ASYNC>> %s.process_callback()' % (cls.__name__)) # For debugging purpose, uid maintains an unique identifier in the log files MDC.put('uid', '[uid=%s]' % (__ctx.uid)) # Ask to the implementation to process asynchronous data returned asynclog.log(pos='%s.process_callback' % cls.__name__, key=__key, value=__value, msg='resuming with notification data : %s' % (__data)) # # if the step as a tag async with a property timeout_success: True # coming back to the callback method means there was no timeout # so this is an assertion failure # if __ctx.line.isTimeoutSuccess(): reporting.report_step_status( success=False, context=__ctx, cause='Assertion Failure: a timeout was expected', state='AsyncStepAssertTimeoutSuccessKO', synchronous=False) __ctx.endAsyncScenario() return True try: # Load an alternative implementation defined in the callback_implementation tag if __ctx.line.callback_implementation: asynclog.log( pos='%s.process_callback' % cls.__name__, key=__key, value=__value, msg= 'post-processing notification data with implementation "%s"' % (str(__ctx.line.callback_implementation))) class_implementation = __ctx.scenario.loaded_implementations[ str(__ctx.line.callback_implementation)] processedData = class_implementation.process_data(__data) else: # Calling back the implementation to interpret the Mock data processedData = __ctx.getCurrentLine().getImplementation( ).process_data(__data) except (SyntaxError, KeyError), x: asynclog.logError( pos='%s.process_callback' % cls.__name__, key=__key, value=__value, msg= 'SyntaxError/KeyError raised when processing process_data()', err=str(x)) logger.error( '[state=StopAsyncStepFatalError][cause=%s][test=%s][scenario=%s]' % (repr(x), __ctx.line.getTestName(), __ctx.scenario)) reporting.report_step_status( success=False, synchronous=False, context=__ctx, cause="%s\n while processing test '%s' in scenario '%s'" % (x, __ctx.line.getTestName(), __ctx.scenario), state='StopSAsynctepFatalError') raise SyntaxError( "%s\n while processing test '%s' in scenario '%s'" % (x, __ctx.line.getTestName(), __ctx.scenario))
success=False, synchronous=False, context=__ctx, cause="%s\n while processing test '%s' in scenario '%s'" % (x, __ctx.line.getTestName(), __ctx.scenario), state='StopSAsynctepFatalError') raise SyntaxError( "%s\n while processing test '%s' in scenario '%s'" % (x, __ctx.line.getTestName(), __ctx.scenario)) # could be raised by process_data() except Exception, cause: asynclog.logError( pos='%s.process_callback' % cls.__name__, key=__key, value=__value, msg= 'SyntaxError/KeyError raised when processing process_data()', err=str(x)) # last boolean parameter means : caused by an async callback reporting.report_step_status(success=False, synchronous=False, context=__ctx, cause=cause, state='stopAsyncStepKO') __ctx.endAsyncScenario() return True # interpreted mock data must be a dictionary if not isinstance(processedData, dict): asynclog.logError(