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

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

class APIRouter(base_wsgi.Router):

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

APIRouter的factory方法是一個classmethod,直接調用APIRouter的構造函數,對APIRouter進行初始化。下面重點分析一下APIRouter類的構造函數:api

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方法:app

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方法。這裏把相關的方法都貼出來,一塊兒分析。ssh

_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方法:ide

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)並初始化:post

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中,供以後的各個流程調用。
相關文章
相關標籤/搜索