Ocata Neutron代码分析(六)——APIRouter的初始化(1)加载core plugin和service plugin

在分析api-paste.ini时,曾分析到wsgi app neutronapiapp_v2_0是直接调用/neutron/api/v2/router.py中APIRouter的factory方法:

class APIRouter(base_wsgi.Router):

  @classmethod
  def factory(cls, global_config, **local_config):
  return cls(**local_config)

APIRouter的factory方法是一个classmethod,直接调用APIRouter的构造函数,对APIRouter进行初始化。下面重点分析一下APIRouter类的构造函数:

class APIRouter(base_wsgi.Router):
    def __init__(self, **local_config):
        mapper = routes_mapper.Mapper()     # 获取routes.Mapper的实例。
        manager.init()                      # 初始化NeutronManager来读取配置文件和加载plugin,该部分最关键的步骤。
        plugin = directory.get_plugin()     # 从_PLUGIN_DIRECTORY._plugin中获取core plugin。
        ext_mgr = extensions.PluginAwareExtensionManager.get_instance()     # 根据所有的plugins获取相应path并加载extensions。
        ext_mgr.extend_resources("2.0", attributes.RESOURCE_ATTRIBUTE_MAP)

        col_kwargs = dict(collection_actions=COLLECTION_ACTIONS,
                          member_actions=MEMBER_ACTIONS)

        def _map_resource(collection, resource, params, parent=None):
            ......
            
        mapper.connect('index', '/', controller=Index(RESOURCES))       # http://controller:9696/v2.0/

        for resource in RESOURCES:
            _map_resource(RESOURCES[resource], resource,
                          attributes.RESOURCE_ATTRIBUTE_MAP.get(
                              RESOURCES[resource], dict()))
            resource_registry.register_resource_by_name(resource)

        for resource in SUB_RESOURCES:
            _map_resource(SUB_RESOURCES[resource]['collection_name'], resource,
                          attributes.RESOURCE_ATTRIBUTE_MAP.get(
                              SUB_RESOURCES[resource]['collection_name'],
                              dict()),
                          SUB_RESOURCES[resource]['parent'])

        # Certain policy checks require that the extensions are loaded
        # and the RESOURCE_ATTRIBUTE_MAP populated before they can be
        # properly initialized. This can only be claimed with certainty
        # once this point in the code has been reached. In the event
        # that the policies have been initialized before this point,
        # calling reset will cause the next policy check to
        # re-initialize with all of the required data in place.
        policy.reset()
        super(APIRouter, self).__init__(mapper)
APIRouter的内容比较复杂,主要分为以下三个部分:
  1. 初始化NeutronManager来加载core plugin和service plugin;
  2. 初始化PluginAwareExtensionManager;
  3. 调用_map_resource方法为四个顶级resource进行map。
下面分为三篇来逐个分析这三个部分。首先是NeutronManager,该部分在APIRouter的构造函数中只有一行代码:
manager.init()

 调用/neutron/manager.py中的init方法:

def init():
    """Call to load the plugins (core+services) machinery."""
    # TODO(armax): use is_loaded() when available
    if not directory.get_plugins():
        NeutronManager.get_instance()

首先,调用了neutron_lib库中的plugins.directory.py中的get_plugins方法。这里把相关的方法都贴出来,一起分析。

_PLUGIN_DIRECTORY = None

@_synchronized("plugin-directory")
def _create_plugin_directory():
    global _PLUGIN_DIRECTORY
    if _PLUGIN_DIRECTORY is None:
        _PLUGIN_DIRECTORY = _PluginDirectory()
    return _PLUGIN_DIRECTORY

def _get_plugin_directory():
    if _PLUGIN_DIRECTORY is None:
        return _create_plugin_directory()
    return _PLUGIN_DIRECTORY

def add_plugin(alias, plugin):
    _get_plugin_directory().add_plugin(alias, plugin)

def get_plugin(alias=constants.CORE):
    return _get_plugin_directory().get_plugin(alias)

def get_plugins():
    return _get_plugin_directory().plugins

def get_unique_plugins():
    return _get_plugin_directory().unique_plugins

def is_loaded():
    return _get_plugin_directory().is_loaded
这里申请了一个全局变量_PLUGIN_DIRECTORY,通过_get_plugin_directory方法来获取这个变量,并且在第一次调用_get_plugin_directory方法(即_PLUGIN_DIRECTORY为None时)时,对_PLUGIN_DIRECTORY变量进行赋值。这里申明global是为了在函数体内对一个函数体外的变量进行赋值。可以看到_PLUGIN_DIRECTORY变量是_PluginDirectory类的实例,其构造函数稍后分析。
其余几个函数,均首先获取了_PLUGIN_DIRECTORY变量,再对其进行操作。具体含义如下:
add_plugin(alias, plugin):
向_PLUGIN_DIRECTORY添加名为alias的plugin。
get_plugin(alias=constants.CORE):
从_PLUGIN_DIRECTORY中获取名为alias(默认为core plugin)的plugin。
get_plugins():
获取_PLUGIN_DIRECTORY中所有的plugin。
get_unique_plugins():
获取_PLUGIN_DIRECTORY中不同类型的plugin。
is_loaded():
获取_PLUGIN_DIRECTORY是否为None。
_PLUGIN_DIRECTORY变量是_PluginDirectory类的实例,_PluginDirectory类中均有函数或属性与上述几个函数相对应。
class _PluginDirectory(object):
    """A directory of activated plugins in a Neutron Deployment.

    The directory is bootstrapped by a Neutron Manager running in
    the context of a Neutron Server process.
    """

    def __init__(self):
        self._plugins = {}                         # 构造函数只是对_plugins属性进行了初始化。

    def add_plugin(self, alias, plugin):
        """Add a plugin of type 'alias'."""
        self._plugins[alias] = plugin

    def get_plugin(self, alias):
        """Get a plugin for a given alias or None if not present."""
        p = self._plugins.get(alias)
        return weakref.proxy(p) if p else None     # 如果没有名为alias的plugin,返回None。否则返回对应plugin的弱引用。

    @property
    def plugins(self):                             # _PluginDirectory的plugins属性是一个函数,返回各个plugin的弱引用。
        """The mapping alias -> weak reference to the plugin."""
        return dict((x, weakref.proxy(y))
                    for x, y in self._plugins.items())

    @property
    def unique_plugins(self):
        """A sequence of the unique plugins activated in the environments."""
        return tuple(weakref.proxy(x) for x in set(self._plugins.values()))

    @property
    def is_loaded(self):
        """True if the directory is non empty."""
        return len(self._plugins) > 0

 回到/neutron/manager.py中的init方法:

def init():
    """Call to load the plugins (core+services) machinery."""
    # TODO(armax): use is_loaded() when available
    if not directory.get_plugins():
        NeutronManager.get_instance()
第一次调用get_plugins方法,此时_PluginDirectory._plugin为空,返回None。接着,调用NeutronManager的get_instance方法来实际加载各个plugin。
有没有觉得get_instance这个函数名特别眼熟,这里NeutronManager也是一个单例模式的实现。
@six.add_metaclass(profiler.TracedMeta)
class NeutronManager(object):
    """Neutron's Manager class.

    Neutron's Manager class is responsible for parsing a config file and
    instantiating the correct plugin that concretely implements
    neutron_plugin_base class.
    """
    @classmethod
    @utils.synchronized("manager")
    def _create_instance(cls):
        if not cls.has_instance():
            cls._instance = cls()

    @classmethod
    def has_instance(cls):
        return cls._instance is not None

    @classmethod
    def get_instance(cls):
        # double checked locking
        if not cls.has_instance():
            cls._create_instance()
        return cls._instance

 通过对NeutronManager的has_instance的两次判断,确定NeutronManager没有被实例化,这时调用其构造函数:

@six.add_metaclass(profiler.TracedMeta)
class NeutronManager(object):
    _instance = None
    def __init__(self, options=None, config_file=None):
        # If no options have been provided, create an empty dict
        if not options:
            options = {}

        """ 检查配置文件中是否配置了core_plugin,否则抛出异常 """
        msg = validate_pre_plugin_load()
        if msg:
            LOG.critical(msg)
            raise Exception(msg)

        # NOTE(jkoelker) Testing for the subclass with the __subclasshook__
        #                breaks tach monitoring. It has been removed
        #                intentionally to allow v2 plugins to be monitored
        #                for performance metrics.
        plugin_provider = cfg.CONF.core_plugin                      # 从配置文件中获取core_plugin,一般为ml2
        LOG.info(_LI("Loading core plugin: %s"), plugin_provider)   # server.log中打印 INFO neutron.manager [-] Loading core plugin: ml2
        # NOTE(armax): keep hold of the actual plugin object
        plugin = self._get_plugin_instance(CORE_PLUGINS_NAMESPACE,  # 通过namespace和plugin_provider获取plugin。
                                           plugin_provider)
        directory.add_plugin(lib_const.CORE, plugin)                # 添加core_plugin(Ml2Plugin的实例)到_PLUGIN_DIRECTORY._plugins中。
        """ 检查配置文件中的dhcp_agents_per_network,否则抛出异常 """
        msg = validate_post_plugin_load()
        if msg:
            LOG.critical(msg)
            raise Exception(msg)

        # load services from the core plugin first
        self._load_services_from_core_plugin(plugin)
        self._load_service_plugins()                                # 加载service_plugins,包括配置文件中指定的和默认的。
        # Used by pecan WSGI
        self.resource_plugin_mappings = {}
        self.resource_controller_mappings = {}
        self.path_prefix_resource_mappings = defaultdict(list)

在构造函数中,分别对core plugin和service plugin进行了初始化,并调用directory.add_plugin将各个plugin的实例添加到_PLUGIN_DIRECTORY变量中,供之后调用。其中,core plugin是直接调用_get_plugin_instance方法来获取相应的plugin类(一般为Ml2Plugin)并初始化:

class NeutronManager(object):
    def _get_plugin_instance(self, namespace, plugin_provider):                 # 通过不同的namespace中的plugin_provider获取相应的plugin。
        plugin_class = self.load_class_for_provider(namespace, plugin_provider) # 调用load_class_for_provider函数来返回Plugin类。
        return plugin_class()                                                   # 实例化Plugin类,在此处完成各个plugin的__init__方法的调用。
而service plugin是直接调用_load_service_plugins方法来加载的,但这个方法也是通过_get_plugin_instance方法来加载各个service plugin。
通过_load_service_plugins方法加载的service plugin分为两种:一种为配置文件中指定的service_plugins,另一种为默认的service plugins,由函数_get_default_service_plugins获取。
 
总结:
各个plugin的加载均是通过NeutronManager以单例模式的方式在其构造函数中实现的,而各个plugin在初始化后的实例均以{名称:对应plugin类的实例}这样的字典方式保存在变量_PLUGIN_DIRECTORY中,供之后的各个流程调用。
posted @ 2018-01-09 11:05  卢卡_莫德里奇  阅读(547)  评论(0编辑  收藏  举报