感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如果转载,请保留作者信息。
博客地址:http://blog.csdn.net/gaoxingnengjisuan
邮箱地址:[email protected]
(2) self._setup_routes(mapper, ext_mgr)
现在我们回到类/cinder/api/openstack/__init__.py----class APIRouter中的初始化方法:
class APIRouter(base_wsgi.Router): def __init__(self, ext_mgr=None): if ext_mgr is None: if self.ExtensionManager: ext_mgr = self.ExtensionManager() (1) else: raise Exception(_("Must specify an ExtensionManager class")) mapper = ProjectMapper() self.resources = {} self._setup_routes(mapper, ext_mgr) (2) self._setup_ext_routes(mapper, ext_mgr) (3) self._setup_extensions(ext_mgr) (4) super(APIRouter, self).__init__(mapper) (5)
来看第二条比较重要的语句:self._setup_routes(mapper, ext_mgr),具体来看方法_setup_routes的实现:
def _setup_routes(self, mapper, ext_mgr): """ mapper = Route name Methods Path mapper.connect = <bound method ProjectMapper.connect of <cinder.api.openstack.ProjectMapper object at 0x2e5bc10>> # 示例: # map.resource("message", "messages", collection={"rss": "GET"}) # "GET /message/rss" => ``Messages.rss()``. # map.resource('message', 'messages', member={'mark':'POST'}) # "POST /message/1/mark" => ``Messages.mark(1)`` """ self.resources['versions'] = versions.create_resource() mapper.connect("versions", "/", controller=self.resources['versions'], action='show') """ mapper = Route name Methods Path versions / """ mapper.redirect("", "/") self.resources['volumes'] = volumes.create_resource(ext_mgr) mapper.resource("volume", "volumes", controller=self.resources['volumes'], collection={'detail': 'GET'}, member={'action': 'POST'}) """ mapper = Route name Methods Path ...... ...... POST /{project_id}/volumes.:(format) POST /{project_id}/volumes formatted_detail_volumes GET /{project_id}/volumes/detail.:(format) detail_volumes GET /{project_id}/volumes/detail formatted_volumes GET /{project_id}/volumes.:(format) volumes GET /{project_id}/volumes formatted_new_volume GET /{project_id}/volumes/new.:(format) new_volume GET /{project_id}/volumes/new PUT /{project_id}/volumes/:(id).:(format) PUT /{project_id}/volumes/:(id) formatted_action_volume POST /{project_id}/volumes/:(id)/action.:(format) action_volume POST /{project_id}/volumes/:(id)/action DELETE /{project_id}/volumes/:(id).:(format) DELETE /{project_id}/volumes/:(id) formatted_edit_volume GET /{project_id}/volumes/:(id)/edit.:(format) edit_volume GET /{project_id}/volumes/:(id)/edit formatted_volume GET /{project_id}/volumes/:(id).:(format) volume GET /{project_id}/volumes/:(id) """ self.resources['types'] = types.create_resource() mapper.resource("type", "types", controller=self.resources['types']) """ mapper = Route name Methods Path ...... ...... POST /{project_id}/types.:(format) POST /{project_id}/types formatted_types GET /{project_id}/types.:(format) types GET /{project_id}/types formatted_new_type GET /{project_id}/types/new.:(format) new_type GET /{project_id}/types/new PUT /{project_id}/types/:(id).:(format) PUT /{project_id}/types/:(id) DELETE /{project_id}/types/:(id).:(format) DELETE /{project_id}/types/:(id) formatted_edit_type GET /{project_id}/types/:(id)/edit.:(format) edit_type GET /{project_id}/types/:(id)/edit formatted_type GET /{project_id}/types/:(id).:(format) type GET /{project_id}/types/:(id) """ self.resources['snapshots'] = snapshots.create_resource(ext_mgr) mapper.resource("snapshot", "snapshots", controller=self.resources['snapshots'], collection={'detail': 'GET'}, member={'action': 'POST'}) """ mapper = Route name Methods Path ...... ...... POST /{project_id}/snapshots.:(format) POST /{project_id}/snapshots formatted_detail_snapshots GET /{project_id}/snapshots/detail.:(format) detail_snapshots GET /{project_id}/snapshots/detail formatted_snapshots GET /{project_id}/snapshots.:(format) snapshots GET /{project_id}/snapshots formatted_new_snapshot GET /{project_id}/snapshots/new.:(format) new_snapshot GET /{project_id}/snapshots/new PUT /{project_id}/snapshots/:(id).:(format) PUT /{project_id}/snapshots/:(id) formatted_action_snapshot POST /{project_id}/snapshots/:(id)/action.:(format) action_snapshot POST /{project_id}/snapshots/:(id)/action DELETE /{project_id}/snapshots/:(id).:(format) DELETE /{project_id}/snapshots/:(id) formatted_edit_snapshot GET /{project_id}/snapshots/:(id)/edit.:(format) edit_snapshot GET /{project_id}/snapshots/:(id)/edit formatted_snapshot GET /{project_id}/snapshots/:(id).:(format) snapshot GET /{project_id}/snapshots/:(id) """ self.resources['snapshot_metadata'] = snapshot_metadata.create_resource() snapshot_metadata_controller = self.resources['snapshot_metadata'] mapper.resource("snapshot_metadata", "metadata", controller=snapshot_metadata_controller, parent_resource=dict(member_name='snapshot', collection_name='snapshots')) """ mapper = Route name Methods Path ...... ...... POST /{project_id}/snapshots/:snapshot_id/metadata.:(format) POST /{project_id}/snapshots/:snapshot_id/metadata formatted_snapshot_metadata GET /{project_id}/snapshots/:snapshot_id/metadata.:(format) snapshot_metadata GET /{project_id}/snapshots/:snapshot_id/metadata formatted_snapshot_new_snapshot_metadata GET /{project_id}/snapshots/:snapshot_id/metadata/new.:(format) snapshot_new_snapshot_metadata GET /{project_id}/snapshots/:snapshot_id/metadata/new PUT /{project_id}/snapshots/:snapshot_id/metadata/:(id).:(format) PUT /{project_id}/snapshots/:snapshot_id/metadata/:(id) DELETE /{project_id}/snapshots/:snapshot_id/metadata/:(id).:(format) DELETE /{project_id}/snapshots/:snapshot_id/metadata/:(id) formatted_snapshot_edit_snapshot_metadata GET /{project_id}/snapshots/:snapshot_id/metadata/:(id)/edit.:(format) snapshot_edit_snapshot_metadata GET /{project_id}/snapshots/:snapshot_id/metadata/:(id)/edit formatted_snapshot_snapshot_metadata GET /{project_id}/snapshots/:snapshot_id/metadata/:(id).:(format) snapshot_snapshot_metadata GET /{project_id}/snapshots/:snapshot_id/metadata/:(id) """ mapper.connect("metadata", "/{project_id}/snapshots/{snapshot_id}/metadata", controller=snapshot_metadata_controller, action='update_all', conditions={"method": ['PUT']}) """ mapper = Route name Methods Path ...... ...... metadata PUT /{project_id}/snapshots/{snapshot_id}/metadata """ self.resources['limits'] = limits.create_resource() mapper.resource("limit", "limits",controller=self.resources['limits']) """ mapper = Route name Methods Path ...... ...... POST /{project_id}/limits.:(format) POST /{project_id}/limits formatted_limits GET /{project_id}/limits.:(format) limits GET /{project_id}/limits formatted_new_limit GET /{project_id}/limits/new.:(format) new_limit GET /{project_id}/limits/new PUT /{project_id}/limits/:(id).:(format) PUT /{project_id}/limits/:(id) DELETE /{project_id}/limits/:(id).:(format) DELETE /{project_id}/limits/:(id) formatted_edit_limit GET /{project_id}/limits/:(id)/edit.:(format) edit_limit GET /{project_id}/limits/:(id)/edit formatted_limit GET /{project_id}/limits/:(id).:(format) limit GET /{project_id}/limits/:(id) """ self.resources['volume_metadata'] = volume_metadata.create_resource() volume_metadata_controller = self.resources['volume_metadata'] mapper.resource("volume_metadata", "metadata", controller=volume_metadata_controller, parent_resource=dict(member_name='volume', collection_name='volumes')) """ mapper = Route name Methods Path ...... ...... POST /{project_id}/volumes/:volume_id/metadata.:(format) POST /{project_id}/volumes/:volume_id/metadata formatted_volume_metadata GET /{project_id}/volumes/:volume_id/metadata.:(format) volume_metadata GET /{project_id}/volumes/:volume_id/metadata formatted_volume_new_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/new.:(format) volume_new_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/new PUT /{project_id}/volumes/:volume_id/metadata/:(id).:(format) PUT /{project_id}/volumes/:volume_id/metadata/:(id) DELETE /{project_id}/volumes/:volume_id/metadata/:(id).:(format) DELETE /{project_id}/volumes/:volume_id/metadata/:(id) formatted_volume_edit_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id)/edit.:(format) volume_edit_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id)/edit formatted_volume_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id).:(format) volume_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id) """ mapper.connect("metadata", "/{project_id}/volumes/{volume_id}/metadata", controller=volume_metadata_controller, action='update_all', conditions={"method": ['PUT']}) """ mapper = Route name Methods Path ...... ...... metadata PUT /{project_id}/volumes/{volume_id}/metadata """这里调用了方法mapper.connect和mapper.resource,实现进而调用python库routes,来实现从url提取相应的参数,如controller,action或者其它用户自己定义的变量,从而实现URL和相应action的映射。具体实现没有进行解析。也就是可以简单的理解为,通过得到的URL,通过这里实现的映射关系,进行格式匹配,具体定位到所要调用的方法上。具体的输出示例,如上述源码中已经给出,可以作为参考。
(3)self._setup_ext_routes(mapper, ext_mgr)
来看方法/cinder/api/openstack/__init__.py----class APIRouter----def __init__中第三条比较重要的语句:
self._setup_ext_routes(mapper, ext_mgr)
具体来看方法_setup_ext_routes的源码实现:
def _setup_ext_routes(self, mapper, ext_mgr): """ mapper = Route name Methods Path versions / ...... ...... ...... formatted_volume_edit_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id)/edit.:(format) volume_edit_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id)/edit formatted_volume_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id).:(format) volume_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id) metadata PUT /{project_id}/volumes/{volume_id}/metadata """ # ext_mgr.get_resources():获取ResourceExtension对象列表; for resource in ext_mgr.get_resources(): LOG.debug(_('Extended resource: %s'), resource.collection) wsgi_resource = wsgi.Resource(resource.controller) self.resources[resource.collection] = wsgi_resource kargs = dict( controller=wsgi_resource, collection=resource.collection_actions, member=resource.member_actions) if resource.parent: kargs['parent_resource'] = resource.parent mapper.resource(resource.collection, resource.collection, **kargs) if resource.custom_routes_fn: resource.custom_routes_fn(mapper, wsgi_resource)
注:需要说明的是在/cinder/api/contrib/目录下的文件都是用来实现功能扩展的,这里主要分为两部分:1.在一个控制器中单纯地实现新的Restful资源功能;2.在一个控制器中即可实现新的Restful资源功能又可对已有的Restful资源功能实现扩展。
方法_setup_ext_routes中针对的是上述第一类,也就是在一个控制器中单纯地实现新的Restful资源功能。这个方法主要实现了两部分的功能,即得到alias和self.extensions[alias]一一对应的关系,以及获取新定义Restful资源功能的URL和具体功能方法的映射关系。
首先来看方法中的语句:
for resource in ext_mgr.get_resources()wsgi_resource = wsgi.Resource(resource.controller)
来看输出示例:
resource.controller = <cinder.api.extensions.ExtensionsResource object at 0x3579bd0>def get_resources(self): """ Returns a list of ResourceExtension objects. 获取ResourceExtension对象列表; """ resources = [] resources.append(ResourceExtension('extensions',ExtensionsResource(self))) for ext in self.extensions.values(): try: resources.extend(ext.get_resources()) except AttributeError: pass return resources我们首先来看输出示例:
即实现了方法get_resources也实现了方法get_controller_extensions:
ext = <cinder.api.contrib.volume_type_encryption.Volume_type_encryption object at 0x18b01d0>
两个方法都没由直接实现:
ext = <cinder.api.contrib.image_create.Image_create object at 0x18b05d0>
经上分析,返回了11个类ResourceExtension的初始化对象;
来看方法get_resources中的语句:resources.extend(ext.get_resources()),这里的方法get_resources()调用了具体类中的get_resources()方法,而类中没有实现get_resources()方法的,在其父类class ExtensionDescriptor(object)中有实现,我们来看具体的类中get_resources的实现,我简单进行了一个归纳:
直接实现了方法get_resources的类(看几个例子,看一下实现过程):
==========================================================
ext = <cinder.api.contrib.backups.Backups object at 0x226f810>:
class Backups(extensions.ExtensionDescriptor):
"""Backups support."""
name = 'Backups'
alias = 'backups'
namespace = 'http://docs.openstack.org/volume/ext/backups/api/v1'
updated = '2012-12-12T00:00:00+00:00'
def get_resources(self):
resources = []
res = extensions.ResourceExtension(
Backups.alias,
BackupsController(),
collection_actions={'detail': 'GET'},
member_actions={'restore': 'POST'})
resources.append(res)
return resources
class BackupsController(wsgi.Controller):
"""
The Backups API controller for the OpenStack API.
OpenStack的备份操作API控制器;
"""
_view_builder_class = backup_views.ViewBuilder
def __init__(self):
self.backup_api = backupAPI.API()
super(BackupsController, self).__init__()
class Controller(object):
"""Default controller."""
__metaclass__ = ControllerMetaclass
_view_builder_class = None
def __init__(self, view_builder=None):
"""Initialize controller with a view builder instance."""
if view_builder:
self._view_builder = view_builder
elif self._view_builder_class:
self._view_builder = self._view_builder_class()
else:
self._view_builder = None
==========================================================
ext = <cinder.api.contrib.hosts.Hosts object at 0x18b0b50>:
class Hosts(extensions.ExtensionDescriptor):
"""Admin-only host administration"""
name = "Hosts"
alias = "os-hosts"
namespace = "http://docs.openstack.org/volume/ext/hosts/api/v1.1"
updated = "2011-06-29T00:00:00+00:00"
def get_resources(self):
resources = [extensions.ResourceExtension('os-hosts',
HostController(),
collection_actions={'update': 'PUT'},
member_actions={
'startup': 'GET',
'shutdown': 'GET',
'reboot': 'GET'})]
return resources
class HostController(object):
"""
The Hosts API controller for the OpenStack API.
主机控制API;
"""
def __init__(self):
self.api = volume_api.HostAPI()
super(HostController, self).__init__()
==========================================================
ext = <cinder.api.contrib.quotas.Quotas object at 0x1908250>:
class Quotas(extensions.ExtensionDescriptor):
"""Quotas management support"""
name = "Quotas"
alias = "os-quota-sets"
namespace = "http://docs.openstack.org/volume/ext/quotas-sets/api/v1.1"
updated = "2011-08-08T00:00:00+00:00"
def get_resources(self):
resources = []
res = extensions.ResourceExtension('os-quota-sets',
QuotaSetsController(),
member_actions={'defaults': 'GET'})
resources.append(res)
return resources
class QuotaSetsController(object)
==========================================================
ext = <cinder.api.contrib.volume_encryption_metadata.Volume_encryption_metadata object at 0x18b0750>:
class Volume_encryption_metadata(extensions.ExtensionDescriptor):
"""Volume encryption metadata retrieval support."""
name = "VolumeEncryptionMetadata"
alias = "os-volume-encryption-metadata"
namespace = ("http://docs.openstack.org/volume/ext/"
"os-volume-encryption-metadata/api/v1")
updated = "2013-07-10T00:00:00+00:00"
def get_resources(self):
resources = []
res = extensions.ResourceExtension('encryption',
VolumeEncryptionMetadataController(),
parent=dict(member_name='volume', collection_name='volumes'))
resources.append(res)
return resources
class VolumeEncryptionMetadataController(wsgi.Controller):
"""
The volume encryption metadata API extension
卷加密元数据控制API类;
"""
class Controller(object):
"""Default controller."""
__metaclass__ = ControllerMetaclass
_view_builder_class = None
def __init__(self, view_builder=None):
"""Initialize controller with a view builder instance."""
if view_builder:
self._view_builder = view_builder
elif self._view_builder_class:
self._view_builder = self._view_builder_class()
else:
self._view_builder = None
==========================================================
没有实现get_resources()方法的类,在其父类class ExtensionDescriptor(object)中有实现:
def get_resources(self): """ List of extensions.ResourceExtension extension objects. Resources define new nouns, and are accessible through URLs. """ resources = [] return resources
class Resource(wsgi.Application): def __init__(self, controller, action_peek=None, **deserializers): """ :param controller: object that implement methods created by routes lib :param action_peek: dictionary of routines for peeking into an action request body to determine the desired action """ self.controller = controller # 获取和更新默认的反序列化方法; default_deserializers = dict(xml=XMLDeserializer, json=JSONDeserializer) default_deserializers.update(deserializers) self.default_deserializers = default_deserializers # 获取默认的序列化方法; self.default_serializers = dict(xml=XMLDictSerializer, json=JSONDictSerializer) # 获取和确认所要引用的动作; # action_peek_xml:通过解析xml字符串,实现确定所要引用的动作; # action_peek_json:通过反序列化操作,实现确定所要引用的动作; self.action_peek = dict(xml=action_peek_xml, json=action_peek_json) self.action_peek.update(action_peek or {}) # Copy over the actions dictionary self.wsgi_actions = {} if controller: self.register_actions(controller) # Save a mapping of extensions self.wsgi_extensions = {} self.wsgi_action_extensions = {}
def _setup_ext_routes(self, mapper, ext_mgr): """ mapper = Route name Methods Path versions / ...... ...... ...... formatted_volume_edit_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id)/edit.:(format) volume_edit_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id)/edit formatted_volume_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id).:(format) volume_volume_metadata GET /{project_id}/volumes/:volume_id/metadata/:(id) metadata PUT /{project_id}/volumes/{volume_id}/metadata """ # ext_mgr.get_resources():获取ResourceExtension对象列表; for resource in ext_mgr.get_resources(): LOG.debug(_('Extended resource: %s'), resource.collection) wsgi_resource = wsgi.Resource(resource.controller) self.resources[resource.collection] = wsgi_resource kargs = dict( controller=wsgi_resource, collection=resource.collection_actions, member=resource.member_actions) if resource.parent: kargs['parent_resource'] = resource.parent mapper.resource(resource.collection, resource.collection, **kargs) if resource.custom_routes_fn: resource.custom_routes_fn(mapper, wsgi_resource)再来看语句: