openstack组件nova代码解析(ocata版本)--nova.api.openstack.wsgi

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

随手记录一下查看nova代码时的笔记

nova.api.openstack.wsgi.ActionDispatcher模块解析:

class ActionDispatcher(object):
    """ 通过action名将方法名映射到本地方法。 """

    def dispatch(self, *args, **kwargs):
        """ 查找并调用local方法."""
        action = kwargs.pop('action', 'default')
        action_method = getattr(self, str(action), self.default)
        return action_method(*args, **kwargs)

    def default(self, data):#子类必须实现的方法
        raise NotImplementedError()

nova.api.openstack.wsgi.JSONDeserializer模块解析:

class JSONDeserializer(ActionDispatcher):

    def _from_json(self, datastring):
        """将json字符串转为json对象"""
        try:
            return jsonutils.loads(datastring)
        except ValueError:
            msg = _("cannot understand JSON")
            raise exception.MalformedRequestBody(reason=msg)

    def deserialize(self, datastring, action='default'):
        return self.dispatch(datastring, action=action)

    def default(self, datastring):
        return {
    'body': self._from_json(datastring)}

nova.api.openstack.wsgi.JSONDictSerializer模块解析:

class JSONDictSerializer(ActionDispatcher):
    """ 将JSON请求body体序列化。 """

    def serialize(self, data, action='default'):
        return self.dispatch(data, action=action)

    def default(self, data):
        return six.text_type(jsonutils.dumps(data))#调用six模块实现python2与3兼容处理

nova.api.openstack.wsgi.Resource模块解析:

class Resource(wsgi.Application):
    """ 处理(反)序列化和控制器分发的WSGI app.

    WSGI app读取RoutesMiddleware提供的路由信息,并在其控制器上调用请求的action方法。
    所有控制器action方法都必须接受'req'参数,它是传入的wsgi.Request。  
    如果操作是PUT或POST,控制器方法还必须接受一个“body”参数(反序列化的请求体)。 
    可能会引发webob.exc异常或返回dict,, 它将被按请求的内容类型序列化。

    从webob.exc.HTTPException继承的Exceptions 将自动包装在Fault() 中,以提供API友好的错误响应。 

    """
    support_api_request_version = False

    def __init__(self, controller, inherits=None):
        """:param controller: routes库创建的对象 
           :param inherits: 另一种资源对象,该资源应该继承的扩展extensions.  应用于父资源的任何action extensions也将应用于此资源。
        """

        self.controller = controller

        self.default_serializers = dict(json=JSONDictSerializer)

        #  复制actions字典
        self.wsgi_actions = {}
        if controller:
            self.register_actions(controller)

        #  保存插件(extensions)的映射 
        self.wsgi_extensions = {}
        self.wsgi_action_extensions = {}
        self.inherits = inherits

    def register_actions(self, controller):
        """ 使用此资源注册控制器actions"""

        actions = getattr(controller, 'wsgi_actions', {})
        for key, method_name in actions.items():
            self.wsgi_actions[key] = getattr(controller, method_name)

    def register_extensions(self, controller):
        """ 使用此资源注册控制器extensions"""

        extensions = getattr(controller, 'wsgi_extensions', [])
        for method_name, action_name in extensions:
            #  查找extending方法
            extension = getattr(controller, method_name)

            if action_name:
                # action_name还不存在时,初始化列表...
                if action_name not in self.wsgi_action_extensions:
                    self.wsgi_action_extensions[action_name] = []
                self.wsgi_action_extensions[action_name].append(extension)
            else:
                # method_name还不存在时,初始化列表
                if method_name not in self.wsgi_extensions:
                    self.wsgi_extensions[method_name] = []
                self.wsgi_extensions[method_name].append(extension)

    def get_action_args(self, request_environment):
        """ 解析routes创建的字典 ."""

        #  检查控制器是否有重写get_action_args(),有就调用重写的方法
        if hasattr(self.controller, 'get_action_args'):
            return self.controller.get_action_args(request_environment)

        try:
            args = request_environment['wsgiorg.routing_args'][1].copy()
        except (KeyError, IndexError, AttributeError):
            return {}

        try:
            del args['controller']
        except KeyError:
            pass

        try:
            del args['format']
        except KeyError:
            pass

        return args

    def get_body(self, request):
        content_type = request.get_content_type()

        return content_type, request.body

    def deserialize(self, body):
        return JSONDeserializer().deserialize(body)

    def process_extensions(self, extensions, resp_obj, request,
                           action_args):
        """处理插件(extensions)"""
        for ext in extensions:
            response = None
            #常规函数可以得到post-processing...
            try:
                with ResourceExceptionHandler():
                    response = ext(req=request, resp_obj=resp_obj,
                                   **action_args)
            except exception.VersionNotFoundForAPIMethod:
                #  如果附加的方法扩展(@wsgi.extends)名没有版本匹配,则不认为是错误。 跳过代码执行。
                continue
            except Fault as ex:
                response = ex

            # response存在返回它,提前退出。 这实际上是代表一种失败模式。 responseNone代表成功。
            if response:
                return response

        return None

    def _should_have_body(self, request):
        return request.method in _METHODS_WITH_BODY #POST和PUT应该包含body内容

    @webob.dec.wsgify(RequestClass=Request)
    def __call__(self, request):
        """ 控制(反)序列化和方法分派的WSGI方法。 """

        if self.support_api_request_version:
            #  根据报头设置请求的API版本 
            try:
                request.set_api_version_request()
            except exception.InvalidAPIVersionString as e:
                return Fault(webob.exc.HTTPBadRequest(
                    explanation=e.format_message()))
            except exception.InvalidGlobalAPIVersion as e:
                return Fault(webob.exc.HTTPNotAcceptable(
                    explanation=e.format_message()))

        # 标识action,  参数和请求的内容类型 
        action_args = self.get_action_args(request.environ)
        action = action_args.pop('action', None)

        # 过滤掉InvalidContentTypes
        try:
            content_type, body = self.get_body(request)
            accept = request.best_match_content_type()
        except exception.InvalidContentType:
            msg = _("Unsupported Content-Type")
            return Fault(webob.exc.HTTPUnsupportedMediaType(explanation=msg))

        # 通过这种方式拆分函数,可以通过包装现有函数的外部工具进行审计。
        #  由于@webob.dec.wsgify()装饰器的作用, 如果试图审计__call__(),可能会遇到报错
        return self._process_stack(request, action, action_args,
                               content_type, body, accept)

    def _process_stack(self, request, action, action_args,
                       content_type, body, accept):
        """ 实现processing stack."""

        #  获取实现方法
        try:
            meth, extensions = self.get_method(request, action,
                                               content_type, body)
        except (AttributeError, TypeError):
            return Fault(webob.exc.HTTPNotFound())
        except KeyError as ex:
            msg = _("There is no such action: %s") % ex.args[0]
            return Fault(webob.exc.HTTPBadRequest(explanation=msg))
        except exception.MalformedRequestBody:
            msg = _("Malformed request body")
            return Fault(webob.exc.HTTPBadRequest(explanation=msg))

        if body:
            msg = _("Action: '%(action)s', calling method: %(meth)s, body: "
                    "%(body)s") % {
    'action': action,
                                   'body': six.text_type(body, 'utf-8'),
                                   'meth': str(meth)}
            LOG.debug(strutils.mask_password(msg))
        else:
            LOG.debug("Calling method '%(meth)s'",
                      {
    'meth': str(meth)})

        # 反序列化request body...
        try:
            contents = {}
            if self._should_have_body(request):
                #  允许请求方法为PUT和POST的body为空 
                if request.content_length == 0:
                    contents = {
    'body': None}
                else:
                    contents = self.deserialize(body)
        except exception.MalformedRequestBody:
            msg = _("Malformed request body")
            return Fault(webob.exc.HTTPBadRequest(explanation=msg))

        #  更新action参数
        action_args.update(contents)
        #比对project_id
        project_id = action_args.pop("project_id", None)
        context = request.environ.get('nova.context')
        if (context and project_id and (project_id != context.project_id)):
            msg = _("Malformed request URL: URL's project_id '%(project_id)s'"
                    " doesn't match Context's project_id"
                    " '%(context_project_id)s'") % \
                    {
    'project_id': project_id,
                     'context_project_id': context.project_id}
            return Fault(webob.exc.HTTPBadRequest(explanation=msg))

        response = None
        try:
            with ResourceExceptionHandler():
                action_result = self.dispatch(meth, request, action_args)
        except Fault as ex:
            response = ex

        if not response:
            # 无异常时; action_result转换ResponseObject
            resp_obj = None
            if type(action_result) is dict or action_result is None:
                resp_obj = ResponseObject(action_result)
            elif isinstance(action_result, ResponseObject):
                resp_obj = action_result
            else:
                response = action_result

            #  运行post-processing插件(extensions)
            if resp_obj:
                #  预序列化以设置response对象
                if hasattr(meth, 'wsgi_code'):
                    resp_obj._default_code = meth.wsgi_code
                # 调用process_extensions
                response = self.process_extensions(extensions, resp_obj,
                                                        request, action_args)

            if resp_obj and not response:
                response = resp_obj.serialize(request, accept)

        if hasattr(response, 'headers'):
            for hdr, val in list(response.headers.items()):
                if six.PY2:
                    # Py2.X Headers 必须是字节字符串
                    response.headers[hdr] = utils.utf8(val)
                else:
                    # Py3.X Headers 必须是utf-8符串
                    response.headers[hdr] = encodeutils.safe_decode(
                            utils.utf8(val))

            if not request.api_version_request.is_null():
                response.headers[API_VERSION_REQUEST_HEADER] = \
                    'compute ' + request.api_version_request.get_string()
                response.headers[LEGACY_API_VERSION_REQUEST_HEADER] = \
                    request.api_version_request.get_string()
                response.headers.add('Vary', API_VERSION_REQUEST_HEADER)
                response.headers.add('Vary', LEGACY_API_VERSION_REQUEST_HEADER)

        return response

    def get_method(self, request, action, content_type, body):
        meth, extensions = self._get_method(request,
                                            action,
                                            content_type,
                                            body)
        if self.inherits:
            _meth, parent_ext = self.inherits.get_method(request,
                                                         action,
                                                         content_type,
                                                         body)
            extensions.extend(parent_ext)
        return meth, extensions

    def _get_method(self, request, action, content_type, body):
        """ 查找action-specific的方法及其extensions"""
        #  查找方法 
        try:
            if not self.controller:
                meth = getattr(self, action)
            else:
                meth = getattr(self.controller, action)
        except AttributeError:
            if (not self.wsgi_actions or
                    action not in _ROUTES_METHODS + ['action']):
                # 找不到抛出error
                raise
        else:
            return meth, self.wsgi_extensions.get(action, [])

        if action == 'action':
            #此方法会从body中找出action_name,此时的body应该是只有一个key的已经被序列化后的json。
            action_name = action_peek(body)
        else:
            action_name = action

        #  查找action方法
        return (self.wsgi_actions[action_name],
                self.wsgi_action_extensions.get(action_name, []))

    def dispatch(self, method, request, action_args):
        """分发一个call到action-specific方法."""

        try:
            return method(req=request, **action_args)
        except exception.VersionNotFoundForAPIMethod:
            return Fault(webob.exc.HTTPNotFound())

转载于:https://my.oschina.net/jennerlo/blog/3005516

你可能感兴趣的:(json,python)