Restful Api服务初始化
api服务的实现是service.NeutronApiService,这是一个符合WSGI规范的app,通过paste进行配置。
neutron/service.py
# serve_wsgi服务
class NeutronApiService(WsgiService):
"""Class for neutron-api service."""
@classmethod
def create(cls, app_name='neutron'):
# Setup logging early, supplying both the CLI options and the
# configuration mapping from the config file
# We only update the conf dict for the verbose and debug
# flags. Everything else must be set up in the conf file...
# Log the options used when starting if we're in debug mode...
config.setup_logging()
service = cls(app_name)
return service
def serve_wsgi(cls):
try:
# create函数进行日志的安装和配置文件的映射。
service = cls.create()
# NeutronApiService中没有start()函数,所以他调用的是父类的start的函数
# 也就是WsgiService这个类中的start函数。
service.start()
except Exception:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Unrecoverable error: please check log '
'for details.'))
return service
class WsgiService(object):
"""Base class for WSGI based services.
For each api you define, you must also define these flags:
:<api>_listen: The address on which to listen
:<api>_listen_port: The port on which to listen
"""
def __init__(self, app_name):
self.app_name = app_name
self.wsgi_app = None
def start(self):
# 父类wsgiService的start()方法,其中_run_wsgi
self.wsgi_app = _run_wsgi(self.app_name)
def _run_wsgi(app_name):
# load_paste_app
app = config.load_paste_app(app_name)
if not app:
LOG.error(_LE('No known API applications configured.'))
return
server = wsgi.Server("Neutron")
server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host,
workers=_get_api_workers())
LOG.info(_LI("Neutron service started, listening on %(host)s:%(port)s"),
{'host': cfg.CONF.bind_host, 'port': cfg.CONF.bind_port})
return server
其中:
service.start()即为self.wsgi_app = _run_wsgi(self.app_name),而该函数最重要的工作是从api-paste.ini中加载app并启动。
调试可以看到config_path指向/etc/neutron/api-paste.ini,neutron通过解析api-paste.ini中的配置信息来进行指定WSGI Application的实现。这里可以看到,在方法load_paste_app中,调用了模块库deploy来实现对api-paste.ini中配置信息的解析和指定WSGI Application的实现。
【注】:
PasteDeployment是一种机制或者说是一种设计模式,它用于在应用WSGI Application和Server提供一个联系的桥梁,并且为用户提供一个接口,当配置好PasteDeployment之后,用户只需调用loadapp方法就可以使用现有的WSGI Application,而保持了WSGIApplication对用户的透明性。
我们可以看到neutron/api-paste.ini中的内容如下:
[composite:neutron]
use = egg:Paste#urlmap
/: neutronversions
/v2.0: neutronapi_v2_0
[composite:neutronapi_v2_0]
use = call:neutron.auth:pipeline_factory
noauth = request_id catch_errors extensions neutronapiapp_v2_0
keystone = request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0
[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory
[filter:catch_errors]
paste.filter_factory = oslo_middleware:CatchErrors.factory
[filter:keystonecontext]
paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
[filter:extensions]
paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory
[app:neutronversions]
paste.app_factory = neutron.api.versions:Versions.factory
[app:neutronapiapp_v2_0]
paste.app_factory = neutron.api.v2.router:APIRouter.factory
通过对api-paste.ini中配置信息的解析,最终就调用了WSGIApplicationapiv2的实现,具体就是neutron.api.v2.router:APIRouter.factory,这个WSGI Application的具体功能就是实现模块功能的扩展和加载过程。之后我们将深入了解该模块是如何实现模块功能的扩展和加载。
paste文件位置可以在配置文件中指定,默认是/etc/neutron/api-paste.ini,在代码中是etc/api-paste.ini。查看api-paste.ini可以确定v2版api的实现是在neutron.api.v2.router:APIRouter
[app:neutronapiapp_v2_0]
paste.app_factory = neutron.api.v2.router:APIRouter.factory
APIRouter在构造时,会根据配置文件core_plugin的配置加载plugin、extension管理器。所在文件:neutron\neutron\api\v2\router.py
class APIRouter(wsgi.Router):
# 一个工厂类方法
@classmethod
def factory(cls, global_config, **local_config):
return cls(**local_config)
# 真正调用的实例化方法
def __init__(self, **local_config):
mapper = routes_mapper.Mapper()
#获取NeutornManage的core_plugin,这个定义在/etc/neutron/neutron.conf,比如我的是
#core_plugin = neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV
plugin = manager.NeutronManager.get_plugin()
#扫描特定路径下的extensions
ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
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):
...
controller = base.create_resource(
collection, resource, plugin, params, allow_bulk=allow_bulk,
parent=parent, allow_pagination=allow_pagination,
allow_sorting=allow_sorting)
...
# 将这些resource加进router中
return mapper.collection(collection, resource, **mapper_kwargs)
# 遍历 {'network': 'networks', 'subnet': 'subnets','port': 'ports'}
# 添加controller
for resource in RESOURCES:
_map_resource(RESOURCES[resource], resource,
attributes.RESOURCE_ATTRIBUTE_MAP.get(
RESOURCES[resource], dict()))
for resource in SUB_RESOURCES: