在分析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)
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
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()
@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__方法的調用。