OpenStack Glance 之paste

Python paste 是WSGI (web server gateway interface)的一个工具库,Openstack的每个项目基本都用到了该库,本文以Glance在paste上的使用为例来介绍paste。WSGI是web服务与应用之间交互的一种规范,它定义了应用、服务、中间件的概念。
分析过程中用到的glance的配置文件glanc-api-paste.ini,其中内容较多,主要分析如下配置:

[pipeline:glance-api-keystone]
pipeline = cors healthcheck http_proxy_to_wsgi versionnegotiation osprofiler authtoken context  rootapp[composite:rootapp]
paste.composite_factory = glance.api:root_app_factory
/: apiversions
/v1: apiv1app
/v2: apiv2app[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
delay_auth_decision = true

glance-api 服务的入口函数:

def main():。。。server = wsgi.Server(initialize_glance_store=True)#加载glance-api的appserver.start(config.load_paste_app('glance-api'), default_port=9292)server.wait()except KNOWN_EXCEPTIONS as e:fail(e)if __name__ == '__main__':main()

上面这段代码是glance-api服务端启动的部分代码,服务监听在9292上,app是由config.load_paste_app(‘glance-api’)加载。详细过程如下:

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

api 名称经过修改:

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

paste.deploy.loadapp(“config:/etc/glance/glance-api-paste.ini”, glance-api-keystone)
调用栈:

paste.deploy.loadapp(uri, name=None, **kw)>paste.deploy.loadobj(APP, uri, name=name, **kw)
paste.deploy.loadobj(object_type, uri, name=None, relative_to=None, global_conf=None)def loadobj(object_type, uri, name=None, relative_to=None,global_conf=None):#load context context = loadcontext(object_type, uri, name=name, relative_to=relative_to,global_conf=global_conf)return context.create()

object_type共有六种:APP(_App) FILTER(_Filter) SERVER(_Server) PIPELINE(_Pipeline) FILTER_APP(_FilterApp) FILTER_WITH(_FilterWith),本文会涉及到APP、FILTER、PIPELINE。

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

#object_type=APP, uri=config:/etc/glance/glance-api-paste.ini name=glance-api-keystone
def loadcontext(object_type, uri, name=None, relative_to=None,global_conf=None):if '#' in uri:if name is None:uri, name = uri.split('#', 1)else:# @@: Ignore fragment or error?uri = uri.split('#', 1)[0]if name is None:name = 'main'if ':' not in uri:raise LookupError("URI has no scheme: %r" % uri)#scheme=config path=/etc/glance/glance-api-paste.inischeme, path = uri.split(':', 1)scheme = scheme.lower()if scheme not in _loaders:raise LookupError("URI scheme not known: %r (from %s)"
#_loaders={'config': _loadconfig, 'egg': _loadegg, 'call': _loadfunc}
#_loader[scheme] == _loadconfigreturn _loaders[scheme](object_type,uri, path, name=name, relative_to=relative_to,global_conf=global_conf)

字典 _loaders={‘config’: _loadconfig, ‘egg’: _loadegg, ‘call’: _loadfunc}由下面部分代码片段可知。

...
_loaders['config'] = _loadconfig
_loaders['egg'] = _loadegg
_loaders['call'] = _loadfunc
...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

_loadconfig(…)详细加载过程

#object_type=APP uri=config:/etc/glance/glance-api-paste.ini path=/etc/glance/glance-api-paste.ini
def _loadconfig(object_type, uri, path, name, relative_to,global_conf):...loader = ConfigLoader(path)...return loader.get_context(object_type, name, global_conf)

ConfigLoader.get_context(…)主要过程:

#object_type=APP name=glance-api-keystone
def get_context(self, object_type, name=None, global_conf=None):
...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

  ...#1.获取glance-api-keystone在配置文件中对应的section:pipeline:glance-api-keystonesection = self.find_config_section(object_type, name=name)...local_conf = {}...#2.解析section pipeline:glance-api-keystone下的配置项:并保持到local_conf字典中。for option in self.parser.options(section):if option.startswith('set '):name = option[4:].strip()global_additions[name] = global_conf[name] = (self.parser.get(section, option))elif option.startswith('get '):name = option[4:].strip()get_from_globals[name] = self.parser.get(section, option)else:if option in defaults:# @@: It's a global option (?), so skip itcontinuelocal_conf[option] = self.parser.get(section, option)...if section.startswith('filter-app:'):context = self._filter_app_context(object_type, section, name=name,global_conf=global_conf, local_conf=local_conf,global_additions=global_additions)#3.为pipeline创建LoaderContext对象。elif section.startswith('pipeline:'):context = self._pipeline_app_context(object_type, section, name=name,global_conf=global_conf, local_conf=local_conf,global_additions=global_additions)elif 'use' in local_conf:context = self._context_from_use(object_type, local_conf, global_conf, global_additions,section)else:context = self._context_from_explicit(object_type, local_conf, global_conf, global_additions,section)...#4.返回pipeline LoaderContext对象。return context
...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

 ...#1.获取pipelinepipeline = local_conf.pop('pipeline').split()...context = LoaderContext(None, PIPELINE, None, global_conf,local_conf, self)#2为app创建LoaderContext对象context.app_context = self.get_context(APP, pipeline[-1], global_conf)#3.为filter创建LoaderContext对象context.filter_contexts = [self.get_context(FILTER, name, global_conf)for name in pipeline[:-1]]return context

从上面的源代码中可以看出,主要做了,key pipeline的value,创建PIPELINE的context对象,创建APP的context对象和FILTER对象。

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

FILTER Context对象的创建,主要是对实例对象的内部属性的赋值操作:

def __init__(self, obj, object_type, protocol,global_conf, local_conf, loader,distribution=None, entry_point_name=None):self.object = obj self.object_type = object_typeself.protocol = protocol#assert protocol in _flatten(object_type.egg_protocols), (#    "Bad protocol %r; should be one of %s"#    % (protocol, ', '.join(map(repr, _flatten(object_type.egg_protocols)))))self.global_conf = global_confself.local_conf = local_confself.loader = loaderself.distribution = distributionself.entry_point_name = entry_point_name

APP Context对象的创建:

        context.app_context = self.get_context(APP, pipeline[-1], global_conf)

这里又调用了get_context(…)方法,该方法前文已经介绍,这里不再赘述。该方法根据传入参数的不同,相应的也会走不同的逻辑,APP Context的get_context过程主要如下:
1)寻找app对应的section

        section = self.find_config_section(object_type, name=name)

2)把app section下的配置项保存到local_conf字典中

        for option in self.parser.options(section):if option.startswith('set '):name = option[4:].strip()global_additions[name] = global_conf[name] = (self.parser.get(section, option))elif option.startswith('get '):name = option[4:].strip()get_from_globals[name] = self.parser.get(section, option)else:if option in defaults:# @@: It's a global option (?), so skip itcontinuelocal_conf[option] = self.parser.get(section, option)

3)调用_context_from_explicit(…)方法创建context对象。

            context = self._context_from_explicit(object_type, local_conf, global_conf, global_additions,section)

_context_from_explicit(…)的源代码如下:

    def _context_from_explicit(self, object_type, local_conf, global_conf,global_addition, section):possible = []#查找local_conf中被支持的协议,找到后,把该协议与该协议所对应的value构建成一个元组放入possible列表中for protocol_options in object_type.egg_protocols:for protocol in protocol_options:if protocol in local_conf:possible.append((protocol, local_conf[protocol]))break#检查possible变量if len(possible) > 1:raise LookupError("Multiple protocols given in section %r: %s"% (section, possible))if not possible:raise LookupError("No loader given in section %r" % section)found_protocol, found_expr = possible[0]del local_conf[found_protocol]#导入协议说对应的value,以为value是python的一个模块、方法或对象,所以可以之间导入。value = import_string(found_expr)#实例化context对象context = LoaderContext(value, object_type, found_protocol,global_conf, local_conf, self)return context

该函数的主要逻辑是:
1)查找local_conf中被支持的协议,找到后,把该协议与该协议所对应的value构建成一个元组放入possible列表中

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

该函数的主要逻辑是:
1)查找local_conf中被支持的协议,找到后,把该协议与该协议所对应的value构建成一个元组放入possible列表中
2)对possible做检查。
3)导入被支持协议对应的value,该value是python支持的类型,本文是glance.api:root_app_factory、
keystonemiddleware.auth_token:filter_factory,即是factory方法。
4)实例化context对象,LoaderContext对象的实例化上文有介绍,不再赘述。
FILTER Context的创建与APP Context的创建过程类似,区别是filter模块有多个需要一个循环,源代码如下:

        context.filter_contexts = [self.get_context(FILTER, name, global_conf)for name in pipeline[:-1]]

获取context的具体过程,与app context是相似的,这里就不再赘述。

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

获取context的具体过程,与app context是相似的,这里就不再赘述。
从上文的loadobj(…)的方法可以知道context获取之后就开始调用context.create(…)方法:
context类型是LoaderContext,该类的create的方法如下:

    def create(self):return self.object_type.invoke(self)

有源码可知,create方法是调用了LoaderContext属性object_type的invoke(…)方法。
有上文可知object_type的类型有APP(_App) FILTER(_Filter) SERVER(_Server) PIPELINE(_Pipeline) FILTER_APP(_FilterApp) FILTER_WITH(_FilterWith)这六种。这里主要会分析PIPELINE、APP、FILTER。

PIPELIEN.invoke(…)

    def invoke(self, context):#创建appapp = context.app_context.create()#创建filterfilters = [c.create() for c in context.filter_contexts]#将filters列表反转filters.reverse()#filter封装appfor filter in filters:app = filter(app)return app

由上面的源码可知:
1)PIPELINE的invoke方法先调用了APP 的create方法,并把返回值赋值给app。
2)常见filter实例,并将filter顺序反转。
3)通过filter来封装app,并返回。

APP.invoke(…)

    def invoke(self, context):if context.protocol in ('paste.composit_factory','paste.composite_factory'):return fix_call(context.object,context.loader, context.global_conf,**context.local_conf)elif context.protocol == 'paste.app_factory':return fix_call(context.object, context.global_conf, **context.local_conf)else:assert 0, "Protocol %r unknown" % context.protocol

FILTER.invoke(…)

    def invoke(self, context):if context.protocol == 'paste.filter_factory':return fix_call(context.object,context.global_conf, **context.local_conf)elif context.protocol == 'paste.filter_app_factory':def filter_wrapper(wsgi_app):# This should be an object, so it has a nicer __repr__return fix_call(context.object,wsgi_app, context.global_conf,**context.local_conf)return filter_wrapperelse:assert 0, "Protocol %r unknown" % context.protocol

有源码可知APP和FILTER的invoke方法都调用了fix_call(…)方法。该方法位于/usr/lib/python2.7/site-packages/paste/deploy/util.py 源码如下:

def fix_call(callable, *args, **kw):"""Call ``callable(*args, **kw)`` fixing any type errors that come out."""try:val = callable(*args, **kw)except TypeError:exc_info = fix_type_error(None, callable, args, kw)reraise(*exc_info)return val

有源码可知,fix_call是调用了之前导入的factory方法。以keystonemiddleware.auth_token:filter_factory为例,来分析filter:

def filter_factory(global_conf, **local_conf):"""Returns a WSGI filter app for use with paste.deploy."""conf = global_conf.copy()conf.update(local_conf)def auth_filter(app):return AuthProtocol(app, conf)return auth_filter

从上文源码可知,filter_factory复制了一份global_conf的值然后返回一个内部函数实例auth_filter。如果调用该返回的实例,者就会使用参入的参数app,和conf为参数创建一个AuthProtocol实例。
代码分析到这里,我想大家都该清楚了,paste的loadapp的基本过程,即filter列表中的filter,都以倒序的方式对最后一个app进行封装。

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

Pipeline模型及app的加载原型
通过上面源代码的分析,建立以下模型:

 [pipeline:xxx]pipeline = filter0 filter2 ... filtern app[app:yyy]paste.app_factory = aaa.bbb.ccc:app_factory
app = loadapp("config:$PATH/zzz.ini", "xxx") ->filter0(filter2(...filtern(app)))

从app的加载过程可以看出在pipeline链上,前面的filter对邻近后面的filter进行封装,最后一个filter对app进行封装。
那么其具体是这么封装的呢?这里以keystonemiddleware.auth_token:filter_factory 对app的封装为例来介绍其封装过程,从上面的源码分析,和app的加载原型课可以看出,filter_factory返回一个auth_filter可执行函数,然后auth_filter被调用,使用app和conf作为参数,创建app被封装后的对象AuthProtocol对象,所以封装就是使用app作为参数来实例化AuthProtocol filter app实例对象。
AuthProtocol实例化过程:

#AuthProtocol(BaseAuthProtocol)def __init__(self, app, conf):...self._conf = _conf_values_type_convert(conf)...super(AuthProtocol, self).__init__(app,log=log,enforce_token_bind=self._conf_get('enforce_token_bind'))...
#BaseAuthProtocoldef __init__(self,app,log=_LOG,enforce_token_bind=_BIND_MODE.PERMISSIVE):self.log = logself._app = appself._enforce_token_bind = enforce_token_bind

由源码可以知道filter封装的过程就是把参数参入的app赋值给当前实例的_app属性。

既然filter 是wsgi app那么它也一定要满足wsgi的app标准,即:返回的可调用对象 filter app必须是app(environ,start_response)满足这样的调用方式。然而在openstack的一些子项目中除了compsite和app外,filter都没有满足这个要求,那他又是为什么呢?仔细看到的同学会看到每个filter的__call__(…)方法都用了装饰器来修饰,如:

    @webob.dec.wsgify(RequestClass=_request._AuthTokenRequest)def __call__(self, req):"""Handle incoming request."""response = self.process_request(req)if response:return responseresponse = req.get_response(self._app)return self.process_response(response)

所以可以断定一定是这个装饰器在其中起了作用。那么这个装饰器做了什么呢,让原本的environ和start_response变成了req。进入装饰器中看一下就一目了然了,如下是装饰器的__init__方法:

#注:RequestClass = webob.Requestdef __init__(self, func=None, RequestClass=None,args=(), kwargs=None, middleware_wraps=None):self.func = funcif (RequestClass is not Noneand RequestClass is not self.RequestClass):self.RequestClass = RequestClassself.args = tuple(args)if kwargs is None:kwargs = {}self.kwargs = kwargsself.middleware_wraps = middleware_wrapsdef __call__(self, req, *args, **kw):"""Call this as a WSGI application or with a request"""func = self.funcif func is None:if args or kw:raise TypeError("Unbound %s can only be called with the function it ""will wrap" % self.__class__.__name__)func = reqreturn self.clone(func)if isinstance(req, dict):if len(args) != 1 or kw:raise TypeError("Calling %r as a WSGI app with the wrong signature")environ = reqstart_response = args[0]req = self.RequestClass(environ)req.response = req.ResponseClass()try:args = self.argsif self.middleware_wraps:args = (self.middleware_wraps,) + argsresp = self.call_func(req, *args, **self.kwargs)except HTTPException as exc:resp = excif resp is None:## FIXME: I'm not sure what this should be?resp = req.responseif isinstance(resp, text_type):resp = bytes_(resp, req.charset)if isinstance(resp, bytes):body = respresp = req.responseresp.write(body)if resp is not req.response:resp = req.response.merge_cookies(resp)return resp(environ, start_response)else:if self.middleware_wraps:args = (self.middleware_wraps,) + argsreturn self.func(req, *args, **kw)

该装饰器是一个类,返回的是一个可执行的对象。同以上代码分析可知,该装饰器是一个带参装饰器,当调用被该装饰器修饰的函数时,就会实例化该装饰器实例,然后再调用该可执行实例,参数为AuthProtocol.call(…),最后再次被调用参数为environ和start_response

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

模型:webob.dec.wsgify(RequestClass=_request._AuthTokenRequest)(\AuthProtocol.call(…))(req, *args, **kw),由此可以看出实际上environ和start_response是直接传给了装饰器实例,那么装饰器实例对其做了什么呢,有如下代码片段可以知道:

    def __call__(self, req, *args, **kw):...environ = reqstart_response = args[0]req = self.RequestClass(environ)req.response = req.ResponseClass()
...resp = self.call_func(req, *args, **self.kwargs)...def call_func(self, req, *args, **kwargs):"""Call the wrapped function; override this in a subclass tochange how the function is called."""return self.func(req, *args, **kwargs)
...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

装饰器实例对其做了什么呢,有如下代码片段可以知道:

    def __call__(self, req, *args, **kw):...environ = reqstart_response = args[0]req = self.RequestClass(environ)req.response = req.ResponseClass()
...resp = self.call_func(req, *args, **self.kwargs)...def call_func(self, req, *args, **kwargs):"""Call the wrapped function; override this in a subclass tochange how the function is called."""return self.func(req, *args, **kwargs)

由上面的源码可知,服务器在接受请求后,调用app时,传递的参数environ和start_response没有变,只是把他传递给了装饰器修饰后的相应方法,该方法对参数做了一层处理,把使用environ作为参数,创建RequestClass(Request)实例,args[0]就是start_response。然后在调用filter的__call__(self, req)方法。

该装饰器在webob包中,webob也是与wsgi相关的工具库。其中Request、Response和Exception对象很重要。这里就不展开讲解。

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

到这里就清楚了,当server接受到http请求之后来,把请求的一些相关信息都封装在environ对象中,再调用app(environ, start_response)来处理请求,start_response是服务器放的hook,webob对app(environ,start_response)做了拦截,转换成app(req)的请求传递方式,经过多个filter app处理后,再由最后一个app来处理请求。
filter app在哪里做的过滤操作
我们知道装饰器在做了参数转换之后,交给了filter app来处理,下面以keystonemiddleware.auth_token:filter_factory filter app的处理过程为例,该filter的app是一个AuthProtocol类型的实例,他的父类中实现了__call__(…)方法。

    @webob.dec.wsgify(RequestClass=_request._AuthTokenRequest)def __call__(self, req):"""Handle incoming request."""#处理接受到的请求response = self.process_request(req)if response:return response#传递到下一个app继续处理response = req.get_response(self._app)#处理上一个app处理后的结果return self.process_response(response)

由源码可知,filter是通过实现process_request和process_response方法来实现对请求和响应的过滤。每个filter因功能的不同,其方法的实现也有所差异,具体过程不再赘述。
request的处理过程如下:

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

...
from paste import deploy
...def load_paste_app(app_name, flavor=None, conf_file=None):
#更新app_name,主要根据glance-api.conf配置文件中的paste_deploy片段的flavor配置项,
更新后app_name为glance-api-{flavor}app_name += _get_deployment_flavor(flavor)if not conf_file:conf_file = _get_deployment_config_file()...app = deploy.loadapp("config:%s" % conf_file, name=app_name)return app

从上面的部分代码可以看出glance-api的app是使用paste.deploy.loadapp(…)加载的。
由此可以知道paste在glance-api服务中是用来加载app的。

openstack glance-api-paster相关推荐

  1. linux文件删除漏洞,OpenStack Glance v1 API任意文件删除漏洞(CVE-2012-4573)

    发布日期:2012-11-08 更新日期:2012-11-13 受影响系统: openstack Glance Grizzly openstack Glance Essex (2012.1) open ...

  2. linux glance删除命令,OpenStack Glance v2 API任意文件删除漏洞(CVE-2012-5482)

    发布日期:2012-11-08 更新日期:2012-11-13 受影响系统: openstack Glance Grizzly openstack Glance Essex (2012.1) open ...

  3. OpenStack Glance(镜像服务)基础架构:Basic architecture

    https://docs.openstack.org/glance/pike/contributor/architecture.html OpenStack Glance has a client-s ...

  4. openstack——Glance镜像服务

    openstack--Glance 一.Glance镜像服务 1.镜像 2.镜像服务 3.Images API的版本 4.镜像格式 5.镜像状态 6.访问权限 二.Glance架构详解 1.架构图 三 ...

  5. OpenStack——glance

    OpenStack--glance 一.glance镜像服务 1.镜像服务 2.Images API的版本 3.镜像格式 4.镜像格式 5.镜像状态 6.访问权限 7.架构图 8.工作流程 二.部署g ...

  6. glance服务器上传的镜像支持,OpenStack Glance安装配置过程记录

    Glance是作为OpenStack的虚拟机的Image(镜像)服务, 它提供了一系列的REST API, 用来管理.查询虚拟机的镜像, 它支持多种后端存储介质, 例如用本地文件系统作为介质.Swif ...

  7. openstack api_探索适用于PowerVC的OpenStack REST API

    IBM®Power®虚拟化中心(PowerVC)是针对IBM Power平台的基于OpenStack的IaaS云解决方案,旨在简化虚拟资源的管理. PowerVC由GUI和RESTful API组成, ...

  8. Openstack Restful API 开发框架 Paste + PasteDeploy + Routes + WebOb

    目录 目录 Paste PasteDeploy Routes WebOb 简介 WSGI入口 Paste和PasteDeploy 配置文件 pasteini 中间件的实现 Routes WebOb 参 ...

  9. Openstack glance 安装 403错误

    Openstack glance 安装 403错误 在按照教程装glance过程中出现了403错误 错误复现: 执行openstack endpoint create --region RegionO ...

  10. OpenStack Glance简介

    1. 含义 管理 VM 的启动镜像,Nova 创建 VM 时将使用 Glance 提供的镜像. 2. glance架构图 Image 的 metadata 会保持到 database 中,默认是 My ...

最新文章

  1. TensorFlow安装 通过Anaconda Prompt Win10 64位安装 cpu版 tensorflow
  2. nginx 没有cookie_Nginx 内容缓存及常见参数配置
  3. AC_Automata模板
  4. 社区奖品之 【图书】基于Project 2002的项目管理
  5. P5299-[PKUWC2018]Slay the Spire【dp】
  6. css之hover改变子元素和其他元素样式
  7. spring aop实现原理_Spring 异步实现原理与实战分享
  8. r语言把多个图合并在一张图_R语言绘图 | 折线图画法,如何画出你满意的图?
  9. sklearn学习笔记之preprocessing
  10. 从零开始使用Vscode调试XV6
  11. A4纸在屏幕上的像素尺寸
  12. webpack抽离 公共代码
  13. 知乎live-李笑来-人人都能用英语-笔记
  14. 完美解决IDEA 中Maven插件报红详细攻略(含阿里云镜像下载失败),差点泪崩...冲冲冲
  15. replace语句的使用---鸡肋命令
  16. Adb shell命令直接打开语言设置界面
  17. 计算机专业英语四个部分思维导图,一张思维导图,彻底分清英语五大基本句型...
  18. 社交返利APP正在被返利机器人,普通返利APP集体围攻绞杀
  19. php 量化交易 开源,hikyuu开源量化交易研究框架 v1.0
  20. 供应链金融生态系统全解读,在不同的行业都有哪些模式?

热门文章

  1. 特殊古典加密方法解密实践
  2. Redis容灾备份的方法
  3. 物理内存占用多少正常
  4. 网格建模资源管理(第一次翻译老外的东西,嘿嘿!)
  5. 基于51单片机十字路口交通信号灯(启动按键+绿灯同亮报警)
  6. 泰坦尼克号 第三章 模型搭建和评估
  7. 汉语数字或罗马数字转化为阿拉伯数字:例如:一百二十三为123、III为3
  8. 可编程并行通信接口8255A
  9. windows 服务器使用量高导致网络异常
  10. java实现画笔的画圆与矩形功能_Java实现画线、矩形、椭圆、字符串功能