感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如果转载,请保留作者信息。
博客地址: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>>]