阅读导读:
1.cinder中卷的建立的过程中,客户端传递过来的request的执行过程是怎样的?
2.__call__方法都通过什么方法封装?
3.如何调用指定中间件的__call__方法?
感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
从这篇博客开始,我将以cinder中卷的建立的过程的源码解析为例,来说明客户端传递过来的request的执行过程。
示例:
我们执行命令:
- cinder create --display_name shinian01 1
复制代码
便可获取响应信息:- +---------------------+--------------------------------------+
- | Property | Value |
- +---------------------+--------------------------------------+
- | attachments | [] |
- | availability_zone | nova |
- | bootable | false |
- | created_at | 2014-03-29T12:36:23.998481 |
- | display_description | None |
- | display_name | shinian01 |
- | id | 3f0aa242-9dab-48db-b63d-92b6dd38cf20 |
- | metadata | {} |
- | size | 1 |
- | snapshot_id | None |
- | source_volid | None |
- | status | creating |
- | volume_type | None |
- +---------------------+--------------------------------------+
复制代码
实际上,cinder模块中实现命令行指定操作主要有以下几个步骤:
- 1.应用若干中间件对客户端发送过来的请求信息进行过滤(封装)处理;
- 2.获取要执行的方法及其相关的扩展方法;
- 3.请求中body部分的反序列化;
- 4.获取的扩展方法的执行;
- 5.执行具体的方法,如卷的建立方法create;
- 6.响应信息的生成。
下面我们来进行逐条分析:
1.应用若干中间件对客户端发送过来的请求信息进行过滤(封装)处理
当请求信息过来以后,会先后调用以下类中的__call__方法对其进行处理:
- FaultWrapper->RequestBodySizeLimiter->CinderKeystoneContext->APIRouter->Resource
其中:
- FaultWrapper:关于错误异常的处理;
- RequestBodySizeLimiter:请求信息中body部分长度的检测;
- CinderKeystoneContext:身份验证相关;
- APIRouter:请求信息的路由处理;
- Resource:action执行前的一些预处理;
这五个类中的__call__方法都是以语句@webob.dec.wsgify(RequestClass=Request)实现装饰的,其中前三个类为cinder定义的中间件,这里不进行深入的分析。我们具体来看APIRouter和Resource。
1.1 class APIRouter----def __call__
这里调用的是类APIRouter的父类class
Router中的__call__方法,我们来看具体代码:
-
- class Router(object):
- """WSGI middleware that maps incoming requests to WSGI apps."""
- def __init__(self, mapper):
- """Create a router for the given routes.Mapper.
- Each route in `mapper` must specify a 'controller', which is a
- WSGI app to call. You'll probably want to specify an 'action' as
- well and have your controller be an object that can route
- the request to the action-specific method.
- Examples:
- mapper = routes.Mapper()
- sc = ServerController()
- # Explicit mapping of one route to a controller+action
- mapper.connect(None, '/svrlist', controller=sc, action='list')
- # Actions are all implicitly defined
- mapper.resource('server', 'servers', controller=sc)
- # Pointing to an arbitrary WSGI app. You can specify the
- # {path_info:.*} parameter so the target app can be handed just that
- # section of the URL.
- mapper.connect(None, '/v1.0/{path_info:.*}', controller=BlogApp())
- """
- self.map = mapper
- self._router = routes.middleware.RoutesMiddleware(self._dispatch, self.map)
- @webob.dec.wsgify(RequestClass=Request)
- def __call__(self, req):
- """Route the incoming request to a controller based on self.map.
- If no match, return a 404.
- """
- return self._router
复制代码
我们可以看到在__call__方法中,返回了变量self._router,而可以得到:
- self._router = routes.middleware.RoutesMiddleware(self._dispatch, self.map)
复制代码
这就是我要说的重点,在前面的博客中,我们分析了cinder API的路由表形成过程,这条语句实现的功能就是根据之前形成的API路由表,实现请求信息的映射和匹配,得到实现这个请求所要执行的具体action的方法。比如上面例子,在这里就可以匹配到具体方法create。
1.2 classResource----def __call__
我们上面说过这几个中间件的__call__方法,都通过装饰器@webob.dec.wsgify(RequestClass=Request)进行了封装,我们以classResource----def__call__为例,来看看这一过程是怎么实现的,来看方法/cinder/api/openstack/wsgi.py----class Resource(wsgi.Application)----def __call__(self, request)的源码:
- @webob.dec.wsgify(RequestClass=Request)
- def __call__(self, request):
- """
- WSGI method that controls (de)serialization and method dispatch.
- """
- LOG.info("%(method)s %(url)s" % {"method": request.method,
- "url": request.url})
- # Identify the action, its arguments, and the requested
- # content type
- action_args = self.get_action_args(request.environ)
- # 输出:action_args = {'action': u'create', 'project_id': u'ecf0109bda814fa1a548af63f9ada370'}
- action = action_args.pop('action', None)
- # 输出:action = create
-
- # 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
- #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)
复制代码
来看/webob/dec.py----class wsgify(object)----def __call__(self, req, *args, **kw)的源码实现:
- def __call__(self, req, *args, **kw):
- """
- Call this as a WSGI application or with a request
-
- req = {'HTTP_X_TENANT_NAME': u'admin', 'routes.route': , 'HTTP_X_ROLE': u'_member_,admin', 'HTTP_X_USER_NAME': u'admin', 'SCRIPT_NAME': '/v1', 'webob.adhoc_attrs': {'response': }, 'REQUEST_METHOD': 'POST', 'PATH_INFO': '/ecf0109bda814fa1a548af63f9ada370/volumes', ......}
- args = (,)
- kw = {}
- """
-
- func = self.func
- #输出示例:func = >
-
- if func is None:
- if args or kw:
- raise TypeError(
- "Unbound %s can only be called with the function it "
- "will wrap" % self.__class__.__name__)
- func = req
- return self.clone(func)
-
- if isinstance(req, dict):
- if len(args) != 1 or kw:
- raise TypeError(
- "Calling %r as a WSGI app with the wrong signature")
- environ = req
- start_response = args[0]
- #输出示例:start_response = args[0] =
-
- #self.RequestClass =
- #获取类Request的初始化对象req = Request(req),形成正式的req;
- req = self.RequestClass(environ)
- #获取响应信息格式;
- req.response = req.ResponseClass()
-
- try:
- args = self.args
- #args = self.args = ()
- #self.middleware_wraps = None
- if self.middleware_wraps:
- args = (self.middleware_wraps,) + args
-
- # 这里调用指定中间件的__call__方法;
- resp = self.call_func(req, *args, **self.kwargs)
-
- except HTTPException as exc:
- resp = exc
-
- if resp is None:
- ## FIXME: I'm not sure what this should be?
- resp = req.response
-
- if isinstance(resp, text_type):
- resp = bytes_(resp, req.charset)
-
- if isinstance(resp, bytes):
- body = resp
- resp = req.response
- resp.write(body)
- if resp is not req.response:
- resp = req.response.merge_cookies(resp)
-
- return resp(environ, start_response)
-
- else:
- if self.middleware_wraps:
- args = (self.middleware_wraps,) + args
- return self.func(req, *args, **kw)
复制代码
总体来讲,这个方法主要实现了对请求信息req和执行请求之后的响应信息进行了一些格式和内容上的处理操作。这里有一条比较重要的语句:
- req = self.RequestClass(environ)
复制代码
就这里的示例来讲,输出示例为self.RequestClass = ,所实现的功能就是通过现有的请求信息,对类Request进行实例初始化,形成后面所要应用到的常见格式的req。我们可以看看这里的输出示例:
- req = POST /v1/ecf0109bda814fa1a548af63f9ada370/volumes HTTP/1.0
- Accept: application/json
- Accept-Encoding: gzip, deflate, compress
- Content-Length: 294
- Content-Type: application/json
- Host: 172.21.5.164:8776
- User-Agent: python-cinderclient
- X-Auth-Project-Id: admin
- X-Auth-Token: MIIQKQYJKoZIhvc......
- X-Domain-Id: None
- X-Domain-Name: None
- X-Identity-Status: Confirmed
- X-Project-Domain-Id: None
- X-Project-Domain-Name: None
- X-Project-Id: ecf0109bda814fa1a548af63f9ada370
- X-Project-Name: admin
- X-Role: _member_,admin
- X-Roles: _member_,admin
- X-Service-Catalog: [{"endpoints_links": [], ......]
- X-Tenant: admin
- X-Tenant-Id: ecf0109bda814fa1a548af63f9ada370
- X-Tenant-Name: admin
- X-User: admin
- X-User-Domain-Id: None
- X-User-Domain-Name: None
- X-User-Id: d2ee2dd06c9e49098a3d2a278c650b6c
- X-User-Name: admin
- {"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": {}}}
复制代码
再来看语句req.response = req.ResponseClass(),这条语句在这里实现的就是形成响应信息的初步格式,来看看输出示例:
- req.response = 200 OK
- Content-Type: text/html; charset=UTF-8
- Content-Length: 0
再来看语句resp = self.call_func(req, *args, **self.kwargs),这条语句实现的就是调用上面提到的方法/cinder/api/openstack/wsgi.py----class Resource(wsgi.Application)----def__call__(self, request)。来看方法call_func的实现:
- def call_func(self, req, *args, **kwargs):
- """Call the wrapped function; override this in a subclass to
- change how the function is called."""
- return self.func(req, *args, **kwargs)
复制代码
这里的self.func的值在类class wsgify(object)的初始化方法中已经进行了赋值,此处self.func
= >。
待到方法class Resource(wsgi.Application)----def __call__(self, request)执行过后,会回到方法/webob/dec.py----class
wsgify(object)----def __call__(self, req, *args, **kw)中继续后面代码的执行,其具体功能也是对获取到的响应信息进行若干格式化上的转换和处理。
现在,应用若干中间件对客户端发送过来的请求信息进行过滤(封装)处理部分简单的解析完成,下一篇博客中会具体解析方法class
Resource(wsgi.Application)----def __call__(self, request),具体深入解析cinder建立volume的过程。
相关文章:
Openstack Cinder中建立volume过程的源码解析(2)
http://www.aboutyun.com/thread-10216-1-1.html
1.如何获取要执行的action方法及其相关的扩展方法?
2.Resource类中的__call__(self,request)方法如何实现?
3.meth的作用?
4.如何从request.environ中获取要执行的action方法?
5.如何对body进行反序列化操作?
Openstack Cinder中建立volume过程的源码解析(3)
http://www.aboutyun.com/thread-10215-1-1.html
1.get_serializer的作用?
2.isgeneratorfunction是用来做什么的?
3.什么是特殊的generator方法?
4.进行响应信息的序列化操作的步骤?
5.简述在cinder模块中实现客户端发送过来的请求信息操作的主要的步骤?
Openstack Cinder中建立volume过程的源码解析(4)----以及taskflow相关解析
http://www.aboutyun.com/thread-10214-1-1.html
1.简述cinder是如何实现卷的建立的?
2.简述taskflow库来实现卷的简历过程?
3.如何根据给定id来检索获取单个的卷的类型?
4.如何构建并返回用于建立卷的flow?
5.如何声明flow是否是真的?
6.简述建立卷的flow的步骤?
Openstack Cinder中建立volume过程的源码解析(5)----以及taskflow相关解析
http://www.aboutyun.com/thread-10213-1-1.html
1.如何实现Flow类的初始化的?
2.用于卷的建立的flow中都添加了哪些task?
3.ExtractVolumeRequestTask类的作用是什么?
4.如何完全或部分的重置flow的内部的状态?
5.如何从给定的卷中提取卷的id信息?
6.OnFailureChangeStatusTask类的作用?
Openstack Cinder中建立volume过程的源码解析(6)----以及taskflow相关解析
http://www.aboutyun.com/thread-10212-1-1.html
1.如何来运行已经构建好的flow?
2.run的源码如何实现及实现过程是什么?
3.resume_it的实现过程是什么?
4.类Runner的初始化方法是什么?
5.run_it的源码如何实现?
Openstack Cinder中建立volume过程的源码解析(7)----以及taskflow相关解析
http://www.aboutyun.com/thread-10211-1-1.html
1.关于flow中task执行的重要语句的实现基本解析完成,如何实现?
2.如果卷的建立出现异常,则如何执行相关的逆转回滚操作?
Openstack Cinder中建立volume过程的源码解析(8)
http://www.aboutyun.com/thread-10219-1-1.html
1.VolumeCastTask的源码如何实现?
2.远程调用建立新卷的操作,有哪几个步骤?
3.task类VolumeCastTask具体是如何来实现根据请求信息进行卷的建立的?
Openstack Cinder中建立volume过程的源码解析(9)
http://www.aboutyun.com/thread-10210-1-1.html
1.如何实现create_volume的源码?
2.Cast如何实现远程调create_volume?
3.如何实现调用方法self.volume_rpcapi.create_volume来实现在目标主机上新卷的建立?
如果转载,请保留作者信息。
博客地址:http://blog.csdn.net/gaoxingnengjisuan
邮箱地址:dong.liu@siat.ac.cn
|