感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如果转载,请保留作者信息。
博客地址:http://blog.csdn.net/gaoxingnengjisuan
邮箱地址:[email protected]
(4)self._setup_extensions(ext_mgr)
在前面的博客中,我们说过在/cinder/api/contrib/目录下的文件都是用来实现功能扩展的,这里主要分为两部分:1.在一个控制器中单纯地实现新的Restful资源功能;2.即可实现新的Restful资源功能又可对已有的Restful资源功能实现扩展。(这里总觉得有点理解的不正确,后续会修正或者改正这部分的内容。)
在这里我们将主要分析/cinder/api/contrib/目录下如何即实现新的Restful资源功能又对已有的Restful资源功能实现扩展。
现在我们回到类/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_extensions(ext_mgr),具体来看方法_setup_extensions的源码实现:
def _setup_extensions(self, ext_mgr): for extension in ext_mgr.get_controller_extensions(): collection = extension.collection controller = extension.controller if collection not in self.resources: LOG.warning(_('Extension %(ext_name)s: Cannot extend ' 'resource %(collection)s: No such resource'), {'ext_name': extension.extension.name, 'collection': collection}) continue LOG.debug(_('Extension %(ext_name)s extending resource: ' '%(collection)s'), {'ext_name': extension.extension.name, 'collection': collection}) resource = self.resources[collection] resource.register_actions(controller) resource.register_extensions(controller)来看方法中的语句:for extension in ext_mgr.get_controller_extensions(),进一步来看方法 get_controller_extensions 的源码实现:
def get_controller_extensions(self): """ Returns a list of ControllerExtension objects. 获取ControllerExtension对象的列表; """ controller_exts = [] for ext in self.extensions.values(): try: get_ext_method = ext.get_controller_extensions except AttributeError: continue controller_exts.extend(get_ext_method()) return controller_exts来看语句:
for ext in self.extensions.values():
get_ext_method = ext.get_controller_extensions
示例输出:
self.extensions = {
'OS-SCH-HNT': <cinder.api.contrib.scheduler_hints.Scheduler_hints object at 0x1f39a50>,
'os-hosts': <cinder.api.contrib.hosts.Hosts object at 0x1e04b90>,
'os-vol-tenant-attr': <cinder.api.contrib.volume_tenant_attribute.Volume_tenant_attribute object at 0x2906ad0>,
'os-quota-sets': <cinder.api.contrib.quotas.Quotas object at 0x1f28290>,
'os-types-manage': <cinder.api.contrib.types_manage.Types_manage object at 0x2816c50>,
'os-volume-encryption-metadata': <cinder.api.contrib.volume_encryption_metadata.Volume_encryption_metadata object at 0x1e04790>,
'os-snapshot-actions': <cinder.api.contrib.snapshot_actions.Snapshot_actions object at 0x1e04550>,
'backups': <cinder.api.contrib.backups.Backups object at 0x280d850>,
'os-volume-actions': <cinder.api.contrib.volume_actions.Volume_actions object at 0x280d950>,
'os-vol-host-attr': <cinder.api.contrib.volume_host_attribute.Volume_host_attribute object at 0x1f28090>,
'encryption': <cinder.api.contrib.volume_type_encryption.Volume_type_encryption object at 0x1e04210>,
'os-availability-zone': <cinder.api.contrib.availability_zones.Availability_zones object at 0x280d2d0>,
'os-types-extra-specs': <cinder.api.contrib.types_extra_specs.Types_extra_specs object at 0x1f39210>,
'os-vol-mig-status-attr': <cinder.api.contrib.volume_mig_status_attribute.Volume_mig_status_attribute object at 0x1f39750>,
'os-image-create': <cinder.api.contrib.image_create.Image_create object at 0x1e04610>,
'os-extended-snapshot-attributes': <cinder.api.contrib.extended_snapshot_attributes.Extended_snapshot_attributes object at 0x28210d0>,
'qos-specs': <cinder.api.contrib.qos_specs_manage.Qos_specs_manage object at 0x29064d0>,
'os-quota-class-sets': <cinder.api.contrib.quota_classes.Quota_classes object at 0x1e04a90>,
'os-volume-transfer': <cinder.api.contrib.volume_transfer.Volume_transfer object at 0x2821d10>,
'os-vol-image-meta': <cinder.api.contrib.volume_image_metadata.Volume_image_metadata object at 0x2906ed0>,
'os-admin-actions': <cinder.api.contrib.admin_actions.Admin_actions object at 0x2821950>,
'os-services': <cinder.api.contrib.services.Services object at 0x1f39cd0>}
根据上一篇博客中的相关内容,我们可以验证,这里分为两部分:
第一部分的类中直接实现了方法get_controller_extensions:
ext = <cinder.api.contrib.scheduler_hints.Scheduler_hints object at 0x1f39a50>
ext = <cinder.api.contrib.volume_tenant_attribute.Volume_tenant_attribute object at 0x2906ad0>
ext = <cinder.api.contrib.types_manage.Types_manage object at 0x2816c50>
ext = <cinder.api.contrib.snapshot_actions.Snapshot_actions object at 0x1e04550>
ext = <cinder.api.contrib.volume_actions.Volume_actions object at 0x280d950>
ext = <cinder.api.contrib.volume_host_attribute.Volume_host_attribute object at 0x1f28090>
ext = <cinder.api.contrib.volume_type_encryption.Volume_type_encryption object at 0x1e04210>
ext = <cinder.api.contrib.volume_mig_status_attribute.Volume_mig_status_attribute object at 0x1f39750>
ext = <cinder.api.contrib.extended_snapshot_attributes.Extended_snapshot_attributes object at 0x28210d0>
ext = <cinder.api.contrib.volume_image_metadata.Volume_image_metadata object at 0x2906ed0>
ext = <cinder.api.contrib.admin_actions.Admin_actions object at 0x2821950>
第二部分的类中没有继承父类的get_controller_extensions方法,这里调用的是其父类中的get_controller_extensions方法:
ext = <cinder.api.contrib.hosts.Hosts object at 0x1e04b90>
ext = <cinder.api.contrib.quotas.Quotas object at 0x1f28290>
ext = <cinder.api.contrib.volume_encryption_metadata.Volume_encryption_metadata object at 0x1e04790>
ext = <cinder.api.contrib.backups.Backups object at 0x280d850>
ext = <cinder.api.contrib.availability_zones.Availability_zones object at 0x280d2d0>
ext = <cinder.api.contrib.types_extra_specs.Types_extra_specs object at 0x1f39210>
ext = <cinder.api.contrib.image_create.Image_create object at 0x1e04610>
ext = <cinder.api.contrib.qos_specs_manage.Qos_specs_manage object at 0x29064d0>
ext = <cinder.api.contrib.quota_classes.Quota_classes object at 0x1e04a90>
ext = <cinder.api.contrib.volume_transfer.Volume_transfer object at 0x2821d10>
ext = <cinder.api.contrib.services.Services object at 0x1f39cd0>
来看语句controller_exts.extend(get_ext_method())的实现(看几个例子,看一下实现过程);
第一部分的类中直接实现了方法get_controller_extensions:
=============================================================
ext = <cinder.api.contrib.scheduler_hints.Scheduler_hints object at 0x1f39a50>
class Scheduler_hints(extensions.ExtensionDescriptor):
"""Pass arbitrary key/value pairs to the scheduler."""
name = "SchedulerHints"
alias = "OS-SCH-HNT"
namespace = volumes.SCHEDULER_HINTS_NAMESPACE
updated = "2013-04-18T00:00:00+00:00"
def get_controller_extensions(self):
controller = SchedulerHintsController()
ext = extensions.ControllerExtension(self, 'volumes', controller)
return [ext]
class SchedulerHintsController(wsgi.Controller):
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.volume_tenant_attribute.Volume_tenant_attribute object at 0x2906ad0>
class Volume_tenant_attribute(extensions.ExtensionDescriptor):
"""Expose the internal project_id as an attribute of a volume."""
name = "VolumeTenantAttribute"
alias = "os-vol-tenant-attr"
namespace = ("http://docs.openstack.org/volume/ext/"
"volume_tenant_attribute/api/v1")
updated = "2011-11-03T00:00:00+00:00"
def get_controller_extensions(self):
controller = VolumeTenantAttributeController()
extension = extensions.ControllerExtension(self, 'volumes', controller)
return [extension]
class VolumeTenantAttributeController(wsgi.Controller):
def __init__(self, *args, **kwargs):
super(VolumeTenantAttributeController, self).__init__(*args, **kwargs)
self.volume_api = volume.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
=============================================================
ext = <cinder.api.contrib.types_manage.Types_manage object at 0x2816c50>
class Types_manage(extensions.ExtensionDescriptor):
"""Types manage support."""
name = "TypesManage"
alias = "os-types-manage"
namespace = "http://docs.openstack.org/volume/ext/types-manage/api/v1"
updated = "2011-08-24T00:00:00+00:00"
def get_controller_extensions(self):
controller = VolumeTypesManageController()
extension = extensions.ControllerExtension(self, 'types', controller)
return [extension]
class VolumeTypesManageController(wsgi.Controller):
"""
The volume types API controller for the OpenStack API.
"""
_view_builder_class = views_types.ViewBuilder
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.snapshot_actions.Snapshot_actions object at 0x1e04550>
class Snapshot_actions(extensions.ExtensionDescriptor):
"""Enable snapshot manager actions."""
name = "SnapshotActions"
alias = "os-snapshot-actions"
namespace = \
"http://docs.openstack.org/volume/ext/snapshot-actions/api/v1.1"
updated = "2013-07-16T00:00:00+00:00"
def get_controller_extensions(self):
controller = SnapshotActionsController()
extension = extensions.ControllerExtension(self,
'snapshots',
controller)
return [extension]
class SnapshotActionsController(wsgi.Controller):
def __init__(self, *args, **kwargs):
super(SnapshotActionsController, self).__init__(*args, **kwargs)
LOG.debug("SnapshotActionsController initialized")
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_controller_extensions方法,这里调用的是其父类中的get_controller_extensions方法:
class ExtensionDescriptor(object):
def get_controller_extensions(self):
"""
List of extensions.ControllerExtension extension objects.
Controller extensions are used to extend existing controllers.
"""
controller_exts = []
return controller_exts
可见没有做什么具体的工作;
我们再来看看在上述第一部分的类所对应的控制器类中,是如何实现即定义新的Restful资源功能又对已有的Restful资源功能实现扩展的。
def action(name): """ Mark a function as an action. The given name will be taken as the action key in the body. This is also overloaded to allow extensions to provide non-extending definitions of create and delete operations. """ def decorator(func): func.wsgi_action = name return func return decorator
def extends(*args, **kwargs): """ Indicate a function extends an operation. Can be used as either:: @extends def index(...): pass or as:: @extends(action='resize') def _action_resize(...): pass """ def decorator(func): # Store enough information to find what we're extending func.wsgi_extends = (func.__name__, kwargs.get('action')) return func # If we have positional arguments, call the decorator if args: return decorator(*args) # OK, return the decorator instead return decorator
我们关注这里两条语句,即:
func.wsgi_action = name
func.wsgi_extends = (func.__name__, kwargs.get('action'))
我们再回来看上面的输出示例,如:
=============================================================
ext = <cinder.api.contrib.types_manage.Types_manage object at 0x2816c50>
class Types_manage(extensions.ExtensionDescriptor):
"""Types manage support."""
name = "TypesManage"
alias = "os-types-manage"
namespace = "http://docs.openstack.org/volume/ext/types-manage/api/v1"
updated = "2011-08-24T00:00:00+00:00"
def get_controller_extensions(self):
controller = VolumeTypesManageController()
extension = extensions.ControllerExtension(self, 'types', controller)
return [extension]
class VolumeTypesManageController(wsgi.Controller):
"""
The volume types API controller for the OpenStack API.
"""
_view_builder_class = views_types.ViewBuilder
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
=============================================================
我们可以看到这些控制器类的父类class Controller(object)中,都会获取类ControllerMetaclass的实例化对象,我们进一步来看看方法class ControllerMetaclass(type)----def __new__的源码实现(对于__init__和__new__方法的区别,可以查询相关资料):
class ControllerMetaclass(type): """ Controller metaclass. This metaclass automates the task of assembling a dictionary mapping action keys to method names. """ def __new__(self, mcs, name, bases, cls_dict): """Adds the wsgi_actions dictionary to the class.""" # Find all actions actions = {} extensions = [] # start with wsgi actions from base classes for base in bases: actions.update(getattr(base, 'wsgi_actions', {})) for key, value in cls_dict.items(): if not callable(value): continue if getattr(value, 'wsgi_action', None): actions[value.wsgi_action] = key elif getattr(value, 'wsgi_extends', None): extensions.append(value.wsgi_extends) # Add the actions and extensions to the class dict cls_dict['wsgi_actions'] = actions cls_dict['wsgi_extensions'] = extensions return super(ControllerMetaclass, mcs).__new__(mcs, name, bases, cls_dict)在这个方法中,将 wsgi_action 和 wsgi_extends 收集起来,保存在相应的字典中,来看输出示例:
cls_dict = {'__module__': 'cinder.api.contrib.admin_actions', '_update': <function _update at 0x35ea5f0>, '_delete': <function _delete at 0x35ea848>, 'wsgi_actions': {'os-reset_status': '_reset_status', 'os-force_delete': '_force_delete'}, 'collection': 'snapshots', '_get': <function _get at 0x35ea7d0>,'wsgi_extensions': [], '__doc__': 'AdminController for Snapshots.'}
cls_dict = {'__module__': 'cinder.api.contrib.admin_actions', '_update': <function _update at 0x35ea320>, 'validate_update': <function validate_update at 0x35ea578>, '_delete': <function _delete at 0x35ea500>, '_migrate_volume': <function _migrate_volume at 0x35ea6e0>, '_force_detach': <function _force_detach at 0x35ea668>,'wsgi_actions': {'os-migrate_volume_completion': '_migrate_volume_completion', 'os-reset_status': '_reset_status', 'os-force_delete': '_force_delete', 'os-migrate_volume': '_migrate_volume', 'os-force_detach': '_force_detach'}, 'collection': 'volumes', '_get': <function _get at 0x35ea488>, 'valid_status': set(['available', 'creating', 'in-use', 'error_deleting', 'attaching', 'detaching', 'error', 'deleting']),'wsgi_extensions': [], '__doc__': 'AdminController for Volumes.', '_migrate_volume_completion': <function _migrate_volume_completion at 0x35ea758>}
cls_dict = {'__module__': 'cinder.api.contrib.extended_snapshot_attributes', 'show': <function show at 0x3526320>, '_get_snapshots': <function _get_snapshots at 0x3526230>, 'detail': <function detail at 0x3526398>, '_extend_snapshot': <function _extend_snapshot at 0x35262a8>,'wsgi_extensions': [('show', None), ('detail', None)], '__init__': <function __init__ at 0x35261b8>,'wsgi_actions': {}}
我们关注其中的wsgi_actions和wsgi_extensions,在后续应用中应该会对其进行解析,这样http请求就可以把名称和具体实现方法一一对应起来。
(5)super(APIRouter, self).__init__(mapper)
class Router(object): """WSGI middleware that maps incoming requests to WSGI apps.""" def __init__(self, mapper): self.map = mapper self._router = routes.middleware.RoutesMiddleware(self._dispatch, self.map)至此,OpenStack Cinder服务启动过程中的资源加载和扩展的源码简单解析完成了。
这几篇博客是我边看源码边写的,肯定会有不严谨和理解偏差的地方,欢迎大家批评指正。