Openstack Cinder中建立volume过程的源码解析(2)

感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如果转载,请保留作者信息。
博客地址:http://blog.csdn.net/gaoxingnengjisuan
邮箱地址:dong.liu@siat.ac.cn


这篇博客我们来继续解析Openstack Cinder中建立volume过程的实现源码。上篇博客中我们解析了应用若干中间件对客户端发送过来的请求信息进行过滤处理,这里我们来分析方法class Resource(wsgi.Application)----def __call__(self, request),具体深入解析cinder建立volume的过程。

来看方法class Resource(wsgi.Application)----def __call__(self, request)的源码实现:

    def __call__(self, request): 
        """ 
        WSGI method that controls (de)serialization and method dispatch. 
        
        request = POST /v1/ecf0109bda814fa1a548af63f9ada370/volumes HTTP/1.0 
                  ......        
        {"volume": {"status": "creating", 
                    "availability_zone": null, 
                    "source_volid": null, 
                    "display_description": null, 
                    "snapshot_id": null, 
                    "user_id": null, 
                    "size": 1, 
                    "display_name": "shinian01", 
                    "imageRef": null, 
                    "attach_status": "detached", 
                    "volume_type": null, 
                    "project_id": null, 
                    "metadata": {}}} 
        """ 

        LOG.info("%(method)s %(url)s" % {"method": request.method, "url": request.url}) 

        # Identify the action, its arguments, and the requested 
        # content type 
        # 从request.environ中获取要执行的action方法; 
        action_args = self.get_action_args(request.environ) 
        action = action_args.pop('action', None) 
        """ 
        action_args = {'action': u'create', 'project_id': u'ecf0109bda814fa1a548af63f9ada370'} 
        action = create 
        action_args = {'project_id': u'ecf0109bda814fa1a548af63f9ada370'} 
        """ 
        # get_body:通过检测header,确定request body的内容类型;获取request中的body部分; 
        content_type, body = self.get_body(request) 
        """ 
        content_type = application/json 
        body = {"volume": {"status": "creating", "availability_zone": null, "source_volid": null, "display_description": null, 
"snapshot_id": null, "user_id": null, "size": 1, "display_name": "shinian01", "imageRef": null, "attach_status": "detached", 
"volume_type": null, "project_id": null, "metadata": {}}} 
        """ 

        # best_match_content_type:确定请求响应的内容类型; 
        accept = request.best_match_content_type() 
        """ 
        accept = application/json 
        """ 

        # NOTE(Vek): Splitting the function up this way allows for 
        #            auditing by external tools that wrap the existing 
        #            function.  If we try to audit __call__(), we can 
        #            run into troubles due to the @webob.dec.wsgify() 
        #            decorator. 
        """ 
        request = POST /v1/ecf0109bda814fa1a548af63f9ada370/volumes HTTP/1.0 
                  ......       
        action = create 
        action_args = {'project_id': u'ecf0109bda814fa1a548af63f9ada370'} 
        content_type = application/json 
        body = {"volume": {"status": "creating", "availability_zone": null, "source_volid": null, "display_description": null, 
"snapshot_id": null, "user_id": null, "size": 1, "display_name": "shinian01", "imageRef": null, "attach_status": "detached", 
"volume_type": null, "project_id": null, "metadata": {}}} 
        accept = application/json 
        """ 
        return self._process_stack(request, action, action_args, content_type, body, accept)

在这个方法中,主要实现了对request的若干预处理操作。

我们来看语句content_type, body = self.get_body(request),所实现的功能是通过检测header,确定request body的内容类型;获取request中的body部分;

输出示例:

content_type = application/json

body = {"volume": {"status": "creating", "availability_zone": null, "source_volid": null, "display_description": null, "snapshot_id": null, "user_id": null, "size": 1, "display_name": "shinian01", "imageRef": null, "attach_status": "detached", "volume_type": null, "project_id": null, "metadata": {}}}

下面我们来看方法_process_stack,这个方法主要完成了请求信息完整的执行过程。我们具体来看方法_process_stack的源码实现:

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

        # Get the implementing method 
        try: 
            # get_method:根据request确定要执行的action方法以及相关的扩展方法; 
            meth, extensions = self.get_method(request, action, content_type, body) 
            """ 
            request = POST /v1/ecf0109bda814fa1a548af63f9ada370/volumes HTTP/1.0 
                      ...... 
            action = create 
            content_type = application/json 
            body = {"volume": {"status": "creating", 
                               "availability_zone": null, 
                               "source_volid": null, 
                               "display_description": null, 
                               "snapshot_id": null, 
                               "user_id": null, 
                               "size": 1, 
                               "display_name": "shinian01", 
                               "imageRef": null, 
                               "attach_status": "detached", 
                               "volume_type": null, 
                               "project_id": null, 
                               "metadata": {}} 
                    } 
             
            meth = <bound method VolumeController.create of <cinder.api.v1.volumes.VolumeController object at 0x2998b10>> 
            extensions = [<bound method SchedulerHintsController.create of <cinder.api.contrib.scheduler_hints.SchedulerHintsController object at 0x2985a90>>] 
            注:meth为从控制器中根据action的值获取相应的方法(例如:cinder.api.v1.volumes.VolumeController.create); extensions为根据控制器和action的值获取相应的扩展方法;      
            """ 
        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)) 

        # Now, deserialize the request body... 
        # 反序列化请求body的实现; 
        # content_type = application/json 
        try: 
            if content_type: 
                #确定反序列化的方法,实现对body的反序列化操作; 
                #meth = <bound method VolumeController.create of <cinder.api.v1.volumes.VolumeController object at 0x29cdb50>> 
                #content_type = application/json 
                #body = {"volume": {"status": "creating", "availability_zone": null, "source_volid": null, "display_description": null, "snapshot_id": null, "user_id": null, "size": 1, "display_name": "shinian01", "imageRef": null, "attach_status": "detached", "volume_type": null, "project_id": null, "metadata": {}}} 
                contents = self.deserialize(meth, content_type, body) 
            else: 
                contents = {} 
            #contents = {'body': {u'volume': {u'status': u'creating', 
            #                                 u'user_id': None, 
            #                                 u'imageRef': None, 
            #                                 u'availability_zone': None, 
            #                                 u'attach_status': u'detached', 
            #                                 u'display_description': None, 
            #                                 u'metadata': {}, 
            #                                 u'source_volid': None, 
            #                                 u'snapshot_id': None, 
            #                                 u'display_name': u'shinian01', 
            #                                 u'project_id': None, 
            #                                 u'volume_type': None, 
            #                                 u'size': 1}}} 
       
        except exception.InvalidContentType: 
            msg = _("Unsupported Content-Type") 
            return Fault(webob.exc.HTTPBadRequest(explanation=msg)) 
        except exception.MalformedRequestBody: 
            msg = _("Malformed request body") 
            return Fault(webob.exc.HTTPBadRequest(explanation=msg)) 

        # Update the action args 
        action_args.update(contents) 
        """ 
        action_args = {'body': {u'volume': {u'status': u'creating', 
                                            u'user_id': None, 
                                            u'imageRef': None, 
                                            u'availability_zone': None, 
                                            u'attach_status': u'detached', 
                                            u'display_description': None, 
                                            u'metadata': {}, 
                                            u'source_volid': None, 
                                            u'snapshot_id': None, 
                                            u'display_name': u'shinian01', 
                                            u'project_id': None, 
                                            u'volume_type': None, 
                                            u'size': 1}}, 
                       'project_id': u'ecf0109bda814fa1a548af63f9ada370'} 
        """ 

        project_id = action_args.pop("project_id", None) 
        """ 
        project_id = ecf0109bda814fa1a548af63f9ada370 
        """         
        context = request.environ.get('cinder.context') 
        """ 
        context = <cinder.context.RequestContext object at 0x29364d0> 
        """ 
        
        if (context and project_id and (project_id != context.project_id)): 
            msg = _("Malformed request url") 
            return Fault(webob.exc.HTTPBadRequest(explanation=msg)) 

        # Run pre-processing extensions 
        response, post = self.pre_process_extensions(extensions, request, action_args) 
        """ 
        extensions = [<bound method SchedulerHintsController.create of <cinder.api.contrib.scheduler_hints.SchedulerHintsController object at 0x2985a90>>] 
        request = POST /v1/ecf0109bda814fa1a548af63f9ada370/volumes HTTP/1.0 
                  ...... 
        action_args = {'body': {u'volume': {u'status': u'creating', 
                                            u'user_id': None, 
                                            u'imageRef': None, 
                                            u'availability_zone': None, 
                                            'scheduler_hints': {}, 
                                            u'attach_status': u'detached', 
                                            u'display_description': None, 
                                            u'metadata': {}, 
                                            u'source_volid': None, 
                                            u'snapshot_id': None, 
                                            u'display_name': u'shinian01', 
                                            u'project_id': None, 
                                            u'volume_type': None, 
                                            u'size': 1}}} 
        
        response = None 
        post = <listreverseiterator object at 0x2969d10> 
        """ 
        
        # response = None 
        if not response: 
            try: 
                with ResourceExceptionHandler(): 
                    action_result = self.dispatch(meth, request, action_args) 
                    # meth = <bound method VolumeController.create of <cinder.api.v1.volumes.VolumeController object at 0x2998b10>> 
                    # request = POST /v1/ecf0109bda814fa1a548af63f9ada370/volumes HTTP/1.0 
                    #           ...... 
                    # action_args = {'body': {u'volume': {u'status': u'creating', 
                    #                                     u'user_id': None, 
                    #                                     u'imageRef': None, 
                    #                                     u'availability_zone': None, 
                    #                                     'scheduler_hints': {}, 
                    #                                     u'attach_status': u'detached', 
                    #                                     u'display_description': None, 
                    #                                     u'metadata': {}, 
                    #                                     u'source_volid': None, 
                    #                                     u'snapshot_id': None, 
                    #                                     u'display_name': u'shinian01', 
                    #                                     u'project_id': None, 
                    #                                     u'volume_type': None, 
                    #                                     u'size': 1}}} 
                    # 
                    # action_result = {'volume': {'status': 'creating', 
                    #                             'display_name': u'shinian01', 
                    #                             'attachments': [], 
                    #                             'availability_zone': 'nova', 
                    #                             'bootable': 'false', 
                    #                             'created_at': datetime.datetime(2014, 3, 27, 7, 39, 42, 673944), 
                    #                             'display_description': None, 
                    #                             'volume_type': 'None', 
                    #                             'snapshot_id': None, 
                    #                             'source_volid': None, 
                    #                             'metadata': {}, 
                    #                             'id': 'e50e8f12-cc56-47eb-a488-64ae9f442464', 
                    #                             'size': 1}} 
                    
            except Fault as ex: 
                response = ex 

        if not response: 
            # No exceptions; convert action_result into a 
            # 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 
            """ 
            action_result = {'volume': {'status': 'creating', 
                                        'display_name': u'shinian01', 
                                        'attachments': [], 
                                        'availability_zone': 'nova', 
                                        'bootable': 'false', 
                                        'created_at': datetime.datetime(2014, 3, 27, 7, 39, 42, 673944), 
                                        'display_description': None, 
                                        'volume_type': 'None', 
                                        'snapshot_id': None, 
                                        'source_volid': None, 
                                        'metadata': {}, 
                                        'id': 'e50e8f12-cc56-47eb-a488-64ae9f442464', 
                                        'size': 1}} 
            resp_obj = <cinder.api.openstack.wsgi.ResponseObject object at 0x2969d90> 
            response = None 
            """ 
            
            # Run post-processing extensions 
            # resp_obj = <cinder.api.openstack.wsgi.ResponseObject object at 0x2969d90> 
            if resp_obj: 
                # resp_obj = <cinder.api.openstack.wsgi.ResponseObject object at 0x2969d90> 
                # request = POST /v1/ecf0109bda814fa1a548af63f9ada370/volumes HTTP/1.0 
                #           ...... 
                _set_request_id_header(request, resp_obj) 
                # Do a preserialize to set up the response object 
                serializers = getattr(meth, 'wsgi_serializers', {}) 
                """ 
                meth = <bound method VolumeController.create of <cinder.api.v1.volumes.VolumeController object at 0x2998b10>> 
                serializers = {'xml': <class 'cinder.api.v1.volumes.VolumeTemplate'>} 
                """ 
                
                resp_obj._bind_method_serializers(serializers) 
                
                # hasattr(meth, 'wsgi_code') = False 
                if hasattr(meth, 'wsgi_code'): 
                    resp_obj._default_code = meth.wsgi_code 

                # accept = application/json 
                # self.default_serializers = {'xml': <class 'cinder.api.openstack.wsgi.XMLDictSerializer'>, 'json': <class 'cinder.api.openstack.wsgi.JSONDictSerializer'>} 
                resp_obj.preserialize(accept, self.default_serializers) 

                # Process post-processing extensions 
                response = self.post_process_extensions(post, resp_obj, request, action_args) 
                """ 
                post = <listreverseiterator object at 0x2969d10> 
                resp_obj = <cinder.api.openstack.wsgi.ResponseObject object at 0x2969d90> 
                action_args = {'body': {u'volume': {u'status': u'creating', 
                                                    u'user_id': None, 
                                                    u'imageRef': None, 
                                                    u'availability_zone': None, 
                                                    'scheduler_hints': {}, 
                                                    u'attach_status': u'detached', 
                                                    u'display_description': None, 
                                                    u'metadata': {}, 
                                                    u'source_volid': None, 
                                                    u'snapshot_id': None, 
                                                    u'display_name': u'shinian01', 
                                                    u'project_id': None, 
                                                    u'volume_type': None, 
                                                    u'size': 1}}} 
                response = None 
                """ 
            
            # resp_obj = <cinder.api.openstack.wsgi.ResponseObject object at 0x2969d90> 
            # response = None 
            if resp_obj and not response: 
                response = resp_obj.serialize(request, accept, self.default_serializers) 
                """ 
                accept = application/json 
                self.default_serializers = {'xml': <class 'cinder.api.openstack.wsgi.XMLDictSerializer'>, 'json': <class 'cinder.api.openstack.wsgi.JSONDictSerializer'>} 
                response = 200 OK 
                x-compute-request-id: req-66a5680c-d160-493e-b333-7caacefc80f7 
                Content-Type: application/json 
                Content-Length: 344 

                {"volume": {"status": "creating", 
                            "display_name": "shinian01", 
                            "attachments": [], 
                            "availability_zone": "nova", 
                            "bootable": "false", 
                            "created_at": "2014-03-27T07:39:42.673944", 
                            "display_description": null, 
                            "volume_type": "None", 
                            "snapshot_id": null, 
                            "source_volid": null, 
                            "metadata": {}, 
                            "id": "e50e8f12-cc56-47eb-a488-64ae9f442464", 
                            "size": 1}} 
                """ 

        try: 
            msg_dict = dict(url=request.url, status=response.status_int) 
            # url = http://172.21.5.164:8776/v1/ecf0109bda814fa1a548af63f9ada370/volumes 
            # status = 200 
            # msg_dict = {'status': 200, 
            #             'url': 'http://172.21.5.164:8776/v1/ecf0109bda814fa1a548af63f9ada370/volumes            #             '} 
            
            msg = _("%(url)s returned with HTTP %(status)d") % msg_dict 
        except AttributeError as e: 
            msg_dict = dict(url=request.url, e=e) 
            msg = _("%(url)s returned a fault: %(e)s") % msg_dict 

        LOG.info(msg) 

        """ 
        response = 200 OK 
        x-compute-request-id: req-66a5680c-d160-493e-b333-7caacefc80f7 
        Content-Type: application/json 
        Content-Length: 344 

        {"volume": {"status": "creating", 
                    "display_name": "shinian01", 
                    "attachments": [], 
                    "availability_zone": "nova", 
                    "bootable": "false", 
                    "created_at": "2014-03-27T07:39:42.673944", 
                    "display_description": null, 
                    "volume_type": "None", 
                    "snapshot_id": null, 
                    "source_volid": null, 
                    "metadata": {}, 
                    "id": "e50e8f12-cc56-47eb-a488-64ae9f442464", 
                    "size": 1}} 
        """ 
        return response
2.获取要执行的action方法及其相关的扩展方法

来看上述方法中的语句:

meth, extensions = self.get_method(request, action, content_type, body)

这条语句实现的功能是根据request确定要执行的action方法以及相关的扩展方法;

我们来看方法get_method的源码实现:

    def get_method(self, request, action, content_type, body): 
        """ 
        根据request确定要执行的action方法以及相关的扩展方法; 
        
        # request = POST /v1/ecf0109bda814fa1a548af63f9ada370/volumes HTTP/1.0 
        #           ...... 
        # action = create 
        # content_type = application/json 
        # body = {"volume": {"status": "creating", "availability_zone": null, "source_volid": null, "display_description": null, "snapshot_id": null, "user_id": null, "size": 1, "display_name": "shinian01", "imageRef": null, "attach_status": "detached", "volume_type": null, "project_id": null, "metadata": {}}}  
        """ 

        # Look up the method 
        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 ['action', 'create', 'delete']): 
                # Propagate the error 
                raise 
        else: 
            return meth, self.wsgi_extensions.get(action, []) 

        if action == 'action': 
            # OK, it's an action; figure out which action... 
            mtype = _MEDIA_TYPE_MAP.get(content_type) 
            action_name = self.action_peek[mtype](body) 
            LOG.debug("Action body: %s" % body) 
        else: 
            action_name = action 

        # Look up the action method 
        return (self.wsgi_actions[action_name], 
                self.wsgi_action_extensions.get(action_name, []))

这个方法的实现主要分为两部分,分别是获取请求信息中action方法的具体路径和获取action方法相关的扩展方法及其具体路径。

首先来看语句:

try: 
    if not self.controller: 
        meth = getattr(self, action) 
    else: 

        meth = getattr(self.controller, action) 

实现了获取请求信息中action方法的具体路径。在这里self.controller = <cinder.api.v1.volumes.VolumeController object at 0x29cdb50>,在方法__call__调用之前就进行了初始化操作,指定了匹配于请求信息的action方法所在的控制器类。此时的输出示例:meth = <bound method VolumeController.create of <cinder.api.v1.volumes.VolumeController object at 0x29cdb50>>,从而指定了匹配于请求信息的action方法的具体路径。

再来看语句:

self.wsgi_extensions.get(action, [])

实现了获取action方法相关的扩展方法及其具体路径。在这里

self.wsgi_extensions = {

'create': [<bound method SchedulerHintsController.create of <cinder.api.contrib.scheduler_hints.SchedulerHintsController object at 0x29b9ad0>>], 

'detail': [<bound method VolumeTenantAttributeController.detail of <cinder.api.contrib.volume_tenant_attribute.VolumeTenantAttributeController object at 0x29b9c50>>, <bound method VolumeHostAttributeController.detail of <cinder.api.contrib.volume_host_attribute.VolumeHostAttributeController object at 0x298a750>>, <bound method VolumeMigStatusAttributeController.detail of <cinder.api.contrib.volume_mig_status_attribute.VolumeMigStatusAttributeController object at 0x298a990>>, <bound method VolumeImageMetadataController.detail of <cinder.api.contrib.volume_image_metadata.VolumeImageMetadataController object at 0x298add0>>], 

'show': [<bound method VolumeTenantAttributeController.show of <cinder.api.contrib.volume_tenant_attribute.VolumeTenantAttributeController object at 0x29b9c50>>,<bound method VolumeHostAttributeController.show of <cinder.api.contrib.volume_host_attribute.VolumeHostAttributeController object at 0x298a750>>,<bound method VolumeMigStatusAttributeController.show of <cinder.api.contrib.volume_mig_status_attribute.VolumeMigStatusAttributeController object at 0x298a990>>,<bound method VolumeImageMetadataController.show of <cinder.api.contrib.volume_image_metadata.VolumeImageMetadataController object at 0x298add0>>]}

这也是在类class Resource(wsgi.Application)的初始化方法__init__执行的过程中得到的,也就是类的初始化过程中得到的。

因为action = create,所以这里self.wsgi_extensions.get(action, []) = {'create': [<bound method SchedulerHintsController.create of <cinder.api.contrib.scheduler_hints.SchedulerHintsController object at 0x29b9ad0>>]},这就是匹配于action = create的扩展方法的具体路径。

在这个方法中,我们获取了请求信息中action方法的具体路径和action方法相关的扩展方法及其具体路径,为实现volume的建立的后续工作做好了准备。

我们回到语句:meth, extensions = self.get_method(request, action, content_type, body)

输出示例:
meth = <bound method VolumeController.create of <cinder.api.v1.volumes.VolumeController object at 0x2998b10>>
extensions = [<bound method SchedulerHintsController.create of <cinder.api.contrib.scheduler_hints.SchedulerHintsController object at 0x2985a90>>]

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值