专栏
天翼云开发者社区

Neutron 的服务启动与插件加载

2024-03-15 09:13:33 31阅读

一、Neutron 代码架构

1. Neutron 接收到 RESTful API 的请求后,交给 WSGI Application 进行初步处理

2. WSGI Application 通过 Python API 调用 Neutron 的 Plugin 模块

3. Plugin 通过 RPC 调用 Neutron 的 Agent

4. Agent 再通过某种协议对 VNF 进行配置 

 

二、Neutron 的两类 API

Neutron-Server 首先是一个 Web Server,对外提供的 RESTful 接口,对 Neutron 内部来说是资源的 CRUD 的外在体现。

Neutron 将 API 分为两大类,Core Service API  和 Extension Service API

  • Core Service API:network,subnet,port,subnetpool 等资源的 API

  • Extension Service API:router,lbaas,ippool,snat 等资源的 API 

 

三、Neutron 服务启动

1. Neutron Server 启动入口

# neutron.egg-info/entry_points.txt
neutron-server = neutron.cmd.eventlet.server:main

 

2. Neutron Server 启动代码

# 加载 WSGI Application
def _run_wsgi(app_name):
    app = config.load_paste_app(app_name)
    return run_wsgi_app(app)

def run_wsgi_app(app):
    server = wsgi.Server("Neutron")
    server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host,
                 workers=_get_api_workers())
    return server

1. Web Server 的 host 和 port 在配置文件中

2. 参数 app 为 WSGI Application,是真正处理请求的实体

3. 参数 wokers 的数量决定了 Web Server 是另外启动线程,还是在当前线程以协程的形式启动 

_get_api_workers 返回的 api_workers 数量(neutron.conf 配置文件中),决定了采用 Web Server 的方式

  • api_workers <1:在当前进程启动 Web Server

  • api_workers >=1:在新进程启动 Web Server

 

3. WSGI Application加载 

etc/api-paste.ini
[composite:neutron]
use = egg:Paste#urlmap
/: neutronversions_composite
/v2.0: neutronapi_v2_0

[composite:neutronapi_v2_0]
use = call:neutron.auth:pipeline_factory
noauth = cors http_proxy_to_wsgi request_id catch_errors extensions neutronapiapp_v2_0
keystone = cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0

[filter:extensions]
paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory

[app:neutronapiapp_v2_0]
paste.app_factory = neutron.api.v2.router:APIRouter.factory

api-paste.ini 的每个 section 都为一个 WSGI Application 的构造函数

最外层的 WSGI Application 是 composite:neutron,根据 url 分配后续处理的 WSGI Application

  • “/”:neutronversions_composite 处理,查询 Neutron API 版本信息

  • “/v2.0”:neutronapi_v2_0 处理,资源的 RESTFUL API

重点看下 neutronapi_v2_0,其 neutron.auth:pipeline_factory 是一个 app factory,noauth 和 keystone 是其两个参数。

def pipeline_factory(loader, global_conf, **local_conf):
    """Create a paste pipeline based on the 'auth_strategy' config option."""
    pipeline = local_conf[cfg.CONF.auth_strategy]
    pipeline = pipeline.split()
    filters = [loader.get_filter(n) for n in pipeline[:-1]]
    app = loader.get_app(pipeline[-1])
    filters.reverse()
    for filter in filters:
        app = filter(app)
    return app

根据配置文件 neutron.conf 的 auth_strategy 选择是 noauth 还是 keystone,然后根据 section 名构建 pipline,处理请求。

pipline 构建过程:neutronapi_v2_0 外面一层一层加上 section 的构造工厂,构造工厂的 app 参数为 APIRouter,最终将 app_v2.0 外层一层一

层加上 filter,各个WSGI的关系如下:

 

四、Service plugin 的加载与请求处理

1. 插件加载

在第三节的 neutronapiapp_v2_0 的 APIRouter 构造函数中,实现 plugin 的加载,包括 Core Plugin 和 Service Plugin:

# neutron/api/v2/router.py
def APIRouter(**local_config):
    return pecan_app.v2_factory(None, **local_config)


def _factory(global_config, **local_config):
    return pecan_app.v2_factory(global_config, **local_config)

setattr(APIRouter, 'factory', _factory)

# neutron/pecan_wsgi/app.py
def v2_factory(global_config, **local_config):
    # ...
    startup.initialize_all()
    return app

# neutron/pecan_wsgi/startup.py
def initialize_all():
    manager.init()
    
# neutron/manager.py
def init():
    if not directory.is_loaded():
        NeutronManager.get_instance()

其中,在 NeutronManager 的构造函数中,_load_service_plugins 调用 _get_plugin_instance 实现对 Service Plugin 的具体加载过程:

class NeutronManager(object):
		# 核心插件加载
        plugin_provider = cfg.CONF.core_plugin
        plugin = self._get_plugin_instance(CORE_PLUGINS_NAMESPACE,
                                           plugin_provider)
        # directory 是单例
        directory.add_plugin(lib_const.CORE, plugin)
        msg = validate_post_plugin_load()
        self._load_services_from_core_plugin(plugin)
        # 服务插件加载
        self._load_service_plugins()

由于调用的代码比较复杂,总结为以下几点:

1. 根据neutron.conf 中配置 service plugins,加载对应的插件类

2. 加载 extension 目录下的类(拓展资源),将已加载的拓展资源的 alias(资源别称) 放入 extensions 集合

3. check_if_plugin_extensions_loaded 检查插件对应的拓展资源是否被加载(根据插件的support_alias与第二步的alias比较)

 

2. 路由映射

上节提到了拓展资源,即 Extension Service 类(继承 ExtensionDescriptor),其 get_resources 方法,调用 build_resource_info 函数构建了

资源的 HTTP Request 与 Plugin 的函数映射关系 。此外,根据 register_quota 参数,决定了资源是否有配额 。

def build_resource_info(plural_mappings, resource_map, which_service,
                        action_map=None, register_quota=False,
                        translate_name=False, allow_bulk=False):

    for collection_name in resource_map:
        if register_quota:
            resource_registry.register_resource_by_name(resource_name)
        member_actions = action_map.get(resource_name, {})
        controller = base.create_resource()
        resource = extensions.ResourceExtension()
        resources.append(resource)
    return resources

 

五、总结

1. API 请求最终到达 Plugin 之后,进行参数校验,再进行 Neutron 数据库操作

2. 调用 networking-odl 的driver,对控制器发起HTTP请求,同步数据

3. networking-odl 是作为 neutron 项目的插件,与控制器对接 

  • 4
  • 1
  • 1
1 评论
0/1000
王****际

写的真好

2024-03-18 08:28:29
0
回复
评论(1) 发表评论
王****际

写的真好

c****3

c****3

1 篇文章 1 粉丝
关注

Neutron 的服务启动与插件加载

2024-03-15 09:13:33 31阅读

一、Neutron 代码架构

1. Neutron 接收到 RESTful API 的请求后,交给 WSGI Application 进行初步处理

2. WSGI Application 通过 Python API 调用 Neutron 的 Plugin 模块

3. Plugin 通过 RPC 调用 Neutron 的 Agent

4. Agent 再通过某种协议对 VNF 进行配置 

 

二、Neutron 的两类 API

Neutron-Server 首先是一个 Web Server,对外提供的 RESTful 接口,对 Neutron 内部来说是资源的 CRUD 的外在体现。

Neutron 将 API 分为两大类,Core Service API  和 Extension Service API

  • Core Service API:network,subnet,port,subnetpool 等资源的 API

  • Extension Service API:router,lbaas,ippool,snat 等资源的 API 

 

三、Neutron 服务启动

1. Neutron Server 启动入口

# neutron.egg-info/entry_points.txt
neutron-server = neutron.cmd.eventlet.server:main

 

2. Neutron Server 启动代码

# 加载 WSGI Application
def _run_wsgi(app_name):
    app = config.load_paste_app(app_name)
    return run_wsgi_app(app)

def run_wsgi_app(app):
    server = wsgi.Server("Neutron")
    server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host,
                 workers=_get_api_workers())
    return server

1. Web Server 的 host 和 port 在配置文件中

2. 参数 app 为 WSGI Application,是真正处理请求的实体

3. 参数 wokers 的数量决定了 Web Server 是另外启动线程,还是在当前线程以协程的形式启动 

_get_api_workers 返回的 api_workers 数量(neutron.conf 配置文件中),决定了采用 Web Server 的方式

  • api_workers <1:在当前进程启动 Web Server

  • api_workers >=1:在新进程启动 Web Server

 

3. WSGI Application加载 

etc/api-paste.ini
[composite:neutron]
use = egg:Paste#urlmap
/: neutronversions_composite
/v2.0: neutronapi_v2_0

[composite:neutronapi_v2_0]
use = call:neutron.auth:pipeline_factory
noauth = cors http_proxy_to_wsgi request_id catch_errors extensions neutronapiapp_v2_0
keystone = cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0

[filter:extensions]
paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory

[app:neutronapiapp_v2_0]
paste.app_factory = neutron.api.v2.router:APIRouter.factory

api-paste.ini 的每个 section 都为一个 WSGI Application 的构造函数

最外层的 WSGI Application 是 composite:neutron,根据 url 分配后续处理的 WSGI Application

  • “/”:neutronversions_composite 处理,查询 Neutron API 版本信息

  • “/v2.0”:neutronapi_v2_0 处理,资源的 RESTFUL API

重点看下 neutronapi_v2_0,其 neutron.auth:pipeline_factory 是一个 app factory,noauth 和 keystone 是其两个参数。

def pipeline_factory(loader, global_conf, **local_conf):
    """Create a paste pipeline based on the 'auth_strategy' config option."""
    pipeline = local_conf[cfg.CONF.auth_strategy]
    pipeline = pipeline.split()
    filters = [loader.get_filter(n) for n in pipeline[:-1]]
    app = loader.get_app(pipeline[-1])
    filters.reverse()
    for filter in filters:
        app = filter(app)
    return app

根据配置文件 neutron.conf 的 auth_strategy 选择是 noauth 还是 keystone,然后根据 section 名构建 pipline,处理请求。

pipline 构建过程:neutronapi_v2_0 外面一层一层加上 section 的构造工厂,构造工厂的 app 参数为 APIRouter,最终将 app_v2.0 外层一层一

层加上 filter,各个WSGI的关系如下:

 

四、Service plugin 的加载与请求处理

1. 插件加载

在第三节的 neutronapiapp_v2_0 的 APIRouter 构造函数中,实现 plugin 的加载,包括 Core Plugin 和 Service Plugin:

# neutron/api/v2/router.py
def APIRouter(**local_config):
    return pecan_app.v2_factory(None, **local_config)


def _factory(global_config, **local_config):
    return pecan_app.v2_factory(global_config, **local_config)

setattr(APIRouter, 'factory', _factory)

# neutron/pecan_wsgi/app.py
def v2_factory(global_config, **local_config):
    # ...
    startup.initialize_all()
    return app

# neutron/pecan_wsgi/startup.py
def initialize_all():
    manager.init()
    
# neutron/manager.py
def init():
    if not directory.is_loaded():
        NeutronManager.get_instance()

其中,在 NeutronManager 的构造函数中,_load_service_plugins 调用 _get_plugin_instance 实现对 Service Plugin 的具体加载过程:

class NeutronManager(object):
		# 核心插件加载
        plugin_provider = cfg.CONF.core_plugin
        plugin = self._get_plugin_instance(CORE_PLUGINS_NAMESPACE,
                                           plugin_provider)
        # directory 是单例
        directory.add_plugin(lib_const.CORE, plugin)
        msg = validate_post_plugin_load()
        self._load_services_from_core_plugin(plugin)
        # 服务插件加载
        self._load_service_plugins()

由于调用的代码比较复杂,总结为以下几点:

1. 根据neutron.conf 中配置 service plugins,加载对应的插件类

2. 加载 extension 目录下的类(拓展资源),将已加载的拓展资源的 alias(资源别称) 放入 extensions 集合

3. check_if_plugin_extensions_loaded 检查插件对应的拓展资源是否被加载(根据插件的support_alias与第二步的alias比较)

 

2. 路由映射

上节提到了拓展资源,即 Extension Service 类(继承 ExtensionDescriptor),其 get_resources 方法,调用 build_resource_info 函数构建了

资源的 HTTP Request 与 Plugin 的函数映射关系 。此外,根据 register_quota 参数,决定了资源是否有配额 。

def build_resource_info(plural_mappings, resource_map, which_service,
                        action_map=None, register_quota=False,
                        translate_name=False, allow_bulk=False):

    for collection_name in resource_map:
        if register_quota:
            resource_registry.register_resource_by_name(resource_name)
        member_actions = action_map.get(resource_name, {})
        controller = base.create_resource()
        resource = extensions.ResourceExtension()
        resources.append(resource)
    return resources

 

五、总结

1. API 请求最终到达 Plugin 之后,进行参数校验,再进行 Neutron 数据库操作

2. 调用 networking-odl 的driver,对控制器发起HTTP请求,同步数据

3. networking-odl 是作为 neutron 项目的插件,与控制器对接 

文章来自专栏

Neutron代码分析

1 篇文章 1 订阅
1 评论
0/1000
王****际

写的真好

2024-03-18 08:28:29
0
回复
评论(1) 发表评论
王****际

写的真好

  • 4
    点赞
  • 1
    收藏
  • 1
    评论