问题导读
1.本文主要分析什么内容?
2.文中两个函数的作用是什么?
本文是在学习Openstack的过程中整理和总结,由于时间和个人能力有限,错误之处在所难免,欢迎指正!
在上一篇博客Neutron-server服务加载与启动源码分析(一) 通过对api-paste.ini中配置信息的解析,最终就调用了WSGIApplicationapiv2的实现,具体就是neutron.api.v2.router:APIRouter.factory,这个WSGI
Application的具体功能就是实现模块功能的扩展和加载过程。现在就深入了解该模块是如何实现模块功能的扩展和加载。
首先来看下neutron.api.v2.router.py
classAPIRouter(wsgi.Router):
@classmethod
deffactory(cls, global_config,**local_config):
return cls(**local_config)
def__init__(self,**local_config):
#路由映射
mapper = routes_mapper.Mapper()
#获取插件
plugin = manager.NeutronManager.get_plugin()
ext_mgr =extensions.PluginAwareExtensionManager.get_instance() #(1)
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): #(2)
allow_bulk = cfg.CONF.allow_bulk
allow_pagination = cfg.CONF.allow_pagination
allow_sorting = cfg.CONF.allow_sorting
controller = base.create_resource(
collection, resource, plugin, params, allow_bulk=allow_bulk,
parent=parent, allow_pagination=allow_pagination,
allow_sorting=allow_sorting)
path_prefix =None
if parent:
path_prefix ="/%s/{%s_id}/%s"%(parent['collection_name'],
parent['member_name'],
collection)
mapper_kwargs = dict(controller=controller,
requirements=REQUIREMENTS,
path_prefix=path_prefix,
**col_kwargs)
return mapper.collection(collection, resource,
**mapper_kwargs)
mapper.connect('index','/', controller=Index(RESOURCES))
#遍历建立对应资源的Controller
for resource in RESOURCES:
_map_resource(RESOURCES[resource], resource,
attributes.RESOURCE_ATTRIBUTE_MAP.get(
RESOURCES[resource], dict()))
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'])
super(APIRouter, self).__init__(mapper)
这里面有两个重要的函数(1)和(2)
首先来看(1) 函数:ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
最先执行的是PluginAwareExtensionManager的初始化中
classPluginAwareExtensionManager(ExtensionManager):
_instance =None
def__init__(self, path, plugins):
self.plugins = plugins
super(PluginAwareExtensionManager, self).__init__(path)
self.check_if_plugin_extensions_loaded() 复制代码
调用父类的初始化,这边的path为/usr/lib/python2.7/dist-packages/neutron/extensions,根据注释可知是配置的extension路径。
classExtensionManager(object):
def__init__(self, path):
LOG.info(_('Initializing extension manager.'))
self.path = path
self.extensions ={}
self._load_all_extensions()
policy.reset() 复制代码
在_load_all_extensions()中
for path in self.path.split(':'):
if os.path.exists(path):
self._load_all_extensions_from_path(path)
else:
LOG.error(_("Extension path '%s' doesn't exist!"), path) 复制代码
在_load_all_extensions_from_path(path)中将/usr/lib/python2.7/dist-packages/neutron/extensions路径下的所有为“.py“的文件按照sorted排序顺序分别加载
def_load_all_extensions_from_path(self, path):
# 对文件进行排序取出并加载
for f in sorted(os.listdir(path)):
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'andnot mod_name.startswith('_'):
mod = imp.load_source(mod_name, ext_path)
ext_name = mod_name[0].upper()+ mod_name[1:]
new_ext_class = getattr(mod, ext_name,None)
ifnot new_ext_class:
LOG.warn(_('Did not find expected name '
'"%(ext_name)s" in %(file)s'),
{'ext_name': ext_name,
'file': ext_path})
continue
new_ext = new_ext_class()
self.add_extension(new_ext)
except Exception as exception:
LOG.warn(_("Extension file %(f)s wasn't loaded due to "
"%(exception)s"),{'f': f,'exception': exception}) 复制代码
其中加载的文件包括external_net.py,agent.py等文件
回到(1)函数,初始化完成,就调用
defget_instance(cls):
if cls._instance isNone:
cls._instance = cls(get_extensions_path(),
manager.NeutronManager.get_service_plugins())
returncls._instance 复制代码
get_instance(cls)函数,在这个函数中,获取路径下的所有文件的paths和服务的插件,来构建cls并且返回。
再来看第二个重要的(2) 函数:def_map_resource(collection, resource, params, parent=None):这个函数是函数中内置的函数,下面被遍历调用。
def _map_resource(collection, resource, params, parent=None):
#读取配置文件中关于分块、分页、排序的设置
allow_bulk = cfg.CONF.allow_bulk
allow_pagination = cfg.CONF.allow_pagination
allow_sorting = cfg.CONF.allow_sorting
# create_resource中主要是根据资源信息建立Controller,这个Controller就是用以之后api请求到来之后真正去处理这些请求
#这个Controller是在neutron.api.v2.base中
#之后wsgi_resource.Resource中根据collection、resource以及对应的RESOURCE_ATTRIBUTE_MAP的信息
#创建一个xml和json的序列化和反序列化的对象
#序列化指:对xml或json语句进行解析,确定要引用的动作
#反序列化指:进行xml或json的封装
controller = base.create_resource(
collection, resource, plugin, params, allow_bulk=allow_bulk,
parent=parent, allow_pagination=allow_pagination,
allow_sorting=allow_sorting)
path_prefix = None
if parent:
path_prefix = "/%s/{%s_id}/%s" % (parent['collection_name'],
parent['member_name'],
collection)
#根据之前创建的controller、REQUIREMENTS和path_prefix建立字典
mapper_kwargs = dict(controller=controller,
requirements=REQUIREMENTS,
path_prefix=path_prefix,
**col_kwargs)
#最后根据字典,建立neutron api的顶级资源集合体
return mapper.collection(collection, resource,
**mapper_kwargs) 复制代码
_map_resource函数从字面的意思我们就可以看出他是对资源进行映射,这边的资源是什么?他对应的就是得到的URL,所以这个函数可以简单的理解为,得到了URL,通过这里实现的映射关系,进行格式匹配,具体定位到所要调用的方法上。这边就将URL的请求信息对应地放置在顶级资源集合体之中,为调用做好准备。
至此,模块功能的扩展和加载就搞定了。
作者:林凯
团队:华为杭州OpenStack团队