Paste Deployment是一个针对wsgi开发的库,用来配置和加载wsgi application和server。openstack中配置都是通过api-paste.ini文件提供。通过这个文件就可以直接调用Paste Deployment代码来加载web server和上面的application。
from paste.deploy import loadapp
wsgi_app = loadapp('config:/path/to/config.ini')
有几个协议: paste.app_factory, paste.composite_factory, paste.filter_factory和 paste.server_factory。 它们都需要一个可调用的东西(比如函数、方法或类)。
#应用程序是最常见的。 定义app工厂如下所示:
def app_factory(global_config, **local_conf):
return wsgi_app
#global_config 是一个字典, local配置是作为keyword参数传递进来。此方法应返回一个 WSGI application.。
Composites只是稍微复杂一点 :
def composite_factory(loader, global_config, **local_conf):
return wsgi_app
#loader参数是一个对象,它有两个有趣的方法。 get_app(name_or_uri, global_conf=None) 返回给定名称的WSGI应用程序 。 get_filter 和 get_server 方法与前面的方法工作方式一样。
#下面是一个过滤器的示例,它检查远程用户CGI变量是否设置,从而创建一个非常简单的身份验证过滤器 :
def auth_filter_factory(global_conf, req_usernames):
# space-separated list of usernames:
req_usernames = req_usernames.split()
def filter(app):
return AuthFilter(app, req_usernames)
return filter
class AuthFilter(object):
def __init__(self, app, req_usernames):
self.app = app
self.req_usernames = req_usernames
def __call__(self, environ, start_response):
if environ.get('REMOTE_USER') in self.req_usernames:
return self.app(environ, start_response)
start_response(
'403 Forbidden', [('Content-type', 'text/html')])
return ['You are forbidden to view this resource']
#它和 paste.filter factory有点相似, 但它还需要接收wsgi应用程序参数,并返回一个wsgi应用程序。 所以上面的例子修改后如下:
class AuthFilter(object):
def __init__(self, app, global_conf, req_usernames):
# ...
#然后AuthFilter将在 filter_app_factory里提供服务 (在本例中,req_usernames是必需的本地配置键)。
#它接收与 applications和filters相同的签名,但返回一个server。
#server 是可调用的,它接收一个参数,即WSGI应用程序。 然后它为应用程序服务。 例子如下:
def server_factory(global_conf, host, port):
port = int(port)
def serve(app):
s = Server(app, host=host, port=port)
s.serve_forever()
return serve
#Server的实现留给了用户。
除了 wsgi_app 作为第一个参数传递, server应该立即运行外,其它的都与paste.server factory一样。
#url请求分发到对应的app,use表明的是分发的方式,这里是urlmap进行分发的
[composite:neutron]
use = egg:Paste#urlmap
/: neutronversions
/v2.0: neutronapi_v2_0
[composite:neutronapi_v2_0]
use = call:neutron.auth:pipeline_factory
noauth = request_id catch_errors extensions neutronapiapp_v2_0
keystone = request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0
[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory
[filter:catch_errors]
paste.filter_factory = oslo_middleware:CatchErrors.factory
[filter:keystonecontext]
paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
[filter:extensions]
paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory
[app:neutronversions]
paste.app_factory = neutron.api.versions:Versions.factory
#创建app
[app:neutronapiapp_v2_0]
#初始化neutron.api.v2.router中APIRouter.factory
paste.app_factory = neutron.api.v2.router:APIRouter.factory
api-paste.ini中filter是定义过滤器,app是定义类似于java中service业务,composite定义类似于controller,use = egg:Paste#urlmap意思是用urlmap进行分发,类似于暴露向外接口。
例:[composite:neutron]
use = egg:Paste#urlmap
/v2.0: neutronapi_v2_0
[composite:neutronapi_v2_0]
use = call:neutron.auth:pipeline_factory
noauth = request_id catch_errors extensions neutronapiapp_v2_0
keystone = request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0
打开neutron/auth.py:
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] #auth_strategy='keystone'
pipeline = pipeline.split()
filters = [loader.get_filter(n) for n in pipeline[:-1]] # filters就是上面keystone后面接的那一堆filters
app = loader.get_app(pipeline[-1]) #基于pecan web框架获取响应REST请求的application,获取的app类型为pecan.middleware.recursive.RecursiveMiddleware对象
filters.reverse() #把上述的filters进行逆序一下,即从最后一个filter开始操作
for filter in filters:
app = filter(app) #利用每一个filter封装一下app,返回最终封装的app
return app
其中pipeline_factory函数会根据neutron.conf中cfg.CONF.auth_strategy的值来加载pipline,因为这个参数的值是keystone,因此会加载keystone = cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0,这一堆filters.
[app:neutronapiapp_v2_0]
paste.app_factory = neutron.api.v2.router:APIRouter.factory
那么最后就是/v2.0会自动找到neutron.api.v2.router这个py文件中APIRouter类factory方法执行