导读
- APIRouter类实现
上一篇“Neutron-server的启动流程和工作方式(一)”里提到APIRouter类实现了app功能的扩展和加载过程,本文进一步展开分析。
APIRouter类
69 class APIRouter(base_wsgi.Router):
70
71 @classmethod
72 def factory(cls, global_config, **local_config):
73 return cls(**local_config)
74
75 def __init__(self, **local_config):
76 mapper = routes_mapper.Mapper()
77 manager.init()
78 plugin = directory.get_plugin()
#(1)生成一个PluginAwareExtensionManager实例
79 ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
80 ext_mgr.extend_resources("2.0", attributes.RESOURCE_ATTRIBUTE_MAP)
81
82 col_kwargs = dict(collection_actions=COLLECTION_ACTIONS,
83 member_actions=MEMBER_ACTIONS)
84 #(2)建立resource到URL的映射关系
85 def _map_resource(collection, resource, params, parent=None):
86 allow_bulk = cfg.CONF.allow_bulk
#(3) 创建resource
87 controller = base.create_resource(
88 collection, resource, plugin, params, allow_bulk=allow_bulk,
89 parent=parent, allow_pagination=True,
90 allow_sorting=True)
91 path_prefix = None
92 if parent:
93 path_prefix = "/%s/{%s_id}/%s" % (parent['collection_name'],
94 parent['member_name'],
95 collection)
#根据之前创建的Controller、REQUIREMENTS和path_prefix建立字典
96 mapper_kwargs = dict(controller=controller,
97 requirements=REQUIREMENTS,
98 path_prefix=path_prefix,
99 **col_kwargs)
#最后根据字典,建立neutron api的顶级资源集合体
100 return mapper.collection(collection, resource,
101 **mapper_kwargs)
102
103 mapper.connect('index', '/', controller=Index(RESOURCES))
104 for resource in RESOURCES:
105 _map_resource(RESOURCES[resource], resource,
106 attributes.RESOURCE_ATTRIBUTE_MAP.get(
107 RESOURCES[resource], dict()))
108 resource_registry.register_resource_by_name(resource)
109
110 for resource in SUB_RESOURCES:
111 _map_resource(SUB_RESOURCES[resource]['collection_name'], resource,
112 attributes.RESOURCE_ATTRIBUTE_MAP.get(
113 SUB_RESOURCES[resource]['collection_name'],
114 dict()),
115 SUB_RESOURCES[resource]['parent'])
116
117 # Certain policy checks require that the extensions are loaded
118 # and the RESOURCE_ATTRIBUTE_MAP populated before they can be
119 # properly initialized. This can only be claimed with certainty
120 # once this point in the code has been reached. In the event
121 # that the policies have been initialized before this point,
122 # calling reset will cause the next policy check to
123 # re-initialize with all of the required data in place.
124 policy.reset()
125 super(APIRouter, self).__init__(mapper)
上面的代码中比较重要就是(1)和(2)两部分。先来看下(1)的实现,首先生成PluginAwareExtensionManager类,再调用get_instance()方法获取ext_mgr实例:
486 class PluginAwareExtensionManager(ExtensionManager):
487
488 _instance = None
489
490 def __init__(self, path, plugins):
491 self.plugins = plugins
#调用父类ExtensionManager的构造函数
492 super(PluginAwareExtensionManager, self).__init__(path)
493 self.check_if_plugin_extensions_loaded()
305 class ExtensionManager(object):
306 """Load extensions from the configured extension path.
307
308 See tests/unit/extensions/foxinsocks.py for an
309 example extension implementation.
310 """
311
312 def __init__(self, path):
313 LOG.info(_LI('Initializing extension manager.'))
314 self.path = path
315 self.extensions = {}
316 self._load_all_extensions()
430 def _load_all_extensions(self):
431 """Load extensions from the configured path.
432
433 The extension name is constructed from the module_name. If your
434 extension module is named widgets.py, the extension class within that
435 module should be 'Widgets'.
436
437 See tests/unit/extensions/foxinsocks.py for an example extension
438 implementation.
439 """
440
441 for path in self.path.split(':'):
442 if os.path.exists(path):
443 self._load_all_extensions_from_path(path)
444 else:
445 LOG.error(_LE("Extension path '%s' doesn't exist!"), path)
447 def _load_all_extensions_from_path(self, path):
448 # Sorting the extension list makes the order in which they
449 # are loaded predictable across a cluster of load-balanced
450 # Neutron Servers
451 for f in sorted(os.listdir(path)):
452 try:
453 LOG.debug('Loading extension file: %s', f)
454 mod_name, file_ext = os.path.splitext(os.path.split(f)[-1])
455 ext_path = os.path.join(path, f)
456 if file_ext.lower() == '.py' and not mod_name.startswith('_'):
457 mod = imp.load_source(mod_name, ext_path)
458 ext_name = mod_name[0].upper() + mod_name[1:]
459 new_ext_class = getattr(mod, ext_name, None)
460 if not new_ext_class:
461 LOG.warning(_LW('Did not find expected name '
462 '"%(ext_name)s" in %(file)s'),
463 {'ext_name': ext_name,
464 'file': ext_path})
465 continue
#根据path下的文件名,生成extension,并调用add_extension加入到self.extensions[]中
466 new_ext = new_ext_class()
467 self.add_extension(new_ext)
468 except Exception as exception:
469 LOG.warning(_LW("Extension file %(f)s wasn't loaded due to "
470 "%(exception)s"),
471 {'f': f, 'exception': exception})
上述代码主要是将配置的extension路径下的所有"*.py"的文件进行排序后分别加载,获取文件名为extension的名称,其中加载的模块包括external_net,dns,dvr等等。
回到(1),类初始化完成后,就调用get_instance():
530 def get_instance(cls):
531 if cls._instance is None:
532 service_plugins = directory.get_plugins()
533 cls._instance = cls(get_extensions_path(service_plugins),
534 service_plugins)
535 return cls._instance
这个函数中,获取路径下所有文件的path和服务的插件,并构建cls返回。
再来看(2),这个是内置函数,在下面被遍历调用.这个函数中比较重要的是(3):
# create_resource中主要是根据资源信息建立Controller,这个Controller就是用以之后api请求到来之后真正去处理这些请求
#这个Controller是在neutron.api.v2.base中
#之后wsgi_resource.Resource中根据collection、resource以及对应的RESOURCE_ATTRIBUTE_MAP的信息
#创建一个xml和json的序列化和反序列化的对象
#序列化指:对xml或json语句进行解析,确定要引用的动作
#反序列化指:进行xml或json的封装
750 def create_resource(collection, resource, plugin, params, allow_bulk=False,
751 member_actions=None, parent=None, allow_pagination=False,
752 allow_sorting=False):
753 controller = Controller(plugin, collection, resource, params, allow_bulk,
754 member_actions=member_actions, parent=parent,
755 allow_pagination=allow_pagination,
756 allow_sorting=allow_sorting)
757
758 return wsgi_resource.Resource(controller, FAULT_MAP)
通过(3)创建的controller,就是著名的MVC模式中C,就是用来干脏活累活的主体了。_map_resource函数就是用来实现对资源的映射,具体可以理解为,得到了URL,通过这里的映射关系,匹配定位到具体要调用的方法上。
至此,app功能的扩展和加载就搞定了。
网友评论