Openstack源代码分析之paste.deploy

Keystone服用中用到了PasteDeploy来部署WSGI应用,所有来分析一下。

一.paste deploy简介

官方文档把PasteDeploy介绍为一个发现并配置WSGI应用和服务的一个系统,通过loadapp(一个简单的函数)就可以部署WSGI,而且不许要知道WSGI应用的细节。

从一个PasteDeploy的配置文件(config.ini)入手分析

[composite:main]
use = egg:Paste#urlmap
/ = home
/blog = blog
/wiki = wiki
/cms = config:cms.ini

[app:home]
use = egg:Paste#static
document_root = %(here)s/htdocs

[filter-app:blog]
use = egg:Authentication#auth
next = blogapp
roles = admin
htpasswd = /home/me/users.htpasswd

[app:blogapp]
use = egg:BlogApp
database = sqlite:/home/me/blog.db

[app:wiki]
use = call:mywiki.main:application
database = sqlite:/home/me/wiki.db
一个一个分析,首先是composite,compsite部分意味着把请求分发到其他应用上,main应该是这个部分的标识(官方文档无解释,自己猜测)。 use = egg:Paste#urlmap意味着一种简单的映射方式,利用Paste包下的urlmap应用,把路径不同的请求进行映射。如后面紧跟的/,/blog,/wiki等路径。

特别要注意的是最后一个,/cms=config:cms.ini,说明如果路径是这样写的话,指向相同目录下的cms.ini,再根据里面的配置进行映射。

[composite:main]
use = egg:Paste#urlmap
/ = home
/blog = blog
/wiki = wiki
/cms = config:cms.ini
然后是app,标识为home,连用到了一种新的映射方式static,进行静态映射,需要配置document_root的参数,指向静态文件目录,这里要注意的一点书%(here)s,官方文档的说明是:You can use variable substitution, which will pul lvariables from the section [DEFAULT] (case sensitive!) with markers like %(var_name)s。大概意思是用[DEFAULT]下的配置变量var_name=xxxx代替了[app:home]下的%(var_name)s的值。像这个配置文件就需要在[DEFAULT]下配置here=xxx为静态。

[app:home]
use = egg:Paste#static
document_root = %(here)s/htdocs
然后,filter-app代表了对blog应用进行过滤,这里没有具体说明,但是use表明使用了Authenication包下的auth应用,next说明指向下一个应用,roles和htpasswd都是配置选项,一个key-value键值对。下面的blogapp指向了egg封装的BlogApp应用,并配置了数据库目录。

[filter-app:blog]
use = egg:Authentication#auth
next = blogapp
roles = admin
htpasswd = /home/me/users.htpasswd

[app:blogapp]
use = egg:BlogApp
database = sqlite:/home/me/blog.db
最后,和上面配置的唯一区别就是use选项配置不再是egg封装的应用,直接通过call命令调用mywiki,main模块下application应用。
[app:wiki]
use = call:mywiki.main:application
database = sqlite:/home/me/wiki.db
至此,第一个配置文件分析完毕。

二.Paste Deploy基本用法

如下所示通过loadapp调用config.ini文件,进行应用WSGI接口的配置。需要注意的是config:xxx后面跟的路径如果是绝对路径,只有在config.ini里面再配置路径,如上面的/cms,才是相对路径。

from paste.deploy import loadapp
wsgi_app = loadapp('config:/path/to/config.ini')
config文件格式如下

[section_name]
key = value
another key = a long value
    that extends over multiple lines
注释行,开头为#或者;

 app应用部署方式有一下四种:

[app:myapp]
use = config:another_config_file.ini#app_name

# or any URI:
[app:myotherapp]
use = egg:MyApp

# or a callable from a module:
[app:mythirdapp]
use = call:my.project:myapplication

# or even another section:
[app:mylastapp]
use = myotherapp

第一种指向同目录下另一个config,ini中的app应用

第二种指向egg分装的应用

第三种指向一个模块下的应用

第四中指向本config.ini下的另一个app

还有一种特别的app的应用部署方式

[app:myapp]
paste.app_factory = myapp.modulename:app_factory
paste.app_factory是一种协议格式,除了app_factory外还有composite_facory,fliter_factory, fliter_app_factory,server_factory,server_runner等。后面跟上myapp.modulename模块下的app_factroy类,不同协议其app_factory也不一样,后面会详细介绍如何定义每一个factories.


全局变量定义在[DEFAULT]下,但是可以修改全局变量,以如下方式

[DEFAULT]
admin_email = webmaster@example.com

[app:main]
use = ...
set admin_email = bob@example.com
这样就修修改了 admin_email


Composite Application,姑且成为复合应用吧,他本身不提供应用,而是通过类似上文url的方式提供映射,到不同的应用。


Filter Composition

有三种方法进行filter,第一种是fliter-with,第二种是filter-app,第三种是pipeline,多说一句,openstack用的最多的是pipeline。

第一种,通过fliter-with指向下一个fliter应用

[app:main]
use = egg:MyEgg
filter-with = printdebug

[filter:printdebug]
use = egg:Paste#printdebug
# and you could have another filter-with here, and so on...
第二种,通过next配置指向下一个filter应用
[filter-app:blog]
use = egg:Authentication#auth
next = blogapp
roles = admin
htpasswd = /home/me/users.htpasswd

[app:blogapp]
use = egg:BlogApp
database = sqlite:/home/me/blog.db
第三种,pipeline依次列出filter即可

[pipeline:main]
pipeline = filter1 egg:FilterEgg#filter2 filter3 app

[filter:filter1]
...


egg模式,通过setuptools打包python源文件,类似java jar的格式

setup(
    name='MyApp',
    ...
    entry_points={
        'paste.app_factory': [
            'main=myapp.mymodule:app_factory',
            'ob2=myapp.mymodule:ob_factory'],
        },
    )
通过上面配置安装egg,use=egg:MyApp#main或者use=egg:MyApp#ob2,可以分别找到myapp.mymodule下的app_factory对象和ob_factory对象

 三.Define Factories

app_factory

def app_factory(global_config, **local_conf):
    return wsgi_app
global_config传入的是字典参数,local_conf传入的是key-value参数

返回一个wsgi application


composite_factory

def composite_factory(loader, global_config, **local_conf):
    return wsgi_app
loader调用一些特殊函数, get_app(name_or_uri, global_conf=None) return a WSGI application with the given name. get_filter and get_server work the same way。如下使用方案:

def pipeline_factory(loader, global_config, pipeline):
    # space-separated list of filter and app names:
    pipeline = pipeline.split()
    filters = [loader.get_filter(n) for n in pipeline[:-1]]
    app = loader.get_app(pipeline[-1])
    filters.reverse() # apply in reverse order!
    for filter in filters:
        app = filter(app)
    return app
[composite:main]
use = <pipeline_factory_uri>
pipeline = egg:Paste#printdebug session myapp

[filter:session]
use = egg:Paste#session
store = memory

[app:myapp]
use = egg:MyApp
将数据读取传入,将filter printdebug,session读入并付给filter,读取app为pipeline参数的最后一个myapp,翻转filter先运行session(myapp),再运行egg:Paste#printdebug(myapp)。


fliter_factory

类似app_factroy但返回的是filter对象,而且仅仅接受WSGI application为唯一的参数,如下所示

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']

定义了AuthFilter对象实现请求变量'REMOTE_USER'下的变量和req_usernames是否一致,filter(app)调用唯一对象app,再里面再做处理并返回信息,通过__call__把类像函数一样调用。


fliter_app_factory

class AuthFilter(object):
    def __init__(self, app, global_conf, req_usernames):
        ....

除了参数包括app,返回对象为WSGI application外,其他不变。


server_factory

一个参数 wsgi_app,返回serve

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_runner

官方文档说不传wsgi_app,其他和server_factory一样,没给例子


bluefire

2013/10/28

13:27


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值