分享

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

shihailong123 发表于 2014-11-22 13:29:19 回帖奖励 阅读模式 关闭右栏 1 26179
阅读导读:
1.cinder中卷的建立的过程中,客户端传递过来的request的执行过程是怎样的?
2.__call__方法都通过什么方法封装?
3.如何调用指定中间件的__call__方法




感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
从这篇博客开始,我将以cinder中卷的建立的过程的源码解析为例,来说明客户端传递过来的request的执行过程。
示例:
我们执行命令:
  1. cinder create --display_name shinian01 1
复制代码
便可获取响应信息:
  1. +---------------------+--------------------------------------+
  2. |       Property      |                Value                 |
  3. +---------------------+--------------------------------------+
  4. |     attachments     |                  []                  |
  5. |  availability_zone  |                 nova                 |
  6. |       bootable      |                false                 |
  7. |      created_at     |      2014-03-29T12:36:23.998481      |
  8. | display_description |                 None                 |
  9. |     display_name    |              shinian01               |
  10. |          id         | 3f0aa242-9dab-48db-b63d-92b6dd38cf20 |
  11. |       metadata      |                  {}                  |
  12. |         size        |                  1                   |
  13. |     snapshot_id     |                 None                 |
  14. |     source_volid    |                 None                 |
  15. |        status       |               creating               |
  16. |     volume_type     |                 None                 |
  17. +---------------------+--------------------------------------+
复制代码
实际上,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__方法,我们来看具体代码:

  1. class Router(object):
  2. """WSGI middleware that maps incoming requests to WSGI apps."""
  3. def __init__(self, mapper):
  4. """Create a router for the given routes.Mapper.
  5. Each route in `mapper` must specify a 'controller', which is a
  6. WSGI app to call. You'll probably want to specify an 'action' as
  7. well and have your controller be an object that can route
  8. the request to the action-specific method.
  9. Examples:
  10. mapper = routes.Mapper()
  11. sc = ServerController()
  12. # Explicit mapping of one route to a controller+action
  13. mapper.connect(None, '/svrlist', controller=sc, action='list')
  14. # Actions are all implicitly defined
  15. mapper.resource('server', 'servers', controller=sc)
  16. # Pointing to an arbitrary WSGI app. You can specify the
  17. # {path_info:.*} parameter so the target app can be handed just that
  18. # section of the URL.
  19. mapper.connect(None, '/v1.0/{path_info:.*}', controller=BlogApp())
  20. """
  21. self.map = mapper
  22. self._router = routes.middleware.RoutesMiddleware(self._dispatch, self.map)
  23. @webob.dec.wsgify(RequestClass=Request)
  24. def __call__(self, req):
  25. """Route the incoming request to a controller based on self.map.
  26. If no match, return a 404.
  27. """
  28.         return self._router
复制代码
我们可以看到在__call__方法中,返回了变量self._router,而可以得到:
  1. 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)的源码:
  1.    @webob.dec.wsgify(RequestClass=Request)
  2.     def __call__(self, request):
  3.         """
  4.         WSGI method that controls (de)serialization and method dispatch.
  5.         """
  6.         LOG.info("%(method)s %(url)s" % {"method": request.method,
  7.                                          "url": request.url})
  8.         # Identify the action, its arguments, and the requested
  9.         # content type
  10.         action_args = self.get_action_args(request.environ)
  11.         # 输出:action_args = {'action': u'create', 'project_id': u'ecf0109bda814fa1a548af63f9ada370'}
  12.         action = action_args.pop('action', None)
  13.         # 输出:action = create
  14.         
  15.         # get_body:通过检测header,确定request body的内容类型;获取request中的body部分;
  16.         content_type, body = self.get_body(request)
  17.         # 输出:content_type = application/json
  18.         # 输出:body = {"volume": {"status": "creating", "availability_zone": null, "source_volid": null,
  19.         #                         "display_description": null, "snapshot_id": null, "user_id": null, "size": 1,
  20.         #                         "display_name": "shinian01", "imageRef": null, "attach_status": "detached",
  21.         #                         "volume_type": null, "project_id": null, "metadata": {}}}
  22.         # best_match_content_type:确定请求响应的内容类型;
  23.         accept = request.best_match_content_type()
  24.         # 输出:accept = application/json
  25.         #request = POST /v1/ecf0109bda814fa1a548af63f9ada370/volumes HTTP/1.0
  26.                   ......      
  27.         #action = create
  28.         #action_args = {'project_id': u'ecf0109bda814fa1a548af63f9ada370'}
  29.         #content_type = application/json
  30.         #body = {"volume": {"status": "creating",
  31.         #                   "availability_zone": null,
  32.         #                   "source_volid": null,
  33.         #                   "display_description": null,
  34.         #                   "snapshot_id": null,
  35.         #                   "user_id": null,
  36.         #                   "size": 1,
  37.         #                   "display_name": "shinian01",
  38.         #                   "imageRef": null,
  39.         #                   "attach_status": "detached",
  40.         #                   "volume_type": null,
  41.         #                   "project_id": null,
  42.         #                   "metadata": {}}}
  43.         #accept = application/json
  44.         
  45.         return self._process_stack(request, action, action_args,
  46.                                    content_type, body, accept)
复制代码
来看/webob/dec.py----class wsgify(object)----def __call__(self, req, *args, **kw)的源码实现:
  1. def __call__(self, req, *args, **kw):
  2.         """
  3.         Call this as a WSGI application or with a request
  4.         
  5.         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', ......}
  6.         args = (,)
  7.         kw = {}
  8.         """
  9.         
  10.         func = self.func
  11.         #输出示例:func = >
  12.         
  13.         if func is None:
  14.             if args or kw:
  15.                 raise TypeError(
  16.                     "Unbound %s can only be called with the function it "
  17.                     "will wrap" % self.__class__.__name__)
  18.             func = req
  19.             return self.clone(func)
  20.         
  21.         if isinstance(req, dict):
  22.             if len(args) != 1 or kw:
  23.                 raise TypeError(
  24.                     "Calling %r as a WSGI app with the wrong signature")
  25.             environ = req
  26.             start_response = args[0]
  27.             #输出示例:start_response = args[0] =            
  28.             
  29.             #self.RequestClass =
  30.             #获取类Request的初始化对象req = Request(req),形成正式的req;
  31.             req = self.RequestClass(environ)
  32.             #获取响应信息格式;
  33.             req.response = req.ResponseClass()
  34.                        
  35.             try:
  36.                 args = self.args
  37.                 #args = self.args = ()
  38.                 #self.middleware_wraps = None
  39.                 if self.middleware_wraps:
  40.                     args = (self.middleware_wraps,) + args
  41.                     
  42.                 # 这里调用指定中间件的__call__方法;
  43.                 resp = self.call_func(req, *args, **self.kwargs)
  44.                
  45.             except HTTPException as exc:
  46.                 resp = exc
  47.                
  48.             if resp is None:
  49.                 ## FIXME: I'm not sure what this should be?
  50.                 resp = req.response
  51.                
  52.             if isinstance(resp, text_type):
  53.                 resp = bytes_(resp, req.charset)
  54.                
  55.             if isinstance(resp, bytes):
  56.                 body = resp
  57.                 resp = req.response
  58.                 resp.write(body)
  59.             if resp is not req.response:
  60.                 resp = req.response.merge_cookies(resp)
  61.             
  62.             return resp(environ, start_response)
  63.         
  64.         else:
  65.             if self.middleware_wraps:
  66.                 args = (self.middleware_wraps,) + args
  67.             return self.func(req, *args, **kw)
复制代码
总体来讲,这个方法主要实现了对请求信息req和执行请求之后的响应信息进行了一些格式和内容上的处理操作。这里有一条比较重要的语句:
  1. req = self.RequestClass(environ)
复制代码
就这里的示例来讲,输出示例为self.RequestClass = ,所实现的功能就是通过现有的请求信息,对类Request进行实例初始化,形成后面所要应用到的常见格式的req。我们可以看看这里的输出示例:
  1. req = POST /v1/ecf0109bda814fa1a548af63f9ada370/volumes HTTP/1.0
  2. Accept: application/json
  3. Accept-Encoding: gzip, deflate, compress
  4. Content-Length: 294
  5. Content-Type: application/json
  6. Host: 172.21.5.164:8776
  7. User-Agent: python-cinderclient
  8. X-Auth-Project-Id: admin
  9. X-Auth-Token: MIIQKQYJKoZIhvc......
  10. X-Domain-Id: None
  11. X-Domain-Name: None
  12. X-Identity-Status: Confirmed
  13. X-Project-Domain-Id: None
  14. X-Project-Domain-Name: None
  15. X-Project-Id: ecf0109bda814fa1a548af63f9ada370
  16. X-Project-Name: admin
  17. X-Role: _member_,admin
  18. X-Roles: _member_,admin
  19. X-Service-Catalog: [{"endpoints_links": [], ......]
  20. X-Tenant: admin
  21. X-Tenant-Id: ecf0109bda814fa1a548af63f9ada370
  22. X-Tenant-Name: admin
  23. X-User: admin
  24. X-User-Domain-Id: None
  25. X-User-Domain-Name: None
  26. X-User-Id: d2ee2dd06c9e49098a3d2a278c650b6c
  27. X-User-Name: admin
  28. {"volume": {"status": "creating",
  29.             "availability_zone": null,
  30.             "source_volid": null,
  31.             "display_description": null,
  32.             "snapshot_id": null,
  33.             "user_id": null,
  34.             "size": 1,
  35.             "display_name": "shinian01",
  36.             "imageRef": null,
  37.             "attach_status": "detached",
  38.             "volume_type": null,
  39.             "project_id": null,
  40.             "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的实现:
  1. def call_func(self, req, *args, **kwargs):
  2.         """Call the wrapped function; override this in a subclass to
  3.         change how the function is called."""
  4.         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







已有(0)人评论

跳转到指定楼层
溜溜小哥 发表于 2015-5-22 17:13:11
你好,你这里引用了我那么多博客,都不声明一下,你不觉得很多分么?至少应该尊重一下我的劳动成果吧?
http://blog.csdn.net/gaoxingnengjisuan

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条