openstack glance-api-paster
来源:互联网 发布:海马模拟器 for mac 编辑:程序博客网 时间:2024/05/18 09:42
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_factorydelay_auth_decision = true
glance-api 服务的入口函数:
def main(): 。。。 server = wsgi.Server(initialize_glance_store=True) #加载glance-api的app server.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-keystonedef 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.ini scheme, 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] == _loadconfig return _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.inidef _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-keystonedef 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-keystone section = 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 it continue local_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.获取pipeline pipeline = 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_type self.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_conf self.local_conf = local_conf self.loader = loader self.distribution = distribution self.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 it continue local_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): #创建app app = context.app_context.create() #创建filter filters = [c.create() for c in context.filter_contexts] #将filters列表反转 filters.reverse() #filter封装app for 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_wrapper else: 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_factoryapp = 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')) ...
#BaseAuthProtocol def __init__(self, app, log=_LOG, enforce_token_bind=_BIND_MODE.PERMISSIVE): self.log = log self._app = app self._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 response response = req.get_response(self._app) return self.process_response(response)
所以可以断定一定是这个装饰器在其中起了作用。那么这个装饰器做了什么呢,让原本的environ和start_response变成了req。进入装饰器中看一下就一目了然了,如下是装饰器的__init__方法:
#注:RequestClass = webob.Request def __init__(self, func=None, RequestClass=None, args=(), kwargs=None, middleware_wraps=None): self.func = func if (RequestClass is not None and RequestClass is not self.RequestClass): self.RequestClass = RequestClass self.args = tuple(args) if kwargs is None: kwargs = {} self.kwargs = kwargs self.middleware_wraps = middleware_wraps def __call__(self, req, *args, **kw): """Call this as a WSGI application or with a request""" func = self.func if 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 = req return 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 = req start_response = args[0] req = self.RequestClass(environ) req.response = req.ResponseClass() try: args = self.args if self.middleware_wraps: args = (self.middleware_wraps,) + args resp = self.call_func(req, *args, **self.kwargs) except HTTPException as exc: resp = exc if resp is None: ## FIXME: I'm not sure what this should be? resp = req.response if isinstance(resp, text_type): resp = bytes_(resp, req.charset) if isinstance(resp, bytes): body = resp resp = req.response resp.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,) + args return 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 = req start_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 to change 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 = req start_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 to change 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
- openstack-glance API
- [OpenStack] glance api 的实验(curl)
- openstack安装glance-api服务启动失败
- openstack glance
- Openstack API管理之glance镜像服务
- openstack中glance组件images的所有python API 汇总
- OpenStack Glance 配置
- openstack之glance篇
- openstack 学习之glance
- OpenStack Glance简介
- openStack glance index
- OpenStack glance 认证函数
- OpenStack Glance 之paste
- openstack glance app
- openstack glance 不能启动
- OpenStack-glance运维
- openstack Glance概念
- __declspec(dllimport)
- Binder源码分析之ServiceManager
- PhotoDraweeView for Fresco
- Hibernate的关联映射
- UnionID机制
- openstack glance-api-paster
- TreeMap Comparator 排序
- [FAQ12402]OTG/SD卡热拔插引起的Gallery NE问题
- OC-Js的交互问题
- Quartz任务调度快速入门
- 欢迎使用CSDN-markdown编辑器
- 学习javaSE基础中遇到的一些难点的总结
- android添加常驻图标到状态栏
- iOS开发面试题:#import 跟#include @class区别