三. nova api中的WSGI application
1. paste.deploy
官方文档:http://pythonpaste.org/deploy/
paste是python的一个WSGI工具包,在WSGI的基础上包装了几层,让应用管理和实现变得方便。
几个概念:
app:WSGI application,实现应用端,响应请求的方法,应当是一个callable object,在被call时传入参数为(env,start_response),app需要完成的任务是响应envrion中的请求,准备好响应头和消息体,然后交给start_response处理,并返回响应消息体。
filter:是一个callable object,其初始化时需要的唯一参数是(app),在被call时对env进行判断或者说过滤,当满足条件后call自身的app,否则直接链断,给出错误处理。
app_factory:app的工厂方法,参数(global_conf,**kwargs),其中global_conf对应配置文件中的[DEFAULT]section键值对,kwargs则对应自身setion键值对。该工程方法返回了app的一个实例。
filter_factory:filter的工厂方法,与app_factory比较需要注意的是,该方法返回的是filter类,实例化的过程在pipeline调用。
一个简单的例子:
##################### ## api-paste.ini ## ##################### [DEFAULT] #DEFAULT段,下面的键值对将作为global_conf author = zhengtianbao
blog = www.zhengtianbao.com
[composite:web] #web是一个符合wsgi的app,不过他是composite类型,下面的use方法将会调用 #Paste包中的urlmap对‘/home’,‘/blog’做初始化,同时在call的时候将会map #到不同的app use = egg:Paste #urlmap
/ home: home
#/blog: blog [pipeline:home] #pipeline管道,是一个callable对象,具体call的过程为: #faultwarp->logrequest->homeapp #是不是很像linux中的grep命令 #顺便一提home作为一个app的初始化过程顺序为: #faultwarp(logrequest(homeapp())) pipeline = faultwarp logrequest homeapp
[ filter :faultwarp]
#filter,直接调用factory方法,下面的key=value作为kwargs参数 #相当与: # import apipaste # apipaste.FaultWarp(app_from_paste, key=value) paste.filter_factory = apipaste:FaultWarp.factory
key = value
[ filter :logrequest]
paste.filter_factory = apipaste:RequestLogging.factory
[app:homeapp] #app,这里是app最终的实现 #相当与: # import apipaste # apipaste.HomeApp(version='1.3') #这里的键值对作为参数传入 paste.app_factory = apipaste:HomeApp.factory
version = 1.3
|
#!/usr/bin/env python #coding=utf8 """apipaste.py""" import os
import eventlet
from eventlet import wsgi
from paste import deploy
class HomeApp( object ):
"""home wsgi application"""
def __init__( self ):
print 'In HomeApp init'
def __call__( self , env, start_response):
print 'app:HomeApp is called'
start_response( '200 OK' , [( 'Content-Type' , 'text/plain' )])
return [ 'welcome to home page, hello world!' ]
@classmethod
def factory( cls , global_conf, * * kwargs):
print 'In HomeApp.factory'
#print 'global_conf: ', global_conf
#print 'kwargs: ', kwargs
return HomeApp() #返回的是HomeApp的一个实例化对象
class FaultWarp( object ):
"""fault warpper class"""
def __init__( self , app): #init时需要传入一个app对象
print 'In FaultWarp init'
self .app = app
def __call__( self , env, start_response):
#在这里可以对env进行一些判断过滤
print 'filter:FaultWarp is called'
return self .app(env, start_response)
@classmethod
def factory( cls , global_conf, * * kwargs):
print 'In FaultWarp factory'
#print 'global_conf: ', global_conf
#print 'kwargs: ', kwargs
return FaultWarp #返回的是FaultWarp类
class RequestLogging( object ):
"""request log class"""
def __init__( self , app):
print 'In RequestLogging init'
self .app = app
def __call__( self , env, start_response):
#在这里可以对请求进行一些日志操作
print 'filter:RequestLogging is called'
return self .app(env, start_response)
@classmethod
def factory( cls , global_conf, * * kwargs):
print 'In RequestLogging factory'
#print 'global_conf: ', global_conf
#print 'kwargs: ', kwargs
return RequestLogging
if __name__ = = '__main__' :
config_file = 'api-paste.ini'
app = 'web'
#app = 'homeapp'
wsgi_app = deploy.loadapp( "config:%s" % os.path.abspath(config_file), app)
wsgi.server(eventlet.listen(('', 8090 )), wsgi_app)
|
我们通过浏览器分别访问
http://localhost:8090/
http://localhost:8090/home/
http://localhost:8090/home/page/id/1
输出结果如下:
[root@localhost 20130801]# python apipaste.py In HomeApp.factory In HomeApp init In FaultWarp factory In RequestLogging factory In RequestLogging init In FaultWarp init (25629) wsgi starting up on http://0.0.0.0:8090/ (25629) accepted ('127.0.0.1', 59121) 127.0.0.1 - - [01/Aug/2013 22:15:48] "GET / HTTP/1.1" 404 448 0.011558 filter:FaultWarp is called filter:RequestLogging is called app:HomeApp is called 127.0.0.1 - - [01/Aug/2013 22:15:53] "GET /home/ HTTP/1.1" 200 160 0.000222 filter:FaultWarp is called filter:RequestLogging is called app:HomeApp is called 127.0.0.1 - - [01/Aug/2013 22:16:01] "GET /home/page/id/1 HTTP/1.1" 200 160 0.000364 |
发现URL中以/home/开头的地址都分发到了HomeApp,观察打印信息可以跟踪它调用pipeline的过程。
当然,在openstack中的app可不是这么简单的,下面就接着说说webob这个python库。
2. webob
官方文档:http://docs.webob.org/en/latest/
webob是一个Python库,主要是用在WSGI中对请求环境变量request environment(也就是WSGI应用中的参数environ)进行包装(提供wrapper),并提供了一个对象来方便的处理返回response消息。
nova中主要用到了webob中的装饰器,对HTTP请求信息req做了包装判断,对response的处理也做了包装。
基本用法:
#!/usr/bin/env python #coding=utf8 import eventlet
from eventlet import wsgi
import webob
import webob.dec
import webob.exc
def hello_world(env, start_response):
start_response( '200 OK' , [( 'Content-Type' , 'text/plain' )])
return [ 'Hello, World!\r\n' ]
@webob .dec.wsgify
def app(req):
"""
webobo.dec.wsgify可以将函数修饰成wsgi app
webob.exc可以生成HTTP的一些错误响应,如404,500等
经过修饰后的WSGI application可以返回以下几种response
#1: 字符串
res = 'hello world!\r\n'
#2: webob错误页信息
res = webob.exc.HTTPForbidden(detail='Error page')
#3: 另外一个wsgi app
res = hello_world
"""
#res = 'message\n'
#res = webob.exc.HTTPForbidden(detail='Error page')
#res = hello_world
#在这里可以对req进行操作
req.environ[ 'msg' ] = 'welcome to my blog:)'
#这样传递到下一个app的时候就能够做一些判断处理
res = app2
#可以打印查看req的信息
#print dir(req)
#print req
#print req.environ
return res
@webob .dec.wsgify
def app2(req):
if req.environ[ 'msg' ]:
print req.environ[ 'msg' ]
res = hello_world
return res
#wsgi.server(eventlet.listen(('', 8090)), hello_world) wsgi.server(eventlet.listen(('', 8090 )), app)
|
回过头来,看nova/wsgi.py文件,该文件是nova中wsgi相关的基类,其中定义了下面几个类:
Server –> WSGI server,wsgi api服务基类
Request –> app request, HTTP请求类,继承自webob.Request
Application –> WSGI application基类
Middleware –> WSGI app filter基类,继承自Application
Debug –> debug,继承自Middleware
Router –> WSGI 路由,用到了routes包
Loader –> paste.deploy加载接口类
貌似就剩下Router这个类了,下面就接着讲nova中所用到的routes。
3. routes
官方文档:http://routes.readthedocs.org/en/latest/index.html
Routes 可以方便的定义符合RESTful 标准的 web服务.map.resource可以快速创建出 增/改/删 路由接口.
这里只说明nova中所用到的:
import routes
import routes.middleware
import webob.dec
import webob.exc
mapper = routes.Mapper()
#需要对mapper指定controller,这个controller相当于是一个wsgi app,用来做URL路由call的app #1.connect方式创建一条route #mapper.connect(None, '/svrlist', controller=sc, action='list') #2.隐式的创建多条routes #mapper.resource('server', 'servers', controller=sc) @webob .dec.wsgify
def dispatch(req):
match = req.environ[ 'wsgiorg.routing_args' ][ 1 ]
if not match:
return webob.exc.HTTPNotFound()
app = match[ 'controller' ]
return app
router = routes.middleware.RoutesMiddleware(dispatch, mapper)
#router做为一个wsgi app被call的时候将会对请求URL先在mapper中匹配前面注册的URL route #将匹配到的信息(上面connect或者resource创建的routes)添加到req.environ中 #然后调用dispatch函数,对req.environ['wsgiorg.routing_args']进行判断, #例如id,controller等,然后分发到相应的wsgi app中 |
下面看从nova中的简化出来的例子:
import eventlet
from eventlet import wsgi
import routes
import routes.middleware
import webob
import webob.dec
import webob.exc
class Router( object ):
"""wsgi router"""
def __init__( self , mapper):
self . map = mapper
self ._router = routes.middleware.RoutesMiddleware(
self ._dispatch,
self . map )
@webob .dec.wsgify
def __call__( self , req):
return self ._router
@staticmethod
@webob .dec.wsgify
def _dispatch(req):
match = req.environ[ 'wsgiorg.routing_args' ][ 1 ]
print 'match: ' , match
#print 'body: ', req.body
if not match:
return webob.exc.HTTPNotFound()
app = match[ 'controller' ]
#简单的判断方法,nova中是放到了Resource类的call方法中进行判断的
if hasattr (app, match[ 'action' ]):
fun = getattr (app, match[ 'action' ])
return fun(req)
else :
return webob.exc.HTTPNotFound()
class APIRouter(Router):
""""""
def __init__( self ):
mapper = routes.Mapper()
self .resources = {}
self ._setup_routes(mapper)
super (APIRouter, self ).__init__(mapper)
def _setup_routes( self , mapper):
#注册routes,关于下面两个用法请看官方文档说明
self .resources[ 'versions' ] = VersionsApp()
self .resources[ 'servers' ] = ServersApp()
mapper.connect( "version" , "/version/" ,
controller = self .resources[ 'versions' ],
action = 'show' )
mapper.resource( "server" , "servers" ,
controller = self .resources[ 'servers' ],
collection = { 'detail' : 'GET' },
member = { 'action' : 'POST' })
class VersionsApp( object ):
"""wsgi application"""
def __init__( self ):
pass
@webob .dec.wsgify
def __call__( self , req):
return 'version called'
@webob .dec.wsgify
def show( self , req):
#对应GET /versions/
return 'in version show fun'
class ServersApp( object ):
"""wsgi application"""
def __init__( self ):
pass
def __call__( self , req):
return 'servers called'
@webob .dec.wsgify
def index( self , req):
#对应GET /servers
return 'welcome to servers app'
@webob .dec.wsgify
def action( self , req):
#对应POST /servers/{id}/action
#这里没有定义id的接口,nova中是在上层route的时候判断定义了接口才能传入id
#例如这个接口定义def action(self, req, id, body)
return 'server action function'
wsgi.server(eventlet.listen(('', 8090 )), APIRouter())
|
请试着发送以下RESTful请求:
GET http://localhost:8090/version/
GET http://localhost:8090/servers
POST http://localhost:8090/servers/2/action Body={‘msg’:’hello world’}
观察返回结果是不是预料的那样.
关于mapper.resource方法的一点说明:
例如创建下面的resource:
map.resource(“message”, “messages”)
则对应为:
GET /messages => messages.index() => url(“messages”)
POST /messages => messages.create() => url(“messages”)
GET /messages/new => messages.new() => url(“new_message”)
PUT /messages/1 => messages.update(id) => url(“message”, id=1)
DELETE /messages/1 => messages.delete(id) => url(“message”, id=1)
GET /messages/1 => messages.show(id) => url(“message”, id=1)
GET /messages/1/edit => messages.edit(id) => url(“edit_message”, id=1)
map.resource(“message”, “messages”, collection={“rss”: “GET”})
GET /messages/rss => messages.rss()
map.resource(“message”, “messages”, member={“mark”:”POST”})
POST /messages/1/mark” => Messages.mark(1)
总结下访问顺序:RESTful请求 -> RoutesMiddleware进行mapper -> wsgi application
大体上nova也是这个流程,不过nova中对Mapper的connect以及resource进行了重写,在router时加了更多的判断,而且最终的wsgi app是包装成Resource(Controller())这种形式的.
相关推荐
在进行OpenStack Nova源码分析时,我们需要深入了解其架构、核心模块以及关键流程。 首先,Nova的整体架构基于服务模型,包括以下主要服务: 1. **nova-api**:API服务,对外提供RESTful API接口,用于与其他...
### OpenStack Nova源码分析之基础环境配置:Eclipse + PyDev #### 一、基础知识与背景介绍 OpenStack是一个开源的云计算管理平台项目,它提供了API和一系列服务来管理和控制大型计算集群中的虚拟服务器资源。Nova...
### OpenStack源码分析—NOVA模块 #### 一、OpenStack与NOVA概述 OpenStack作为一款开源的云计算管理平台,提供了丰富的功能和服务来帮助用户构建和管理云基础设施。在其众多服务中,Nova(计算服务)是核心组成...
在深入Nova源码之前,我们首先需要理解其基本架构。Nova由多个服务组成,如nova-api、nova-scheduler、nova-compute等,它们协同工作以实现完整的计算功能: 1. **nova-api**:这是Nova的前端接口,处理HTTP请求并...
源码分析: 1. **Nova Compute**:主要负责虚拟机实例的生命周期管理,包括启动、停止、迁移等操作。它与底层的虚拟化技术(如KVM、Xen、VMware等)交互,实现虚拟机的创建和管理。通过`nova/compute`目录下的代码...
nova-api发布api服务没有用到一个些框架,基本都是从头写的。在不了解它时,以为它非常复杂,难以掌握。花了两三天的时间把它分析一遍后,发现它本身的结构比较简单,主要难点在于对它所使用的一些类库不了解,如...
源码分析是深入理解OpenStack Nova工作原理的重要途径。下面将对OpenStack Nova的源码进行详细的解析。 1. **Nova架构概述** Nova由多个服务组成,如nova-api、nova-scheduler、nova-compute等,它们通过AMQP...
通过对Nova及其核心组件`nova-compute`的源码分析,我们可以更深入地理解OpenStack的工作原理。OpenStack的设计思想和实现机制对于构建高效、可靠的云基础设施具有重要意义。通过不断优化Nova的内部结构和服务流程,...
总的来说,OpenStack Nova源码分析是一次深入了解云计算基础设施管理机制的旅程,它将带你走过从用户请求到实际资源分配的每一步,帮助你在OpenStack的世界中游刃有余。对于想要投身云服务开发、运维和优化的人来说...
OpenStack Nova是OpenStack云平台的核心组件之一,主要负责计算资源的管理,包括虚拟机的生命周期管理、调度、网络配置以及存储操作等。"openstack-nova-folsom-2-348-g9468508.zip"这个压缩包文件很可能是Nova在...
Nova是OpenStack的核心服务之一,负责虚拟机实例的生命周期管理,包括创建、调度、启动、停止、迁移等操作。 **描述** "openstack超级源码" 提示我们要深入源代码层面理解OpenStack的工作原理。源代码分析可以帮助...
- **源码结构**:Nova源码遵循Python项目标准,使用setuptools进行管理。理解模块间的关系有助于深入源码。 - **部署环境**:为了便于源码调试,需要搭建一个“all-in-one”的OpenStack开发环境,如RDO的Packstack ...
《OpenStack Nova G3源码解析》 OpenStack Nova是OpenStack云计算平台的核心组件之一,主要负责计算资源的管理,包括...无论是对OpenStack的整体架构,还是对具体功能的实现,源码分析都是提升技能不可或缺的一部分。
###nova源码架构介绍 源码的获取 开发环境的搭建 nova模块调用介绍 nova源码模块功能介绍 ###添加Nova-api自定义模块 ###数据库表结构的扩展 nova表结构的扩展 keystone表结构的扩展 resetful接口服务的扩展 ###...
Horizon的源码分析: 1. **模块结构**: Horizon源码被组织成多个模块,每个模块对应OpenStack服务的一个特定部分,例如Nova(计算)、Swift(对象存储)或Neutron(网络)。这些模块包含视图、模板、模型和测试,...
6. **源码分析**:项目可能包含对OpenStack源码的阅读和理解,有助于深入学习OpenStack的工作原理,优化云环境性能,或者根据需求进行定制开发。 7. **云服务管理**:了解如何通过OpenStack提供的命令行工具或...
通过阅读和分析Horizon的源码,我们可以了解到它如何与OpenStack的其他服务如Nova、Glance、Neutron等进行通信,以及它是如何处理用户请求并呈现结果的。 源码解析: 1. **项目结构**: Horizon源码主要分布在`...
OpenStack是一个开放源码的云计算平台,它提供了多种云服务,包括计算、网络、存储等,而Swift是其专门针对对象存储设计的服务。 8.1 OpenStack背景介绍:OpenStack起源于NASA和Rackspace的合作项目,旨在打造一个...