Ocata Neutron代码分析(六)——APIRouter的初始化(2)PluginAwareExtensionManager的初始化

上篇分析了core plugin和service plugin的加载过程。APIRouter的构造函数下一步将进行PluginAwareExtensionManager的初始化:

ext_mgr = extensions.PluginAwareExtensionManager.get_instance()

PluginAwareExtensionManager也是一个单例模式。

class PluginAwareExtensionManager(ExtensionManager):
    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            service_plugins = directory.get_plugins()                   # 获取plugins(包括core和service)的弱引用,统称service_plugins。
            cls._instance = cls(get_extensions_path(service_plugins),   # 获取service_plugins支持的extensions的path。
                                service_plugins)
        return cls._instance
在第一次调用get_instance函数时,其_instance变量为None,进入if分支。
首先通过上一节分析的get_plugins函数获取plugins名称和弱引用对应的字典,包括core plugin和service plugin,这里统称service_plugins。
然后,实例化PluginAwareExtensionManager。这里首先调用了get_extensions_path函数:
def get_extensions_path(service_plugins=None):
    paths = collections.OrderedDict()

    # Add Neutron core extensions
    paths[core_extensions.__path__[0]] = 1
    if service_plugins:
        # Add Neutron *-aas extensions
        for plugin in service_plugins.values():
            neutron_mod = provider_configuration.NeutronModule(
                plugin.__module__.split('.')[0])
            try:
                paths[neutron_mod.module().extensions.__path__[0]] = 1
            except AttributeError:
                # Occurs normally if module has no extensions sub-module
                pass

    # Add external/other plugins extensions
    if cfg.CONF.api_extensions_path:
        for path in cfg.CONF.api_extensions_path.split(":"):
            paths[path] = 1

    LOG.debug("get_extension_paths = %s", paths)

    # Re-build the extension string
    path = ':'.join(paths)
    return path
get_extensions_path函数构造了一个paths字典,extensions的路径为key,values均为1。这个paths包括三个部分:
  • core_extensions path:即/neutron/extensions。core_extensions保存在这个目录中;
  • Neutron *-aas extensions path:形如*-aas的extensions,例如vpnaas;
  • 配置文件中指定的其他path:配置文件中的api_extensions_path指定。
获取到extensions的path后,调用PluginAwareExtensionManager的构造函数:
class PluginAwareExtensionManager(ExtensionManager):
    _instance = None
    def __init__(self, path, plugins):
        self.plugins = plugins
        super(PluginAwareExtensionManager, self).__init__(path)
        self.check_if_plugin_extensions_loaded()
可以看到,构造函数主要分为两个步骤:
  • 通过调用父类ExtensionManager的构造函数来加载extensions;
  • 检查是否所有extensions都被加载,否则抛出异常。
下面分析一下父类ExtensionManager的构造函数:
class ExtensionManager(object):
    """Load extensions from the configured extension path.

    See tests/unit/extensions/foxinsocks.py for an
    example extension implementation.
    """

    def __init__(self, path):
        LOG.info(_LI('Initializing extension manager.'))
        self.path = path
        self.extensions = {}
        self._load_all_extensions()

 ExtensionManager的构造函数调用_load_all_extensions:

class ExtensionManager(object):
    def _load_all_extensions(self):
        """Load extensions from the configured path.

        The extension name is constructed from the module_name. If your
        extension module is named widgets.py, the extension class within that
        module should be 'Widgets'.

        See tests/unit/extensions/foxinsocks.py for an example extension
        implementation.
        """

        for path in self.path.split(':'):                   # 分别处理path中的各个extensions目录
            if os.path.exists(path):
                self._load_all_extensions_from_path(path)
            else:
                LOG.error(_LE("Extension path '%s' doesn't exist!"), path)

    def _load_all_extensions_from_path(self, path):
        # Sorting the extension list makes the order in which they
        # are loaded predictable across a cluster of load-balanced
        # Neutron Servers
        for f in sorted(os.listdir(path)):                                  # 将path(extension目录)下的所有文件进行排序并遍历。
            try:
                LOG.debug('Loading extension file: %s', f)
                mod_name, file_ext = os.path.splitext(os.path.split(f)[-1])
                ext_path = os.path.join(path, f)
                if file_ext.lower() == '.py' and not mod_name.startswith('_'):  # 将所有.py文件(除__init__.py)作为一个extension添加到self.extensions中。
                    mod = imp.load_source(mod_name, ext_path)
                    ext_name = mod_name[0].upper() + mod_name[1:]               # extensions的实现类(ext_name)是对应模块名(mod_name)的首字母大写。
                    new_ext_class = getattr(mod, ext_name, None)                # 根据ext_name到相应模块中获取extensions的实现类,没有获取到则抛出异常。
                    if not new_ext_class:
                        LOG.warning(_LW('Did not find expected name '
                                        '"%(ext_name)s" in %(file)s'),
                                    {'ext_name': ext_name,
                                     'file': ext_path})
                        continue
                    new_ext = new_ext_class()                                   # 实例化extensions目录下各.py文件中的extensions实现类。
                    self.add_extension(new_ext)                                 # 将各个extensions的实例添加到ExtensionManager的extensions属性中。
            except Exception as exception:
                LOG.warning(_LW("Extension file %(f)s wasn't loaded due to "
                                "%(exception)s"),
                            {'f': f, 'exception': exception})
可以看出,各个extension的加载就是在_load_all_extensions函数中进行的。总而言之,这个过程就是到各个extensions目录(上面已经分析过这些目录分为三个部分)中遍历每个.py文件(除了__init__.py),每个.py文件对应一个extension,该extension对应的实现类是.py文件的文件名的首字母大写。获取到各个extension的对应类后实例化,并将实例添加到PluginAwareExtensionManager.extensions变量中。
完成各个extension的加载后,在PluginAwareExtensionManager的构造函数中,还要检查是否所有extensions都被加载:
class PluginAwareExtensionManager(ExtensionManager):
    def check_if_plugin_extensions_loaded(self):
        """Check if an extension supported by a plugin has been loaded."""
        plugin_extensions = self.get_supported_extension_aliases()          # 获取self.plugins中各个plugin支持的extensions,并组成set。
        missing_aliases = plugin_extensions - set(self.extensions)          # 取两个set的差集。
        missing_aliases -= _PLUGIN_AGNOSTIC_EXTENSIONS                      # 排除不依赖plugin的extension。
        if missing_aliases:                                                 # 如果仍有未加载的extension,则抛出异常。
            raise exceptions.ExtensionsNotFound(
                extensions=list(missing_aliases))
至此,PluginAwareExtensionManager的初始化过程就结束了。其中最重要的是PluginAwareExtensionManager中的extensions变量,这是一个保存了每个extension的实现类的实例的列表。
由于PluginAwareExtensionManager是单例模式的实现,所以之后统一通过其get_instance函数来获取这个唯一的extension manager。
posted @ 2018-01-09 11:28  卢卡_莫德里奇  阅读(407)  评论(0编辑  收藏  举报