感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如果转载,请保留作者信息。
博客地址:http://blog.csdn.net/gaoxingnengjisuan
邮箱地址:[email protected]
在这篇博客中,我会依据上篇博客的内容来简单地解析cinder是如何通过api-paste.ini中的配置信息来进行指定WSGI Application的实现的。
首先来看api-paste.ini文件的具体内容:
############# # OpenStack # ############# [composite:osapi_volume] use = call:cinder.api:root_app_factory /: apiversions /v1: openstack_volume_api_v1 /v2: openstack_volume_api_v2 [composite:openstack_volume_api_v1] use = call:cinder.api.middleware.auth:pipeline_factory noauth = faultwrap sizelimit noauth apiv1 keystone = faultwrap sizelimit authtoken keystonecontext apiv1 keystone_nolimit = faultwrap sizelimit authtoken keystonecontext apiv1 [composite:openstack_volume_api_v2] use = call:cinder.api.middleware.auth:pipeline_factory noauth = faultwrap sizelimit noauth apiv2 keystone = faultwrap sizelimit authtoken keystonecontext apiv2 keystone_nolimit = faultwrap sizelimit authtoken keystonecontext apiv2 [filter:faultwrap] paste.filter_factory = cinder.api.middleware.fault:FaultWrapper.factory [filter:noauth] paste.filter_factory = cinder.api.middleware.auth:NoAuthMiddleware.factory [filter:sizelimit] paste.filter_factory = cinder.api.middleware.sizelimit:RequestBodySizeLimiter.factory [app:apiv1] paste.app_factory = cinder.api.v1.router:APIRouter.factory [app:apiv2] paste.app_factory = cinder.api.v2.router:APIRouter.factory [pipeline:apiversions] pipeline = faultwrap osvolumeversionapp [app:osvolumeversionapp] paste.app_factory = cinder.api.versions:Versions.factory ########## # Shared # ########## [filter:keystonecontext] paste.filter_factory = cinder.api.middleware.auth:CinderKeystoneContext.factory [filter:authtoken] paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory # signing_dir is configurable, but the default behavior of the authtoken我们来逐个字段对文件 api-paste.ini 的内容进行解析:
首先来看字段:
[composite:osapi_volume]
use = call:cinder.api:root_app_factory
/: apiversions
/v1: openstack_volume_api_v1
/v2: openstack_volume_api_v2
针对于标志osapi_volume,这里使用composite的分解机制,实现XXXX/XXXX形式的API交给apiversions来处理,XXXX/V1/XXXX形式的API交给openstack_volume_api_v1来处理,XXXX/V2/XXXX形式的API交给openstack_volume_api_v2来处理。
来看apiversions的实现:
[pipeline:apiversions]
pipeline = faultwrap osvolumeversionapp
[app:osvolumeversionapp]
paste.app_factory = cinder.api.versions:Versions.factory
这里使用pipeline section实现faultwrap对osvolumeversionapp的过滤操作,而osvolumeversionapp具体映射为Application:cinder.api.versions:Versions.factory。
来看openstack_volume_api_v1的实现:
[composite:openstack_volume_api_v1]
use = call:cinder.api.middleware.auth:pipeline_factory
noauth = faultwrap sizelimit noauth apiv1
keystone = faultwrap sizelimit authtoken keystonecontext apiv1
keystone_nolimit = faultwrap sizelimit authtoken keystonecontext apiv1
这里使用composite section实现了多个Application的集合应用,openstack_volume_api_v1具体映射到的Application为use = call:cinder.api.middleware.auth:pipeline_factory,这个Application对应了三个参数:noauth,keystone和keystone_nolimit。我们可以看到这里Application具体实现的方法是pipeline_factory,这个方法的实现机制和上一篇博客中所介绍的pipeline section的机制是类似的。我们可以看到在参数noauth,keystone和keystone_nolimit中,分别集成了多个应用,实际上每个参数最终实现的Application分别是最后一个,即apiv1,apiv1和apiv1,其前面的Application都扮演这最后一个Application的过滤器。我们以参数keystone为例,实现参数keystone的Application为apiv1,它前面的faultwrap sizelimit authtoken keystonecontext等应用都是它的过滤器,其实现过程也就是faultwrap(sizelimit(authtoken(keystonecontext(apiv1)))),具体的调用过程就是apiv1->keystonecontext->authtoken->sizelimit->faultwrap,前面方法的执行结果作为后面方法的输入参数,最后得到的运行结果作为参数keystone的值。可以说明,三个参数noauth,keystone和keystone_nolimit的值都是这样得到的,作为Application use = call:cinder.api.middleware.auth:pipeline_factory的输入值。
从文件中可以看出:openstack_volume_api_v1和openstack_volume_api_v2的实现过程是一致的,这里不用进行说明。
再分别来看其他的Application的实现:
[app:apiv1]
paste.app_factory = cinder.api.v1.router:APIRouter.factory
[app:apiv2]
paste.app_factory = cinder.api.v2.router:APIRouter.factory
[filter:faultwrap]
paste.filter_factory = cinder.api.middleware.fault:FaultWrapper.factory
[filter:noauth]
paste.filter_factory = cinder.api.middleware.auth:NoAuthMiddleware.factory
[filter:sizelimit]
paste.filter_factory = cinder.api.middleware.sizelimit:RequestBodySizeLimiter.factory
[filter:keystonecontext]
paste.filter_factory = cinder.api.middleware.auth:CinderKeystoneContext.factory
[filter:authtoken]
paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory
我们可以看到这些应用的不同类型与上面的分析是一致的,faultwrap noauth sizelimit authtoken keystonecontext作为过滤器都是filter类型,而apiv1和apiv2作为最后实现的Application都是app类型。
经过分析代码,我看到这些过滤器的实现类FaultWrapper,NoAuthMiddleware,RequestBodySizeLimiter和CinderKeystoneContext都是属于cinder部分的源码,它们都继承于同一个类class Middleware(Application),在这个类中实现了方法factory,而过滤器authtoken实现的文件/keystoneclient/middleware/auth_token.py中也实现了filter_factory这个方法。而在类FaultWrapper,NoAuthMiddleware,RequestBodySizeLimiter和CinderKeystoneContext之中都实现了方法__call__,而在方法filter_factory中最后返回了类AuthProtocol,而在类AuthProtocol中也实现了方法__call__,而在各个类的方法factory中都调用了对应的类的__call__方法,也就是具体实现了这些各种类型的Application的功能,其实我们可以看到这些Application主要就是实现了一些身份验证和请求参数限制等等验证性质的功能。上述就是各个Application的具体实现过程。
我在各个Application的factory方法中定制了一些参数的输出,来验证我们上述的分析(针对/v1)。这里我们以参数keystone = faultwrap sizelimit authtoken keystonecontext apiv1的实现过程为准来看:
======================================================================================== call def pipeline_factory loader = <paste.deploy.loadwsgi.ConfigLoader object at 0x1408f10> global_conf = {'__file__': '/etc/cinder/api-paste.ini', 'here': '/etc/cinder'} local_conf = {'keystone': 'faultwrap sizelimit authtoken keystonecontext apiv1', 'noauth': 'faultwrap sizelimit noauth apiv1', 'keystone_nolimit': 'faultwrap sizelimit authtoken keystonecontext apiv1'} app = <cinder.api.middleware.fault.FaultWrapper object at 0x2c62590> ======================================================================================== ======================================================================================== call class cinder.api.v1.router.APIRouter global_config = {'__file__': '/etc/cinder/api-paste.ini', 'here': '/etc/cinder'} local_config = {} ======================================================================================== ======================================================================================== call class cinder.api.middleware.auth.CinderKeystoneContext global_config = {'__file__': '/etc/cinder/api-paste.ini', 'here': '/etc/cinder'} local_config = {} app = <cinder.api.v1.router.APIRouter object at 0x247f8d0> ======================================================================================== ======================================================================================== call def filter_factory global_conf = {'__file__': '/etc/cinder/api-paste.ini', 'here': '/etc/cinder'} local_conf = {'service_protocol': 'http', 'admin_user': 'cinder', 'service_port': '5000', 'admin_tenant_name': 'services', 'auth_port': '35357', 'auth_protocol': 'http', 'auth_uri': 'http://172.21.5.164:5000/', 'admin_password': '81641f54a6864291', 'auth_host': '172.21.5.164', 'service_host': '172.21.5.164'} app = <cinder.api.middleware.auth.CinderKeystoneContext object at 0x25bb290> ======================================================================================== ======================================================================================== call class cinder.api.middleware.sizelimit.RequestBodySizeLimiter global_config = {'__file__': '/etc/cinder/api-paste.ini', 'here': '/etc/cinder'} local_config = {} app = <keystoneclient.middleware.auth_token.AuthProtocol object at 0x25afa10> ======================================================================================== ======================================================================================== call class cinder.api.middleware.fault.FaultWrapper global_config = {'__file__': '/etc/cinder/api-paste.ini', 'here': '/etc/cinder'} local_config = {} app = <cinder.api.middleware.sizelimit.RequestBodySizeLimiter object at 0x284f5d0> ========================================================================================我们可以看到方法pipeline_factory中确定了三个参数的具体信息后,先后调用APIRouter->CinderKeystoneContext->filter_factory->RequestBodySizeLimiter->FaultWrapper等类和方法中的factory方法,并且前一个Application作为后一个Application的输入参数,最后回到方法pipeline_factorypipeline_factory,而得到的最后的输入参数就是app = <cinder.api.middleware.fault.FaultWrapper object at 0x2c62590>,当然这么描述不是很准确,这里得到的app就是经过几个过滤器包装的apiv1。
好啦,分析完成了,分析的不是很深入,也不是很准确,但是挺累的哈!