Openstack Nova-M V2API启动与extension加载

本文详细介绍了Openstack Nova-M版本中V2API的启动过程,特别是如何加载和使用extension。在M版中,Nova的v2 API通过兼容层与v21 API交互。文章通过代码分析展示了`nova-api`入口点、配置加载、WSGI服务、以及如何通过`api-paste.ini`文件设置URL映射。文中还探讨了Nova的auth策略、filter处理、APIRouterV21扩展注册等关键步骤,揭示了如何实现API的兼容性和扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Openstack Nova-M V2API启动与extension加载

标签(空格分隔): Openstack

在M版中,nova的v2 API默认是使用通过兼容性包装的v21 API。本文只是代码随笔,结合代码阅读时效果更佳。
nova-api 入口 nova.cmd.api.main()

def main():

***

launcher = service.process_launcher()
started = 0
for api in CONF.enabled_apis:
    should_use_ssl = api in CONF.enabled_ssl_apis
    try:
        server = service.WSGIService(api, use_ssl=should_use_ssl)
        launcher.launch_service(server, workers=server.workers or 1)
        started += 1
    except exception.PasteAppNotFound as ex:
        log.warning(
            _LW("%s. ``enabled_apis`` includes bad values. "
                "Fix to remove this warning."), six.text_type(ex))

if started == 0:
    log.error(_LE('No APIs were started. '
                  'Check the enabled_apis config option.'))
    sys.exit(1)

launcher.wait()

来看server = service.WSGIService(api, use_ssl=should_use_ssl),其中以 api = osapi_compute 为例,来看service.WSGIService类:

class WSGIService(service.Service):
"""Provides ability to launch API from a 'paste' configuration."""

def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):
    """Initialize, but do not start the WSGI server.

    :param name: The name of the WSGI server given to the loader.
    :param loader: Loads the WSGI application using the given name.
    :returns: None

    """
    self.name = name
    # NOTE(danms): Name can be metadata, os_compute, or ec2, per
    # nova.service's enabled_apis
    # nova-osapi-compute
    self.binary = 'nova-%s' % name
    self.topic = None
    self.manager = self._get_manager()
    self.loader = loader or wsgi.Loader()
    self.app = self.loader.load_app(name)
    # inherit all compute_api worker counts from osapi_compute
    if name.startswith('openstack_compute_api'):
        wname = 'osapi_compute'
    else:
        wname = name
    self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")
    self.port = getattr(CONF, '%s_listen_port' % name, 0)
    self.workers = (getattr(CONF, '%s_workers' % wname, None) or
                    processutils.get_worker_count())
    if self.workers and self.workers < 1:
        worker_name = '%s_workers' % name
        msg = (_("%(worker_name)s value of %(workers)s is invalid, "
                 "must be greater than 0") %
               {'worker_name': worker_name,
                'workers': str(self.workers)})
        raise exception.InvalidInput(msg)
    self.use_ssl = use_ssl
    self.server = wsgi.Server(name,
                              self.app,
                              host=self.host,
                              port=self.port,
                              use_ssl=self.use_ssl,
                              max_url_len=max_url_len)
    # Pull back actual port used
    self.port = self.server.port
    self.backdoor_port = None

来看self.app = self.loader.load_app(name),此处loader为wsgi模块中Loader的实例,初始化时提供了self.config_path,为api-paster.ini的路径,来看load_app方法:

    def load_app(self, name):
    """Return the paste URLMap wrapped WSGI application.

    :param name: Name of the application to load.
    :returns: Paste URLMap object wrapping the requested application.
    :raises: `nova.exception.PasteAppNotFound`

    """
    try:
        LOG.debug("Loading app %(name)s from %(path)s",
                  {'name': name, 'path': self.config_path})
        return deploy.loadapp("config:%s" % self.config_path, name=name)
    except LookupError:
        LOG.exception(_LE("Couldn't lookup app: %s"), name)
        raise exception.PasteAppNotFound(name=name, path=self.config_path)

这个方法调用了pastedeploy模块的deploy.loadapp方法,并提供了两个参数: 加了config:前缀的api-paste.ini文件的路径以及
name = osapi_compute
下面给出M版自带api-paste.ini模板中相关部分:

[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v2: openstack_compute_api_v21_legacy_v2_compatible
/v2.1: openstack_compute_api_v21

[composite:openstack_compute_api_v21_legacy_v2_compatible]
use = call:nova.api.auth:pipeline_factory_v21
noauth2 = cors compute_req_id faultwrap sizelimit noauth2 legacy_v2_compatible osapi_compute_app_v21
keystone = cors compute_req_id faultwrap sizelimit authtoken keystonecontext legacy_v2_compatible osapi_compute_app_v21

[filter:legacy_v2_compatible]
paste.filter_factory = nova.api.openstack:LegacyV2CompatibleWrapper.factory

[app:osapi_compute_app_v21]
paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory

具体paste.deploy原理大家有兴趣可以去官方文档查看详细手册,这里只简单介绍一些简单具体的实现,先看用于处理osapi_compute的nova.api.openstack.urlmap.urlmap_factory方法:

def urlmap_factory(loader, global_conf, **local_conf):
if 'not_found_app' in local_conf:
    not_found_app = local_conf.pop('not_found_app')
else:
    not_found_app = global_conf.get('not_found_app')
if not_found_app:
    not_found_app = loader.get_app(not_found_app, global_conf=global_conf)
urlmap = URLMap(not_found_app=not_found_app)
for path, app_name in local_conf.items():
    path = paste.urlmap.parse_path_expression(path)
    app = loader.get_app(app_name, global_conf=global_conf)
    urlmap[path] = app
return urlmap

其中抓取日志,可以看到如下参数:

global_conf = {'__file__': '/etc/nova/api-paste.ini', 'here': '/etc/nova'}
local_conf = {'/v2': 'openstack_compute_api_v21_legacy_v2_compatible', '/': 'oscomputeversions', '/v2.1': 'openstack_compute_api_v21'}

此处loader仍为之前创建的loader实例,我们在此只关注v2的api相关信息,看如下代码:

for path, app_name in local_conf.items():
    path = paste.urlmap.parse_path_expression(path)
    app = loader.get_app(app_name, global_conf=global_conf)
    urlmap[path] = app

当local_conf.items()循环到key=‘/v2’时,path经过paste.urlmap.parse_path_expression方法处理后仍然为
‘/v2’,app_name = ‘openstack_compute_api_v21_legacy_v2_compatible’
此时注意运行至app = loader.get_app(app_name, global_conf=global_conf)这行代码时,deploy内部代码逻辑会使用nova.api.auth.pipeline_factory_v21方法:

def pipeline_factory_v21(loader, global_conf, **local_conf):
"""A paste pipeline replica that keys off of auth_strategy."""
return _load_pipeline(loader, local_conf[CONF.auth_strategy].split())

再来看此时的参数:

global_conf = {'__file__': '/etc/nova/api-paste.ini', 'here': '/etc/nova'}
local_conf = {'keystone': 'cors compute_req_id faultwrap sizelimit authtoken keystonecontext legacy_v2_compatible osapi_compute_app_v21', 'noauth2': 'cors compute_req_id faultwrap sizelimit noauth2 legacy_v2_compatible osapi_compute_app_v21'}

这时,通过CONF.auth_strategy可以指定所使用的auth策略。在此我们选择简单的noauth2策略来向下进行,进入_load_pipeline方法:

def _load_pipeline(loader, pipeline):
filters = [loader.get_filter(n
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值