OpenStack Cinder服务启动过程中的资源加载和扩展源码解析之三

感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如果转载,请保留作者信息。
博客地址: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资源功能实现扩展的。
我们可以看到在这些控制器类中的方法中,有两类装饰器,即@action和@extension,其中定义新的Restful资源功能就是应用装饰器@action实现的,而扩展已有的Restful资源功能就是应用装饰器@extension实现的。我们来看它们是如何实现的。首先来看这两个装饰器:

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_actionswsgi_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服务启动过程中的资源加载和扩展的源码简单解析完成了。

这几篇博客是我边看源码边写的,肯定会有不严谨和理解偏差的地方,欢迎大家批评指正。

你可能感兴趣的:(源代码,openstack,cinder)