示例#1
0
def get_endpoint_for_project(service_name=None,
                             service_type=None,
                             region_name=None):
    if service_name is None and service_type is None:
        raise exceptions.MistralException(
            "Either 'service_name' or 'service_type' must be provided.")

    ctx = context.ctx()

    service_catalog = obtain_service_catalog(ctx)

    # When region_name is not passed, first get from context as region_name
    # could be passed to rest api in http header ('X-Region-Name'). Otherwise,
    # just get region from mistral configuration.
    region = (region_name or ctx.region_name)
    if service_name == 'keystone':
        # Determining keystone endpoint should be done using
        # keystone_authtoken section as this option is special for keystone.
        region = region or CONF.keystone_authtoken.region_name
    else:
        region = region or CONF.openstack_actions.default_region

    service_endpoints = service_catalog.get_endpoints(
        service_name=service_name,
        service_type=service_type,
        region_name=region)

    endpoint = None
    os_actions_endpoint_type = CONF.openstack_actions.os_actions_endpoint_type

    for endpoints in six.itervalues(service_endpoints):
        for ep in endpoints:
            # is V3 interface?
            if 'interface' in ep:
                interface_type = ep['interface']
                if os_actions_endpoint_type in interface_type:
                    endpoint = ks_endpoints.Endpoint(None, ep, loaded=True)
                    break
            # is V2 interface?
            if 'publicURL' in ep:
                endpoint_data = {
                    'url': ep['publicURL'],
                    'region': ep['region']
                }
                endpoint = ks_endpoints.Endpoint(None,
                                                 endpoint_data,
                                                 loaded=True)
                break

    if not endpoint:
        raise exceptions.MistralException(
            "No endpoints found [service_name=%s, service_type=%s,"
            " region_name=%s]" % (service_name, service_type, region))
    else:
        return endpoint
示例#2
0
def _extract_mistral_auth_params(headers):
    service_catalog = None

    if headers.get("X-Target-Auth-Uri"):
        insecure_header = headers.get('X-Target-Insecure', 'False')
        if insecure_header == 'False':
            insecure = False
        elif insecure_header == 'True':
            insecure = True
        else:
            raise (exc.MistralException(
                'X-Target-Insecure must be either "True", "False" or not '
                'provided. The default is "False".'))

        params = {
            # TODO(akovi): Target cert not handled yet
            'auth_cacert': None,
            'insecure': insecure,
            'auth_token': headers.get('X-Target-Auth-Token'),
            'auth_uri': headers.get('X-Target-Auth-Uri'),
            'tenant': headers.get('X-Target-Project-Id'),
            'user': headers.get('X-Target-User-Id'),
            'user_name': headers.get('X-Target-User-Name'),
            'region_name': headers.get('X-Target-Region-Name'),
            'is_target': True
        }
        if not params['auth_token']:
            raise (exc.MistralException(
                'Target auth URI (X-Target-Auth-Uri) target auth token '
                '(X-Target-Auth-Token) must be present'))

        # It's possible that target service catalog is not provided, in this
        # case, Mistral needs to get target service catalog dynamically when
        # talking to target openstack deployment later on.
        service_catalog = _extract_service_catalog_from_headers(
            headers
        )
    else:
        params = {
            'auth_uri': CONF.keystone_authtoken.www_authenticate_uri,
            'auth_cacert': CONF.keystone_authtoken.cafile,
            'insecure': False,
            'region_name': headers.get('X-Region-Name'),
            'is_target': False
        }

    params['service_catalog'] = service_catalog

    return params
示例#3
0
    def test_resume_fails(self, mock_fw):
        # Start and pause workflow.
        wb_service.create_workbook_v2(WORKBOOK_DIFFERENT_TASK_STATES)

        wf_ex = self.engine.start_workflow('wb.wf1', {})

        self.await_execution_paused(wf_ex.id)

        wf_ex = db_api.get_workflow_execution(wf_ex.id)

        self.assertEqual(states.PAUSED, wf_ex.state)

        # Simulate failure and check if it is handled.
        err = exc.MistralException('foo')

        with mock.patch.object(
                db_api,
                'acquire_lock',
                side_effect=err):

            self.assertRaises(
                exc.MistralException,
                self.engine.resume_workflow,
                wf_ex.id
            )

            mock_fw.assert_called_once_with(wf_ex.id, err)
示例#4
0
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)
示例#5
0
    def _on_message(self, request, message):
        LOG.debug('Received message %s',
                  request)

        is_async = request.get('async', False)
        rpc_ctx = request.get('rpc_ctx')
        redelivered = message.delivery_info.get('redelivered', None)
        rpc_method_name = request.get('rpc_method')
        arguments = request.get('arguments')
        correlation_id = message.properties['correlation_id']
        reply_to = message.properties['reply_to']

        if redelivered is not None:
            rpc_ctx['redelivered'] = redelivered

        rpc_context = self._set_auth_ctx(rpc_ctx)

        rpc_method = self._get_rpc_method(rpc_method_name)

        if not rpc_method:
            raise exc.MistralException("No such method: %s" % rpc_method_name)

        response = rpc_method(rpc_ctx=rpc_context, **arguments)

        if not is_async:
            self.publish_message(
                response,
                reply_to,
                correlation_id
            )
示例#6
0
def _extract_auth_params_from_headers(headers):
    target_service_catalog = None

    if headers.get("X-Target-Auth-Uri"):
        params = {
            # TODO(akovi): Target cert not handled yet
            'auth_cacert': None,
            'auth_token': headers.get('X-Target-Auth-Token'),
            'auth_uri': headers.get('X-Target-Auth-Uri'),
            'project_id': headers.get('X-Target-Project-Id'),
            'user_id': headers.get('X-Target-User-Id'),
            'user_name': headers.get('X-Target-User-Name'),
        }
        if not params['auth_token']:
            raise (exc.MistralException(
                'Target auth URI (X-Target-Auth-Uri) target auth token '
                '(X-Target-Auth-Token) must be present'))

        target_service_catalog = _extract_service_catalog_from_headers(headers)
    else:
        params = {
            'auth_cacert': CONF.keystone_authtoken.cafile,
            'auth_token': headers.get('X-Auth-Token'),
            'auth_uri': CONF.keystone_authtoken.auth_uri,
            'project_id': headers.get('X-Project-Id'),
            'user_id': headers.get('X-User-Id'),
            'user_name': headers.get('X-User-Name'),
        }

    params['target_service_catalog'] = target_service_catalog

    return params
示例#7
0
    def _get_action_descriptor(self):
        res = None

        action_name = self._get_action_name()
        namespace = self.wf_ex.workflow_namespace

        provider = action_service.get_system_action_provider()

        wb_name = self.wf_ex.runtime_context.get('wb_name')

        if wb_name:
            # First try to find the action within the same workbook
            # that the workflow belongs to.
            res = provider.find('%s.%s' % (wb_name, action_name),
                                namespace=namespace)

        if res is None:
            # Now try to find by the action name as it appears in the
            # workflow text.
            res = provider.find(action_name, namespace=namespace)

        if res is None:
            raise exc.MistralException(
                "Failed to find action [action_name=%s, namespace=%s]" %
                (action_name, namespace))

        return res
示例#8
0
    def evaluate(cls, script, context):
        if not _PY_MINI_RACER:
            raise exc.MistralException(
                "PyMiniRacer module is not available. Please install "
                "PyMiniRacer.")

        ctx = _PY_MINI_RACER.MiniRacer()
        return ctx.eval(('$ = {}; {}'.format(json.dumps(context), script)))
示例#9
0
    def evaluate(cls, script, context):
        if not _V8EVAL:
            raise exc.MistralException(
                "v8eval module is not available. Please install v8eval.")

        v8 = _V8EVAL.V8()
        return v8.eval(
            ('$ = %s; %s' %
             (json.dumps(context), script)).encode(encoding='UTF-8'))
示例#10
0
def set_transport_options(check_backend=True):
    # We can be sure that all needed transport options are registered
    # only if we at least once called method get_transport(). Because
    # this is the method that registers them.
    messaging.get_transport(CONF)

    backend = messaging.TransportURL.parse(CONF, CONF.transport_url).transport

    if check_backend and backend not in ['rabbit', 'kombu']:
        raise exc.MistralException("Unsupported backend: %s" % backend)
示例#11
0
    def _wait_for_result(self, correlation_id):
        """Waits for the result from the server.

        Waits for the result from the server, checks every second if
        a timeout occurred. If a timeout occurred - the `RpcTimeout` exception
        will be raised.
        """
        try:
            return self._listener.get_result(correlation_id, self._timeout)
        except moves.queue.Empty:
            raise exc.MistralException("RPC Request timeout")
示例#12
0
    def evaluate(cls, script, context):
        if not _PYV8:
            raise exc.MistralException(
                "PyV8 module is not available. Please install PyV8."
            )

        with _PYV8.JSContext() as ctx:
            # Prepare data context and way for interaction with it.
            ctx.eval('$ = %s' % json.dumps(context))

            return ctx.eval(script)
示例#13
0
    def __init__(self, definition_cfg):
        self.cfg = definition_cfg

        try:
            self.event_types = self.cfg['event_types']
            self.properties = self.cfg['properties']
        except KeyError as err:
            raise exceptions.MistralException(
                "Required field %s not specified" % err.args[0])

        if isinstance(self.event_types, six.string_types):
            self.event_types = [self.event_types]
示例#14
0
    def evaluate(cls, script, ctx):
        if not _PY_MINI_RACER:
            raise exc.MistralException(
                "PyMiniRacer module is not available. Please install "
                "PyMiniRacer."
            )

        js_ctx = _PY_MINI_RACER.MiniRacer()

        return js_ctx.eval(
            '$ = {}; {}'.format(utils.to_json_str(ctx), script)
        )
示例#15
0
    def _wait_for_result(self):
        """Waits for the result from the server.

        Waits for the result from the server, checks every second if
        a timeout occurred. If a timeout occurred - the `RpcTimeout` exception
        will be raised.
        """
        while not utils.get_thread_local(IS_RECEIVED):
            try:
                self.conn.drain_events(timeout=self._timeout)
            except socket.timeout:
                raise exc.MistralException("RPC Request timeout")
示例#16
0
def _get_rpc_info(rpc_backend, transport):

    if rpc_backend in ['amqp', 'zmq']:
        return {}
    elif transport and len(transport.hosts) == 1:
        # TODO(ddeja): Handle multiple hosts.
        return _get_rpc_info_from_transport_url(transport, rpc_backend)
    elif rpc_backend in ['rabbit', 'fake']:
        return _get_rabbit_info_from_oslo()
    else:
        raise exc.MistralException('Mistral cannot run with rpc_backend %s' %
                                   rpc_backend)
示例#17
0
def get_endpoint_for_project(service_name=None, service_type=None):
    if service_name is None and service_type is None:
        raise exceptions.MistralException(
            "Either 'service_name' or 'service_type' must be provided.")

    ctx = context.ctx()

    service_catalog = obtain_service_catalog(ctx)

    service_endpoints = service_catalog.get_endpoints(
        service_name=service_name,
        service_type=service_type,
        region_name=ctx.region_name)

    endpoint = None
    for endpoints in six.itervalues(service_endpoints):
        for ep in endpoints:
            # is V3 interface?
            if 'interface' in ep:
                interface_type = ep['interface']
                if CONF.os_actions_endpoint_type in interface_type:
                    endpoint = ks_endpoints.Endpoint(None, ep, loaded=True)
                    break
            # is V2 interface?
            if 'publicURL' in ep:
                endpoint_data = {
                    'url': ep['publicURL'],
                    'region': ep['region']
                }
                endpoint = ks_endpoints.Endpoint(None,
                                                 endpoint_data,
                                                 loaded=True)
                break

    if not endpoint:
        raise exceptions.MistralException(
            "No endpoints found [service_name=%s, service_type=%s,"
            " region_name=%s]" % (service_name, service_type, ctx.region_name))
    else:
        return endpoint
示例#18
0
    def evaluate(cls, script, ctx):
        if not _PYV8:
            raise exc.MistralException(
                "PyV8 module is not available. Please install PyV8."
            )

        with _PYV8.JSContext() as js_ctx:
            # Prepare data context and way for interaction with it.
            js_ctx.eval('$ = %s' % utils.to_json_str(ctx))

            result = js_ctx.eval(script)

            return _PYV8.convert(result)
示例#19
0
    def evaluate(cls, script, ctx):
        if not _V8EVAL:
            raise exc.MistralException(
                "v8eval module is not available. Please install v8eval."
            )

        v8 = _V8EVAL.V8()

        ctx_str = utils.to_json_str(ctx)

        return v8.eval(
            ('$ = %s; %s' % (ctx_str, script)).encode(encoding='UTF-8')
        )
示例#20
0
    def run(self, executor='blocking'):
        """Start the server."""
        self.conn = self._make_connection(
            self.host,
            self.port,
            self.user_id,
            self.password,
            self.virtual_host,
        )

        LOG.info("Connected to AMQP at %s:%s" % (self.host, self.port))

        try:
            conn = kombu.connections[self.conn].acquire(block=True)
            exchange = self._make_exchange(
                self.exchange,
                durable=self.durable_queue,
                auto_delete=self.auto_delete
            )
            queue = self._make_queue(
                self.topic,
                exchange,
                routing_key=self.routing_key,
                durable=self.durable_queue,
                auto_delete=self.auto_delete
            )
            with conn.Consumer(
                    queues=queue,
                    callbacks=[self._on_message_safe],
            ) as consumer:
                consumer.qos(prefetch_count=1)

                self._running.set()
                self._stopped.clear()

                while self.is_running:
                    try:
                        conn.drain_events(timeout=1)
                    except socket.timeout:
                        pass
                    except KeyboardInterrupt:
                        self.stop()

                        LOG.info("Server with id='{0}' stopped.".format(
                            self.server_id))

                        return
        except socket.error as e:
            raise exc.MistralException("Broker connection failed: %s" % e)
        finally:
            self._stopped.set()
示例#21
0
    def send_request_to_auth_server(self, url, access_token=None):
        certfile = CONF.keycloak_oidc.certfile
        keyfile = CONF.keycloak_oidc.keyfile
        cafile = CONF.keycloak_oidc.cafile or self.get_system_ca_file()
        insecure = CONF.keycloak_oidc.insecure

        verify = None

        if parse.urlparse(url).scheme == "https":
            verify = False if insecure else cafile

        cert = (certfile, keyfile) if certfile and keyfile else None

        headers = {}

        if access_token:
            headers["Authorization"] = "Bearer %s" % access_token

        try:
            resp = requests.get(
                url,
                headers=headers,
                verify=verify,
                cert=cert
            )
        except requests.ConnectionError:
            msg = _(
                "Can't connect to the keycloak server with address '%s'."
            ) % url

            LOG.exception(msg)

            raise exc.MistralException(message=msg)

        if resp.status_code == 401:
            LOG.warning(
                "HTTP response from OIDC provider:"
                " [%s] with WWW-Authenticate: [%s]",
                pprint.pformat(resp.text),
                resp.headers.get("WWW-Authenticate")
            )
        else:
            LOG.debug(
                "HTTP response from the OIDC provider: %s",
                pprint.pformat(resp.json())
            )

        resp.raise_for_status()

        return resp.json()
示例#22
0
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)
示例#23
0
def _get_rpc_info_from_transport_url(transport, rpc_backend):
    if rpc_backend in ['rabbit', 'fake']:
        durable_queues = CONF.oslo_messaging_rabbit.amqp_durable_queues
        auto_delete = CONF.oslo_messaging_rabbit.amqp_auto_delete
    else:
        raise exc.MistralException('Mistral cannot run with rpc_backend %s' %
                                   rpc_backend)

    transport_host = transport.hosts[0]

    return _prepare_conf_dict(transport_host.username, transport_host.password,
                              transport_host.hostname, transport_host.port,
                              transport.virtual_host or '/', durable_queues,
                              auto_delete)
示例#24
0
    def get_public_key(self, realm_name):
        keycloak_key_url = (CONF.keycloak_oidc.auth_url +
                            CONF.keycloak_oidc.public_cert_url % realm_name)

        response_json = self.send_request_to_auth_server(keycloak_key_url)

        keys = response_json.get('keys')

        if not keys:
            raise exc.MistralException(
                'Unexpected response structure from the keycloak server.')

        public_key = jwt_algos.RSAAlgorithm.from_jwk(json.dumps(keys[0]))

        return public_key
示例#25
0
    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)
示例#26
0
def _extract_auth_params_from_headers(headers):
    service_catalog = None

    if headers.get("X-Target-Auth-Uri"):
        params = {
            # TODO(akovi): Target cert not handled yet
            'auth_cacert': None,
            'insecure': headers.get('X-Target-Insecure', False),
            'auth_token': headers.get('X-Target-Auth-Token'),
            'auth_uri': headers.get('X-Target-Auth-Uri'),
            'project_id': headers.get('X-Target-Project-Id'),
            'user_id': headers.get('X-Target-User-Id'),
            'user_name': headers.get('X-Target-User-Name'),
            'region_name': headers.get('X-Target-Region-Name'),
            'is_target': True
        }
        if not params['auth_token']:
            raise (exc.MistralException(
                'Target auth URI (X-Target-Auth-Uri) target auth token '
                '(X-Target-Auth-Token) must be present'))

        # It's possible that target service catalog is not provided, in this
        # case, Mistral needs to get target service catalog dynamically when
        # talking to target openstack deployment later on.
        service_catalog = _extract_service_catalog_from_headers(
            headers
        )
    else:
        params = {
            'auth_cacert': CONF.keystone_authtoken.cafile,
            'insecure': False,
            'auth_token': headers.get('X-Auth-Token'),
            'auth_uri': CONF.keystone_authtoken.auth_uri,
            'project_id': headers.get('X-Project-Id'),
            'user_id': headers.get('X-User-Id'),
            'user_name': headers.get('X-User-Name'),
            'region_name': headers.get('X-Region-Name'),
            'is_target': False
        }

    params['service_catalog'] = service_catalog

    return params
示例#27
0
    def decorator(*args, **kwargs):
        try:
            return method(*args, **kwargs)
        except exc.MistralException:
            raise
        except MessagingTimeout:
            timeout = cfg.CONF.rpc_response_timeout
            raise exc.MistralException('This rpc call "%s" took longer than '
                                       'configured %s seconds.' %
                                       (method.__name__, timeout))
        except (client.RemoteError, exc.KombuException, Exception) as e:
            # Since we're going to transform the original exception
            # we need to log it as is.
            LOG.exception("Caught a messaging remote error."
                          " See details of the original exception.")

            if hasattr(e, 'exc_type') and hasattr(exc, e.exc_type):
                exc_cls = getattr(exc, e.exc_type)

                raise exc_cls(e.value)

            _wrap_exception_and_reraise(e)
示例#28
0
    def authenticate(self, req):
        certfile = CONF.keycloak_oidc.certfile
        keyfile = CONF.keycloak_oidc.keyfile
        cafile = CONF.keycloak_oidc.cafile or self.get_system_ca_file()
        insecure = CONF.keycloak_oidc.insecure

        if 'X-Auth-Token' not in req.headers:
            msg = _("Auth token must be provided in 'X-Auth-Token' header.")
            LOG.error(msg)
            raise exc.UnauthorizedException(message=msg)
        access_token = req.headers.get('X-Auth-Token')

        try:
            decoded = jwt.decode(access_token,
                                 algorithms=['RS256'],
                                 verify=False)
        except Exception as e:
            msg = _("Token can't be decoded because of wrong format %s")\
                % str(e)
            LOG.error(msg)
            raise exc.UnauthorizedException(message=msg)

        # Get user realm from parsed token
        # Format is "iss": "http://<host>:<port>/auth/realms/<realm_name>",
        __, __, realm_name = decoded['iss'].strip().rpartition('/realms/')

        # Get roles from from parsed token
        roles = ','.join(decoded['realm_access']['roles']) \
            if 'realm_access' in decoded else ''

        # NOTE(rakhmerov): There's a special endpoint for introspecting
        # access tokens described in OpenID Connect specification but it's
        # available in KeyCloak starting only with version 1.8.Final so we have
        # to use user info endpoint which also takes exactly one parameter
        # (access token) and replies with error if token is invalid.
        user_info_endpoint = ("%s/realms/%s/protocol/openid-connect/userinfo" %
                              (CONF.keycloak_oidc.auth_url, realm_name))

        verify = None
        if urllib.parse.urlparse(user_info_endpoint).scheme == "https":
            verify = False if insecure else cafile

        cert = (certfile, keyfile) if certfile and keyfile else None

        try:
            resp = requests.get(
                user_info_endpoint,
                headers={"Authorization": "Bearer %s" % access_token},
                verify=verify,
                cert=cert)
        except requests.ConnectionError:
            msg = _("Can't connect to keycloak server with address '%s'."
                    ) % CONF.keycloak_oidc.auth_url
            LOG.error(msg)
            raise exc.MistralException(message=msg)

        if resp.status_code == 401:
            LOG.warning(
                "HTTP response from OIDC provider:"
                " [%s] with WWW-Authenticate: [%s]", pprint.pformat(resp.text),
                resp.headers.get("WWW-Authenticate"))
        else:
            LOG.debug("HTTP response from OIDC provider: %s",
                      pprint.pformat(resp.text))

        resp.raise_for_status()

        LOG.debug("HTTP response from OIDC provider: %s",
                  pprint.pformat(resp.json()))

        req.headers["X-Identity-Status"] = "Confirmed"
        req.headers["X-Project-Id"] = realm_name
        req.headers["X-Roles"] = roles
示例#29
0
def _wrap_exception_and_reraise(exception):
    message = "%s: %s" % (exception.__class__.__name__, exception.args[0])

    raise exc.MistralException(message)
示例#30
0
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)