def _with_auth_context(auth_ctx, func, *args, **kw): """Runs the given function with the specified auth context. :param auth_ctx: Authentication context. :param func: Function to run with the specified auth context. :param args: Function positional arguments. :param kw: Function keyword arguments. :return: Function result. """ old_auth_ctx = context.ctx() if context.has_ctx() else None context.set_ctx(auth_ctx) try: return func(*args, **kw) except Exception as e: # Note (rakhmerov): In case of "Too many connections" error from the # database it doesn't get wrapped with a SQLAlchemy exception for some # reason so we have to check the exception message explicitly. if isinstance(e, _RETRY_ERRORS) or 'Too many connections' in str(e): LOG.exception("DB error detected, operation will be retried: %s", func) raise finally: context.set_ctx(old_auth_ctx)
def schedule_call(factory_method_path, target_method_name, run_after, serializers=None, **method_args): """Schedules call and lately invokes target_method. Add this call specification to DB, and then after run_after seconds service CallScheduler invokes the target_method. :param factory_method_path: Full python-specific path to factory method for target object construction. :param target_method_name: Name of target object method which will be invoked. :param run_after: Value in seconds. param serializers: map of argument names and their serializer class paths. Use when an argument is an object of specific type, and needs to be serialized. Example: { "result": "mistral.utils.serializer.ResultSerializer"} Serializer for the object type must implement serializer interface in mistral/utils/serializer.py :param method_args: Target method keyword arguments. """ ctx_serializer = context.RpcContextSerializer( context.JsonPayloadSerializer() ) ctx = ( ctx_serializer.serialize_context(context.ctx()) if context.has_ctx() else {} ) execution_time = (datetime.datetime.now() + datetime.timedelta(seconds=run_after)) if serializers: for arg_name, serializer_path in serializers.items(): if arg_name not in method_args: raise exc.MistralException( "Serializable method argument %s" " not found in method_args=%s" % (arg_name, method_args)) try: serializer = importutils.import_class(serializer_path)() except ImportError as e: raise ImportError("Cannot import class %s: %s" % (serializer_path, e)) method_args[arg_name] = serializer.serialize( method_args[arg_name] ) values = { 'factory_method_path': factory_method_path, 'target_method_name': target_method_name, 'execution_time': execution_time, 'auth_context': ctx, 'serializers': serializers, 'method_arguments': method_args, 'processing': False } db_api.create_delayed_call(values)
def decorate(*args, **kw): _prepare() try: res = func(*args, **kw) queues = _get_queues() tx_queue = queues[0] non_tx_queue = queues[1] if not tx_queue and not non_tx_queue: return res auth_ctx = context.ctx() if context.has_ctx() else None def _within_new_thread(): old_auth_ctx = context.ctx() if context.has_ctx() else None context.set_ctx(auth_ctx) try: if tx_queue: _process_tx_queue(tx_queue) if non_tx_queue: _process_non_tx_queue(non_tx_queue) finally: context.set_ctx(old_auth_ctx) eventlet.spawn(_within_new_thread) finally: _clear() return res
def schedule_call(factory_method_path, target_method_name, run_after, serializers=None, key=None, **method_args): """Schedules call and lately invokes target_method. Add this call specification to DB, and then after run_after seconds service CallScheduler invokes the target_method. :param factory_method_path: Full python-specific path to factory method that creates a target object that the call will be made against. :param target_method_name: Name of a method which will be invoked. :param run_after: Value in seconds. :param serializers: map of argument names and their serializer class paths. Use when an argument is an object of specific type, and needs to be serialized. Example: { "result": "mistral.utils.serializer.ResultSerializer"} Serializer for the object type must implement serializer interface in mistral/utils/serializer.py :param key: Key which can potentially be used for squashing similar delayed calls. :param method_args: Target method keyword arguments. """ ctx_serializer = context.RpcContextSerializer() ctx = ( ctx_serializer.serialize_context(context.ctx()) if context.has_ctx() else {} ) execution_time = (datetime.datetime.now() + datetime.timedelta(seconds=run_after)) if serializers: for arg_name, serializer_path in serializers.items(): if arg_name not in method_args: raise exc.MistralException( "Serializable method argument %s" " not found in method_args=%s" % (arg_name, method_args)) try: serializer = importutils.import_class(serializer_path)() except ImportError as e: raise ImportError( "Cannot import class %s: %s" % (serializer_path, e) ) method_args[arg_name] = serializer.serialize(method_args[arg_name]) values = { 'factory_method_path': factory_method_path, 'target_method_name': target_method_name, 'execution_time': execution_time, 'auth_context': ctx, 'serializers': serializers, 'key': key, 'method_arguments': method_args, 'processing': False } db_api.create_delayed_call(values)
def decorate(*args, **kw): _prepare() try: res = func(*args, **kw) queue = _get_queue() auth_ctx = context.ctx() if context.has_ctx() else None # NOTE(rakhmerov): Since we make RPC calls to the engine itself # we need to process the action queue asynchronously in a new # thread. Otherwise, if we have one engine process the engine # will may send a request to itself while already processing # another one. In conjunction with blocking RPC it will lead # to a deadlock (and RPC timeout). def _within_new_thread(): old_auth_ctx = context.ctx() if context.has_ctx() else None context.set_ctx(auth_ctx) try: _process_queue(queue) finally: context.set_ctx(old_auth_ctx) eventlet.spawn(_within_new_thread) finally: _clear() return res
def _within_new_thread(): old_auth_ctx = context.ctx() if context.has_ctx() else None context.set_ctx(auth_ctx) try: _process_queue(queue) finally: context.set_ctx(old_auth_ctx)
def decorate(*args, **kw): # Retrying library decorator might potentially run a decorated # function within a new thread so it's safer not to apply the # decorator directly to a target method/function because we can # lose an authentication context. # The solution is to create one more function and explicitly set # auth context before calling it (potentially in a new thread). auth_ctx = context.ctx() if context.has_ctx() else None return retry.call(_with_auth_context, auth_ctx, func, *args, **kw)
def decorate(*args, **kw): # We can't use RetryDecorator from oslo_service directly because # it runs a decorated function in a different thread and hence # the function doesn't have access to authentication context # set as a thread local variable. # The solution is to reuse RetryDecorator but explicitly set # auth context in the new thread that RetryDecorator spawns. # In order to do that we need an additional helper function. auth_ctx = ctx.ctx() if ctx.has_ctx() else None return _with_auth_context(auth_ctx, func, *args, **kw)
def _within_new_thread(): old_auth_ctx = context.ctx() if context.has_ctx() else None context.set_ctx(auth_ctx) try: if tx_queue: _process_tx_queue(tx_queue) if non_tx_queue: _process_non_tx_queue(non_tx_queue) finally: context.set_ctx(old_auth_ctx)
def schedule_call(factory_method_path, target_method_name, run_after, serializers=None, **method_args): """Add this call specification to DB, and then after run_after seconds service CallScheduler invokes the target_method. :param factory_method_path: Full python-specific path to factory method for target object construction. :param target_method_name: Name of target object method which will be invoked. :param run_after: Value in seconds. param serializers: map of argument names and their serializer class paths. Use when an argument is an object of specific type, and needs to be serialized. Example: { "result": "mistral.utils.serializer.ResultSerializer"} Serializer for the object type must implement serializer interface in mistral/utils/serializer.py :param method_args: Target method keyword arguments. """ ctx = context.ctx().to_dict() if context.has_ctx() else {} execution_time = (datetime.datetime.now() + datetime.timedelta(seconds=run_after)) if serializers: for arg_name, serializer_path in serializers.items(): if arg_name not in method_args: raise exc.MistralException("Serializable method argument %s" " not found in method_args=%s" % (arg_name, method_args)) try: serializer = importutils.import_class(serializer_path)() except ImportError as e: raise ImportError("Cannot import class %s: %s" % (serializer_path, e)) method_args[arg_name] = serializer.serialize(method_args[arg_name]) values = { 'factory_method_path': factory_method_path, 'target_method_name': target_method_name, 'execution_time': execution_time, 'auth_context': ctx, 'serializers': serializers, 'method_arguments': method_args } db_api.create_delayed_call(values)
def delete_trust(trust_id=None): if not trust_id: # Try to retrieve trust from context. if auth_ctx.has_ctx(): trust_id = auth_ctx.ctx().trust_id if not trust_id: return keystone_client = keystone.client_for_trusts(trust_id) try: keystone_client.trusts.delete(trust_id) except Exception as e: LOG.warning("Failed to delete trust [id=%s]: %s", trust_id, e)
def _within_new_thread(): # This is a new thread so we need to init a profiler again. if cfg.CONF.profiler.enabled: profiler.init(cfg.CONF.profiler.hmac_keys) old_auth_ctx = context.ctx() if context.has_ctx() else None context.set_ctx(auth_ctx) try: if tx_queue: _process_tx_queue(tx_queue) if non_tx_queue: _process_non_tx_queue(non_tx_queue) finally: context.set_ctx(old_auth_ctx)
def _persist_job(job): ctx_serializer = context.RpcContextSerializer() ctx = ( ctx_serializer.serialize_context(context.ctx()) if context.has_ctx() else {} ) execute_at = (utils.utc_now_sec() + datetime.timedelta(seconds=job.run_after)) args = job.func_args arg_serializers = job.func_arg_serializers if arg_serializers: for arg_name, serializer_path in arg_serializers.items(): if arg_name not in args: raise exc.MistralException( "Serializable function argument %s" " not found in func_args=%s" % (arg_name, args)) try: serializer = importutils.import_class(serializer_path)() except ImportError as e: raise ImportError( "Cannot import class %s: %s" % (serializer_path, e) ) args[arg_name] = serializer.serialize(args[arg_name]) values = { 'run_after': job.run_after, 'target_factory_func_name': job.target_factory_func_name, 'func_name': job.func_name, 'func_args': args, 'func_arg_serializers': arg_serializers, 'auth_ctx': ctx, 'execute_at': execute_at, 'captured_at': None, 'key': job.key } return db_api.create_scheduled_job(values)
def _persist_job(cls, job): ctx_serializer = context.RpcContextSerializer() ctx = ( ctx_serializer.serialize_context(context.ctx()) if context.has_ctx() else {} ) execute_at = (utils.utc_now_sec() + datetime.timedelta(seconds=job.run_after)) args = job.func_args arg_serializers = job.func_arg_serializers if arg_serializers: for arg_name, serializer_path in arg_serializers.items(): if arg_name not in args: raise exc.MistralException( "Serializable function argument %s" " not found in func_args=%s" % (arg_name, args)) try: serializer = importutils.import_class(serializer_path)() except ImportError as e: raise ImportError( "Cannot import class %s: %s" % (serializer_path, e) ) args[arg_name] = serializer.serialize(args[arg_name]) values = { 'run_after': job.run_after, 'target_factory_func_name': job.target_factory_func_name, 'func_name': job.func_name, 'func_args': args, 'func_arg_serializers': arg_serializers, 'auth_ctx': ctx, 'execute_at': execute_at, 'captured_at': None } return db_api.create_scheduled_job(values)
def _with_auth_context(auth_ctx, func, *args, **kw): """Runs the given function with the specified auth context. :param auth_ctx: Authentication context. :param func: Function to run with the specified auth context. :param args: Function positional arguments. :param kw: Function keyword arguments. :return: Function result. """ old_auth_ctx = context.ctx() if context.has_ctx() else None context.set_ctx(auth_ctx) try: return func(*args, **kw) except _RETRY_ERRORS: LOG.exception("DB error detected, operation will be retried: %s", func) raise finally: context.set_ctx(old_auth_ctx)
def decorate(*args, **kw): _prepare() try: res = func(*args, **kw) queues = _get_queues() tx_queue = queues[0] non_tx_queue = queues[1] if not tx_queue and not non_tx_queue: return res auth_ctx = context.ctx() if context.has_ctx() else None def _within_new_thread(): # This is a new thread so we need to init a profiler again. if cfg.CONF.profiler.enabled: profiler.init(cfg.CONF.profiler.hmac_keys) old_auth_ctx = context.ctx() if context.has_ctx() else None context.set_ctx(auth_ctx) try: if tx_queue: _process_tx_queue(tx_queue) if non_tx_queue: _process_non_tx_queue(non_tx_queue) finally: context.set_ctx(old_auth_ctx) eventlet.spawn(_within_new_thread) finally: _clear() return res
def _with_auth_context(auth_ctx, func, *args, **kw): """Runs the given function with the specified auth context. :param auth_ctx: Authentication context. :param func: Function to run with the specified auth context. :param args: Function positional arguments. :param kw: Function keywork arguments. :return: Function result. """ old_auth_ctx = ctx.ctx() if ctx.has_ctx() else None ctx.set_ctx(auth_ctx) try: return func(*args, **kw) except db_exc.DBDeadlock as e: LOG.exception("DB deadlock detected, operation will be retried: %s", func) raise e finally: ctx.set_ctx(old_auth_ctx)
def _with_auth_context(auth_ctx, func, *args, **kw): """Runs the given function with the specified auth context. :param auth_ctx: Authentication context. :param func: Function to run with the specified auth context. :param args: Function positional arguments. :param kw: Function keywork arguments. :return: Function result. """ old_auth_ctx = ctx.ctx() if ctx.has_ctx() else None ctx.set_ctx(auth_ctx) try: return func(*args, **kw) except db_exc.DBDeadlock as e: LOG.exception( "DB deadlock detected, operation will be retried: %s", func ) raise e finally: ctx.set_ctx(old_auth_ctx)
def _with_auth_context(auth_ctx, func, *args, **kw): """Runs the given function with the specified auth context. :param auth_ctx: Authentication context. :param func: Function to run with the specified auth context. :param args: Function positional arguments. :param kw: Function keyword arguments. :return: Function result. """ old_auth_ctx = context.ctx() if context.has_ctx() else None context.set_ctx(auth_ctx) try: return func(*args, **kw) except _RETRY_ERRORS: LOG.exception( "DB error detected, operation will be retried: %s", func ) raise finally: context.set_ctx(old_auth_ctx)
def get_project_id(): if CONF.pecan.auth_enable and auth_ctx.has_ctx(): return auth_ctx.ctx().project_id else: return DEFAULT_PROJECT_ID
def schedule_call(factory_method_path, target_method_name, run_after, serializers=None, unique_key=None, **method_args): """Schedules call and lately invokes target_method. Add this call specification to DB, and then after run_after seconds service CallScheduler invokes the target_method. :param factory_method_path: Full python-specific path to factory method that creates a target object that the call will be made against. :param target_method_name: Name of a method which will be invoked. :param run_after: Value in seconds. :param serializers: map of argument names and their serializer class paths. Use when an argument is an object of specific type, and needs to be serialized. Example: { "result": "mistral.utils.serializer.ResultSerializer"} Serializer for the object type must implement serializer interface in mistral/utils/serializer.py :param unique_key: Unique key which in combination with 'processing' flag restricts a number of delayed calls if it's passed. For example, if we schedule two calls but pass the same unique key for them then we won't get two of them in DB if both have same value of 'processing' flag. :param method_args: Target method keyword arguments. """ ctx_serializer = context.RpcContextSerializer( context.JsonPayloadSerializer()) ctx = (ctx_serializer.serialize_context(context.ctx()) if context.has_ctx() else {}) execution_time = (datetime.datetime.now() + datetime.timedelta(seconds=run_after)) if serializers: for arg_name, serializer_path in serializers.items(): if arg_name not in method_args: raise exc.MistralException("Serializable method argument %s" " not found in method_args=%s" % (arg_name, method_args)) try: serializer = importutils.import_class(serializer_path)() except ImportError as e: raise ImportError("Cannot import class %s: %s" % (serializer_path, e)) method_args[arg_name] = serializer.serialize(method_args[arg_name]) values = { 'factory_method_path': factory_method_path, 'target_method_name': target_method_name, 'execution_time': execution_time, 'auth_context': ctx, 'serializers': serializers, 'unique_key': unique_key, 'method_arguments': method_args, 'processing': False } db_api.insert_or_ignore_delayed_call(values)