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)
gt: plugin_workers_thread.kill())
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):
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应用本质上做了以下事情:
客户端发送HTTP请求
服务器接收请求,然后生成一个HTML文档
服务器把HTML文档作为HTTP响应发送给客户端
客户端对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():
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的实现原理,并对其使用到相关技术进行了示例说明。