neutron-server源码分析总纲

背景:本文以neutron的12.0.0版本为例进行源码分析。

neutron-server作为Neutron的控制中心,主要对Neutron四大类资源进行管理。从实现架构上可以粗略分为两大模块:neutron-api和RPC。这么分类的依据来自于neutron-server的启动方法,其资源的初始化基本都是在start_api_and_rpc_workers这个方法中完成的。

该方法的源码如下所示:

def start_api_and_rpc_workers(neutron_api):
    try:
        worker_launcher = service.start_all_workers()
        pool = eventlet.GreenPool()
        api_thread = pool.spawn(neutron_api.wait)
        plugin_workers_thread = pool.spawn(worker_launcher.wait)
        api_thread.link(lambda gt: plugin_workers_thread.kill())
        plugin_workers_thread.link(lambda gt: api_thread.kill())
        pool.waitall()
    except NotImplementedError:
        neutron_api.wait()

为了简洁,删除了日志等内容。在这个方法中生成了两个线程,分别是api_thread和plugin_workers_thread。api_thread与neutron-api线程(在调用方法中已创建)相关联,即neutron-server的wsgi部分。这部分内容也是接下来需要讲解的重点;而plugin_workers_thread通过名称即可猜测出它必定与plugin插件相关,事实上它主要用于RPC服务。如果这两部分内容放在一篇博文进行讲解,不仅不利于阅读,也不利于理解整个Neutron(因为包含的信息太多),所以关于RPC的内容将会放到下一章节进行讲解。

一 、neutron-server是什么

弄清楚这个问题,我们才能明白neutron-server为什么要如此设计。

Neutron是OpenStack项目中负责提供网络服务的组件,它基于软件定义网络的思想,实现了网络虚拟化下的资源管理。Neutron 的设计目标是实现“网络即服务(Networking as a Service)”,在设计上遵循了基于 SDN 实现网络虚拟化的原则,在实现上充分利用了 Linux 系统上的各种网络相关的技术。

  • Neutron功能

    • 二层交换:支持多种虚拟交换机,一般使用Linux Bridge和Open vSwitch实现。

    • 三层路由:利用Linux协议栈实现

    • 负载均衡:通过HAProxy实现

    • 防火墙:使用iptables实现

    • VPN 等等

  • 网络类型

    • Local:本地网络,主要用于测试

    • Flat: Falt网络,只有一个广播域

    • VLAN:可以跨节点,私有云使用较多

    • VXLAN: 大二层网络,能够克服vlan和物理网络基础设施的限制,公有云使用较多。

    • GRE: 与VXLAN类似

  • Neutron架构

     

 

其中,Neutron Server对外提供OpenStack网络API,接收请求,并调用plugin处理请求;plugin处理Neutron Server发来的请求,维护OpenStack逻辑网络的状态,并调用Agent处理请求;Agent处理Plugin请求,负责在Network provider上实现各种网络功能;Network provider提供网络服务的虚拟或者物理网络设备;MQ用于组件之间的通信和RPC调用;database存放OpenStack的网络状态信息,包括Network,Subnet,Port,Router等。

二、neutron-api

在eventlet_wsgi_server方法中,service.serve_wsgi以wsgi的方式启动了service.NeutronApiService。service.NeutronApiService继承自WsgiService类。WsgiService类是一个普通的Python类。它有两个公共方法start和wait,如下所示:

class WsgiService(object):
    def __init__(self, app_name):
        self.app_name = app_name
        self.wsgi_app = None
​
    def start(self):
        self.wsgi_app = _run_wsgi(self.app_name)
​
    def wait(self):
        self.wsgi_app.wait()

该类唯一参数是app_name,默认值是'neutron',该值来自其子类NeutronApiService的create方法。在start方法中调用了_run_wsgi方法,然后返回一个wsgi类型的app对象。 _run_wsgi方法如下所示:

def _run_wsgi(app_name):
    app = config.load_paste_app(app_name)
    if not app:
        LOG.error('No known API applications configured.')
        return
    return run_wsgi_app(app)

load_paste_app方法是一个封装方法,最终会调用下面两个方法,并返回一个符合WSGI规范的application,实际上就是一个叫作“neutron”的对象(定义于/etc/neutron/api-paste.ini文件,这部分内容属于pecan应用,后面会对其用法进行详细说明,请不要惊慌)

loader = wsgi.Loader(cfg.CONF)
app = loader.load_app(app_name)

一句话概括,load_paste_app方法根据字符串"neutron",生成了一个符合WSGI规范的application对象,而这个对象将被neutron-server用于对外提供服务。用MVC模型进行类比,application就是通常所说的MVC模型中的controller对象。

仅仅有了application还不行,必须把application加载到server容器,其才能在server容器的帮助之下对外提供HTTP服务。Tomcat、Jetty、WebLogic等中间件都是满足servlet规范的servlet容器。Java Web应用必须部署在这类容器中才能真正运行起来。Python的WSGI规范与之大同小异,大家可互相印证加深理解。

我们已经得到了满足WSGI规范的application对象,那么接下来必然是把该对象加载到满足WSGI规范的server容器中。

继续跟踪调用链:

def run_wsgi_app(app):
    server = wsgi.Server("Neutron")
    server.start(app, cfg.CONF.bind_port, cfg.CONF.bind_host,
                 workers=_get_api_workers())
    LOG.info("Neutron service started, listening on %(host)s:%(port)s",
             {'host': cfg.CONF.bind_host, 'port': cfg.CONF.bind_port})
    return server

wsgi.Server.start方法最终调用_run方法加载application到eventlet.wsgi.server(这是一个满足WSGI规范的服务器)中。

def _run(self, application, socket):
    eventlet.wsgi.server(socket, application,
                             max_size=self.num_threads,
                             log=LOG,
                             keepalive=CONF.wsgi_keep_alive,
                             log_format=CONF.wsgi_log_format,
                             socket_timeout=self.client_socket_timeout)

至此,neutron-api的调用链分析宣布结束。

 

三、application生成

当前版本的neutron-server使用pecan生产neutronapiapp_v2_0(一个符合wsgi的application)。生成该application的工厂方法是neutron.pecan_wsgi.v2_factory,详情如下:

def v2_factory(global_config, **local_config):
    app_hooks = [
        hooks.UserFilterHook(),  # priority 90
        hooks.ContextHook(),  # priority 95
        hooks.ExceptionTranslationHook(),  # priority 100
        hooks.BodyValidationHook(),  # priority 120
        hooks.OwnershipValidationHook(),  # priority 125
        hooks.QuotaEnforcementHook(),  # priority 130
        hooks.NotifierHook(),  # priority 135
        hooks.QueryParametersHook(),  # priority 139
        hooks.PolicyHook(),  # priority 140
    ]
    app = pecan.make_app(root.V2Controller(),
                         debug=False,
                         force_canonical=False,
                         hooks=app_hooks,
                         guess_content_type_from_ext=True)
    startup.initialize_all()
    return app

pecan.make_app可以生成了一个application,其在配置文件api-paste.ini中的别名是neutronapiapp_v2_0,最终该对象会在下面的调用链中被调用。

cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0

 

四、Python Web规范WSGI

WSGI(Web Server Gateway Interface)是一个规范,定义了Web服务器与Python应用程序之间进行交互的标准接口,使得Python Web应用可以和Web服务器对接,以提高Web应用在不同Web服务器间的移植性。

WSGI Web服务器又有哪些作用呢?

世界上所有的Web应用本质上做了以下事情:

  1. 客户端发送HTTP请求

  2. 服务器接收请求,然后生成一个HTML文档

  3. 服务器把HTML文档作为HTTP响应发送给客户端

  4. 客户端对HTML进行解析展示

上述2和3是在服务器端完成的。如果没有Web服务器,那么每个HTTP请求,应用程序都需要自己解析HTTP协议或者动态生成HTML文档,处理各种不同的网络异常或应用异常。在计算机的世界,这种重复操作的事情显然是会被一种中间应用所替代--Web服务器。

WSGI接口规范定义很简单,只要求Web应用是可调用对象,可以是一个函数,也可以是一个实现了 call方法的实例或者是一个类。同时,WSGI还规定可调用对象可以接收两个参数,并且可调用对象需要返回一个可迭代的值。

  • 可调用对象是一个函数

def application(env,start_response):
    pass
  • 可调用对象是一个实例

class Application():
    def __init__(self,env,start_response):
        pass
  • 可调用对象是一个类

class Application():
    def __call__(self,env,start_response):
        pass

下面以可调用对象是一个函数为例,实现一个简单的Web应用:

import eventlet
import eventlet.wsgi
​
def application(evn,start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return '<h1>app</h1>'
eventlet.wsgi.server(eventlet.listen(('',8080)),application)

简简单单6行代码,就实现了一个返回字符串“app”的符合WSGI规范的Web应用。

五、使用neutron-server的方式实现一个Web服务

neutron-server的Web容器使用的是eventlet.wsgi.server,使用pecan生成WSGI application。

import pecan
import eventlet.wsgi
import eventlet
import time
from pecan import expose
from pecan.hooks import PecanHook
​
class RootController():
    @expose()
    def index(self):
        return str(time.time())
        
class UserHook(PecanHook):
    priority = 91
    def before(self, state):
        print "user hook before"
​
    def after(self, state):
        print "user hook after"
        
def setup_app():
    app_hooks = [UserHook()]
    app = pecan.make_app(RootController(),hooks=app_hooks)
    return app
​
def run():
    eventlet.wsgi.server(eventlet.listen(('', 8080)), setup_app(), keepalive=False, socket_timeout=30)
​
if __name__ == '__main__':
    run()

上面讲解了很多关于neutron-api初始化过程的调用链,其实精简到最后大概类似上述代码。

小结

neutron-server是一个Web Server,对外提供Service API。通过这些API,可以对Neutron管理的network、subnet、port等资源进行增删改查。同时,neutron会在背后通过plugin的方式把这些逻辑资源映射到物理世界。本文只分析了neutron-api的实现原理,并对其使用到相关技术进行了示例说明。

如果对云计算感兴趣,可以关注我的微信公众号:

 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

half-beast

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值