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。