分享

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

shihailong123 发表于 2014-11-22 15:18:37 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 16527
问题导读
1.资源的加载是如何实现的?
2.cinder模块功能的扩展和加载过程的具体实现是怎样的?





在OpenStack Cinder服务启动的过程中,对/cinder/api/contrib/和/cinder/api/v1/和/cinder/api/v2/下定义的资源功能进行了加载。
在前面的博客cinder服务启动源码分析中,我们知道在服务类cinder.service.WSGIService的初始化过程中,有这样的源码实现:
  1. class WSGIService(object):  
  2.         """
  3.         Provides ability to launch API from a 'paste' configuration.
  4.         实现启动API的类;
  5.         """  
  6.       
  7.         def __init__(self, name, loader=None):  
  8.             """
  9.             Initialize, but do not start the WSGI server.
  10.             类的初始化方法,但是不启动服务;
  11.      
  12.             :param name: The name of the WSGI server given to the loader.
  13.             给定的要加载的服务的名称;
  14.             :param loader: Loads the WSGI application using the given name.
  15.             :returns: None
  16.      
  17.             """  
  18.             # 给定的要加载的服务的名称;  
  19.             # osapi_volume  
  20.             self.name = name  
  21.             # 根据给定的服务名称导入对应的管理类;  
  22.             self.manager = self._get_manager()  
  23.             self.loader = loader or wsgi.Loader()  
  24.             self.app = self.loader.load_app(name)  
  25.             # 获取主机host;  
  26.             self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")  
  27.             # 获取主机端口;  
  28.             self.port = getattr(CONF, '%s_listen_port' % name, 0)  
  29.             # Server:管理WSGI服务的类;  
  30.             self.server = wsgi.Server(name,  
  31.                                       self.app,  
  32.                                       host=self.host,  
  33.                                       port=self.port)  
复制代码

对于语句:
self.loader = loader or wsgi.Loader()
self.app = self.loader.load_app(name)
之前我们没有进行深入的分析,实际上,这两条语句就实现了模块功能的扩展和加载过程。我们来看源码:

  1. class Loader(object):
  2.     """
  3.     Used to load WSGI applications from paste configurations.   
  4.     """
  5.     def __init__(self, config_path=None):
  6.         """Initialize the loader, and attempt to find the config.
  7.         :param config_path: Full or relative path to the paste config.
  8.         :returns: None
  9.         """
  10.         # 此处指向/etc/cinder/api-paste.ini
  11.         config_path = config_path or CONF.api_paste_config
  12.         # 获取config_path的全路径;
  13.         self.config_path = utils.find_config(config_path)def load_app(self, name):
  14.         """
  15.         Return the paste URLMap wrapped WSGI application.
  16.         :param name: Name of the application to load.
  17.         :returns: Paste URLMap object wrapping the requested application.
  18.         :raises: `cinder.exception.PasteAppNotFound`
  19.         加载osapi_volume程序;
  20.         """
  21.         try:
  22.             return deploy.loadapp("config:%s" % self.config_path, name=name)
  23.         except LookupError as err:
  24.             LOG.error(err)
  25.             raise exception.PasteAppNotFound(name=name, path=self.config_path)
复制代码

我们可以看到config_path指向/etc/cinder/api-paste.ini,之前我们在博客Paste Deployment简介以及cinder-api-paste.ini的解析(1)Paste
Deployment简介以及cinder-api-paste.ini的解析(2)
中,已经进行过解析,cinder通过api-paste.ini中的配置信息来进行指定WSGI Application的实现。这里可以看到,在方法load_app中,调用了模块库deploy来实现对api-paste.ini中配置信息的解析和指定WSGI
Application的实现。

而通过上述两篇博客我们也可以知道,通过对api-paste.ini中配置信息的解析,最终就调用了WSGI Applicationapiv1的实现,具体就是cinder.api.v1.router:APIRouter.factory,这个WSGI
Application的具体功能就是实现模块功能的扩展和加载过程,所以这里我们就深入分析cinder.api.v1.router:APIRouter.factory的实现过程。
这里调用的方法是/cinder/api/openstack/__init__.py----class APIRouter(base_wsgi.Router)----def factory:

  1. <font face="Consolas, Courier New, Courier, mono, serif" color="#000000"><span style="font-size: 12px; line-height: 18px;">def factory(cls, global_config, **local_config): </span></font>
  2.     return cls()
复制代码


输出示例:
cls =
global_config = {'__file__': '/etc/cinder/api-paste.ini', 'here': '/etc/cinder'}

所以我们来看类/cinder/api/v1/router.py----class APIRouter的具体实现:

  1. class APIRouter(cinder.api.openstack.APIRouter):
  2.     """
  3.     Routes requests on the OpenStack API to the appropriate controller
  4.     and method.
  5.     """
  6.     ExtensionManager = extensions.ExtensionManager
  7.     def _setup_routes(self, mapper, ext_mgr):
  8.         ......
复制代码

进而来看其父类/cinder/api/openstack/__init__.py----class APIRouter的初始化方法:

  1. class APIRouter(base_wsgi.Router):
  2.     def __init__(self, ext_mgr=None):
  3.         if ext_mgr is None:
  4.             if self.ExtensionManager:
  5.                 ext_mgr = self.ExtensionManager() (1)
  6.             else:
  7.                 raise Exception(_("Must specify an ExtensionManager class"))
  8.         
  9.         mapper = ProjectMapper()        
  10.         self.resources = {}
  11.         self._setup_routes(mapper, ext_mgr) (2)
  12.         self._setup_ext_routes(mapper, ext_mgr) (3)
  13.         self._setup_extensions(ext_mgr) (4)
  14.         super(APIRouter, self).__init__(mapper) (5)
复制代码


在这个类的初始化方法中,就实现了cinder模块功能的扩展和加载过程,这里我们就对其中比较重要的5条语句进行详细的解析,来看看cinder模块功能的扩展和加载过程的具体实现过程。
(1).ext_mgr = self.ExtensionManager()
这里实现的具体语句是类/cinder/api/v1/router.py----class APIRouter中的语句:
ExtensionManager = extensions.ExtensionManager
具体来看类ExtensionManager的初始化方法:

  1. class ExtensionManager(object):
  2.     """
  3.     Load extensions from the configured extension path.
  4.     See cinder/tests/api/extensions/foxinsocks/extension.py for an
  5.     example extension implementation.
  6.     """
  7.     def __init__(self):
  8.         LOG.audit(_('Initializing extension manager.'))
  9.         self.extensions = {}
  10.         self._load_extensions()
复制代码

继续来看方法_load_extensions的实现:

  1. def _load_extensions(self):
  2.         """
  3.         Load extensions specified on the command line.
  4.         """
  5.         # extensions=['cinder.api.contrib.standard_extensions']
  6.         extensions = list(self.cls_list)
  7.         old_contrib_path = ('cinder.api.openstack.volume.contrib.'
  8.                             'standard_extensions')
  9.         new_contrib_path = 'cinder.api.contrib.standard_extensions'
  10.         if old_contrib_path in extensions:
  11.             extensions = [e.replace(old_contrib_path, new_contrib_path)
  12.                           for e in extensions]
  13.         for ext_factory in extensions:
  14.             try:
  15.                 self.load_extension(ext_factory)
  16.             except Exception as exc:
  17.                 LOG.warn(......)
复制代码

这里最重要的语句就是:
for ext_factory in extensions:
    try:
        self.load_extension(ext_factory)
    except Exception as exc:
        LOG.warn(......)
输出示例:
extensions=['cinder.api.contrib.standard_extensions']
继续来看方法load_extension的实现源码:

  1. def load_extension(self, ext_factory):
  2.     """
  3.     ext_factory=['cinder.api.contrib.standard_extensions']
  4.     """
  5.     # ext_factory = cinder.api.contrib.standard_extensions
  6.     factory = importutils.import_class(ext_factory)
  7.     factory(self)
复制代码

这里ext_factory = cinder.api.contrib.standard_extensions,所以调用的是方法/cinder/api/contrib/__init__.py----def standard_extensions;
我们继续来看方法:

  1. def standard_extensions(ext_mgr):
  2.     extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__)
复制代码
  1. def load_standard_extensions(ext_mgr, logger, path, package, ext_list=None):
  2.     """
  3.     Registers all standard API extensions.
  4.     ext_mgr =
  5.     logger =
  6.     path = ['/usr/lib/python2.6/site-packages/cinder/api/contrib']
  7.     package = cinder.api.contrib
  8.     ext_list = None
  9.     """
  10.     # our_dir = path[0] = /usr/lib/python2.6/site-packages/cinder/api/contrib
  11.     our_dir = path[0]
  12.     for dirpath, dirnames, filenames in os.walk(our_dir):      
  13.         # Compute the relative package name from the dirpath
  14.         relpath = os.path.relpath(dirpath, our_dir)
  15.         
  16.         if relpath == '.':
  17.             relpkg = ''
  18.         else:
  19.             relpkg = '.%s' % '.'.join(relpath.split(os.sep))
  20.         # Now, consider each file in turn, only considering .py files
  21.         for fname in filenames:
  22.             
  23.             root, ext = os.path.splitext(fname)
  24.             # Skip __init__ and anything that's not .py
  25.             if ext != '.py' or root == '__init__':
  26.                 continue
  27.             # Try loading it
  28.             classname = "%s%s" % (root[0].upper(), root[1:])
  29.             
  30.             classpath = ("%s%s.%s.%s" % (package, relpkg, root, classname))
  31.             if ext_list is not None and classname not in ext_list:
  32.                 logger.debug("Skipping extension: %s" % classpath)
  33.                 continue
  34.             try:
  35.                 ext_mgr.load_extension(classpath)
  36.             except Exception as exc:
  37.                 logger.warn(_('Failed to load extension %(classpath)s: '
  38.                               '%(exc)s'),
  39.                             {'classpath': classpath, 'exc': exc})
  40.         # Now, let's consider any subdirectories we may have...
  41.         subdirs = []
  42.         for dname in dirnames:            
  43.             # Skip it if it does not have __init__.py
  44.             if not os.path.exists(os.path.join(dirpath, dname, '__init__.py')):
  45.                 continue
  46.             ext_name = ("%s%s.%s.extension" %
  47.                         (package, relpkg, dname))
  48.             try:
  49.                 ext = importutils.import_class(ext_name)
  50.             except ImportError:
  51.                 # extension() doesn't exist on it, so we'll explore
  52.                 # the directory for ourselves
  53.                 subdirs.append(dname)
  54.             else:
  55.                 try:
  56.                     ext(ext_mgr)
  57.                 except Exception as exc:
  58.                     logger.warn(_('Failed to load extension %(ext_name)s: %(exc)s), {'ext_name': ext_name, 'exc': exc})
  59.         # Update the list of directories we'll explore...
  60.         dirnames[:] = subdirs
复制代码

我们来看这个方法的实现,首先来看两个变量的输出示例:
our_dir = path[0] = /usr/lib/python2.6/site-packages/cinder/api/contrib
filenames = [
'volume_tenant_attribute.pyc', 'volume_type_encryption.py', 'volume_host_attribute.pyc', 'snapshot_actions.py', 'types_extra_specs.pyo', 'volume_actions.pyc', 'getname.pyc', 'volume_type_encryption.pyo', 'snapshot_actions.pyo', 'quota_classes.pyc', 'getname.py',
'services.pyc', 'image_create.py', 'snapshot_actions.pyc', 'services.pyo', 'image_create.pyo', 'volume_encryption_metadata.py', 'qos_specs_manage.pyc', 'availability_zones.pyc', 'quota_classes.py', 'quotas.pyc', 'volume_transfer.pyo', 'volume_tenant_attribute.pyo',
'quota_classes.pyo', 'image_create.pyc', 'volume_actions.py~', 'scheduler_hints.py~', 'snapshot_actions.py~', 'hosts.py', 'volume_host_attribute.pyo', 'hosts.pyo', 'extended_snapshot_attributes.py~', 'volume_encryption_metadata.py~', 'quotas.pyo', 'types_extra_specs.py',
'volume_image_metadata.pyc', 'volume_type_encryption.pyc', 'volume_image_metadata.pyo', 'volume_mig_status_attribute.py', 'scheduler_hints.py', 'volume_mig_status_attribute.pyo', 'qos_specs_manage.py~', 'services.py', 'volume_host_attribute.py', 'volume_encryption_metadata.pyc',
'hosts.pyc', 'scheduler_hints.pyo', '__init__.py', 'volume_encryption_metadata.pyo', 'volume_mig_status_attribute.pyc', 'backups.py~', 'volume_host_attribute.py~', 'quota_classes.py~', 'extended_snapshot_attributes.pyc', 'availability_zones.pyo', 'quotas.py',
'volume_type_encryption.py~', 'volume_transfer.pyc', 'volume_mig_status_attribute.py~', 'scheduler_hints.pyc', 'backups.pyc', 'volume_tenant_attribute.py~', '__init__.pyc', 'hosts.py~', 'backups.pyo', 'types_extra_specs.pyc', 'qos_specs_manage.py', 'types_manage.py~',
'admin_actions.pyo', 'volume_tenant_attribute.py', 'volume_actions.pyo', 'types_extra_specs.py~', 'admin_actions.pyc', 'extended_snapshot_attributes.pyo', 'qos_specs_manage.pyo', 'volume_image_metadata.py', '__init__.py~', 'types_manage.pyo', 'availability_zones.py',
'backups.py', 'volume_actions.py', 'types_manage.py', 'types_manage.pyc', 'extended_snapshot_attributes.py', '__init__.pyo', 'services.py~', 'volume_image_metadata.py~', 'availability_zones.py~', 'quotas.py~', 'volume_transfer.py~', 'admin_actions.py', 'volume_transfer.py']
从而我们可以知道,这个方法中会遍历路径/usr/lib/python2.6/site-packages/cinder/api/contrib,调用方法ext_mgr.load_extension加载路径/usr/lib/python2.6/site-packages/cinder/api/contrib下每个以.py后缀结尾的文件对应的类,如:
cinder.api.contrib.volume_type_encryption.Volume_type_encryption
cinder.api.contrib.snapshot_actions.Snapshot_actions
cinder.api.contrib.getname.Getname
cinder.api.contrib.image_create.Image_create
cinder.api.contrib.volume_encryption_metadata.Volume_encryption_metadata
cinder.api.contrib.quota_classes.Quota_classes
cinder.api.contrib.hosts.Hosts
cinder.api.contrib.types_extra_specs.Types_extra_specs
cinder.api.contrib.volume_mig_status_attribute.Volume_mig_status_attribute
cinder.api.contrib.scheduler_hints.Scheduler_hints
cinder.api.contrib.services.Services
cinder.api.contrib.volume_host_attribute.Volume_host_attribute
cinder.api.contrib.quotas.Quotas
cinder.api.contrib.qos_specs_manage.Qos_specs_manage
cinder.api.contrib.volume_tenant_attribute.Volume_tenant_attribute
cinder.api.contrib.volume_image_metadata.Volume_image_metadata
cinder.api.contrib.availability_zones.Availability_zones
cinder.api.contrib.backups.Backups
cinder.api.contrib.volume_actions.Volume_actions
cinder.api.contrib.types_manage.Types_manage
cinder.api.contrib.extended_snapshot_attributes.Extended_snapshot_attributes
cinder.api.contrib.admin_actions.Admin_actions
cinder.api.contrib.volume_transfer.Volume_transfer
从源码中可以看到,这里再次调用了方法load_extension:
  1. def load_extension(self, ext_factory):
  2.     """
  3.     Execute an extension factory.
  4.     """
  5.         
  6.     LOG.debug(_("Loading extension %s"), ext_factory)
  7.     # Load the factory
  8.     factory = importutils.import_class(ext_factory)
  9.     # Call it
  10.     LOG.debug(_("Calling extension factory %s"), ext_factory)
  11.     factory(self)
复制代码

这个方法中factory(self)会调用上述各个类,进一步看源码,发现上述各个类中都没有实现def __init__方法,都要调用其父类中的def __init__方法;
以cinder.api.contrib.backups.Backups为例:

  1. class Backups(extensions.ExtensionDescriptor):
  2.     """Backups support."""
  3.     name = 'Backups'
  4.     alias = 'backups'
  5.     namespace = 'http://docs.openstack.org/volume/ext/backups/api/v1'
  6.     updated = '2012-12-12T00:00:00+00:00'
  7.     def get_resources(self):
  8.         resources = []
  9.         res = extensions.ResourceExtension(
  10.             Backups.alias,
  11.             BackupsController(),
  12.             collection_actions={'detail': 'GET'},
  13.             member_actions={'restore': 'POST'})
  14.         resources.append(res)
  15.         return resources
复制代码

这里先会实现对变量name,alias,namespace,updated的赋值,然后会调用父类ExtensionDescriptor中的__init__方法。

  1. class ExtensionDescriptor(object):
  2.     """
  3.     Base class that defines the contract for extensions.
  4.     Note that you don't have to derive from this class to have a valid
  5.     extension; it is purely a convenience.
  6.     API扩展描述的基类;
  7.     """
  8.     name = None
  9.     alias = None
  10.     namespace = None
  11.     updated = None
  12.     def __init__(self, ext_mgr):
  13.         """
  14.         Register extension with the extension manager.
  15.         """
  16.         ext_mgr.register(self)
复制代码
  1. def register(self, ext):
  2.     # Do nothing if the extension doesn't check out
  3.     if not self._check_extension(ext):
  4.         return
  5.     alias = ext.alias
  6.     LOG.audit(_('Loaded extension: %s'), alias)
  7.     if alias in self.extensions:
  8.         raise exception.Error("Found duplicate extension: %s" % alias)
  9.         
  10.     self.extensions[alias] = ext
复制代码

以cinder.api.contrib.volume_image_metadata.Volume_image_metadata为例,这里就是把变量alias = os-vol-image-meta和类cinder.api.contrib.volume_image_metadata.Volume_image_metadata一一对应起来;
再以cinder.api.contrib.backups.Backups为例,这里就是把变量alias = backups和类cinder.api.contrib.backups.Backups一一对应起来;




相关文章


OpenStack Cinder服务启动过程中的资源加载和扩展源码解析之二
http://www.aboutyun.com/thread-10223-1-1.html

OpenStack Cinder服务启动过程中的资源加载和扩展源码解析之三
http://www.aboutyun.com/thread-10224-1-1.html




原文链接:http://blog.csdn.net/gaoxingnengjisuan/article/details/21722111



没找到任何评论,期待你打破沉寂

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

本版积分规则

关闭

推荐上一条 /2 下一条