OpenStack中的API结构地图


 https://my.oschina.net/crooner/blog/609419
OpenStack中的API结构地图

摘要: 本文详细介绍了OpenStack中的paste相关配置,从中找到OpenStack API 与相关类和方法的映射规律。本文以nova API为例,步步深入研究其中的调用关系,并在最后通过添加自定义API的方式对映射关系进行了验证。
1. OpenStack中的API结构地图
当你执行如下命令的时候:
里面做了什么呢?
在这张图中我们加了--debug就看得更清楚了。
具体来说它是由两次http请求构成的。
分别是
(1)向keystone验证
REQ : curl -i 'http://ubuntu80: 35357 /v2 .0 /tokens' - X   POST  - H   "Accept: application/json"  - H   "Content-Type: application/json"  - H   "User-Agent: python-novaclient"  -d '{ "auth" : { "tenantName" "admin" "passwordCredentials" : { "username" "admin" "password" "{SHA1}5705cc2e5fda0ab7529d5093c5e389fffe45d615" }}}'
(2)调用nova-api取得所有实例(虚机)
REQ:  curl -i  'http://ubuntu80:8774/v2/0e962df9db3f4469b3d9bfbc5ffdaf7e/servers/detail'  -X GET -H  "Accept: application/json"  -H  "User-Agent: python-novaclient"  -H  "X-Auth-Project-Id: admin"  -H  "X-Auth-Token: {SHA1}5962c90ceb8f53d805382c386ac240776fe55a54"
我们从上面的例子中看到了一条条URL,而这些URL构成了整个OpenStack API的基础。本文的目的在于,通过这一条条可见的URL找到与OpenStack相关的源码(类或方法),从而迅速定位代码点。

2. OpenStack中的地图配置文件
 查找api-paste.ini,为什么找这个配置文件,可以查看我的另外一篇文章(《Paste模块的世界》 http://my.oschina.net/crooner/blog/606895   )。
我的这台服务器上安装了nova,glance,neutron(说明一下,我这里是JUNO版本),这里主要介绍nova,打开它吧。
nano   /etc/api-paste.ini
看到里面的注释,Metadata,EC2,OpenStack,社区还是很贴心的哇,一看就很清楚。
因为nova api主要就分成三个部分,关于metadata的部分可以参看我的另外一篇(《扩展OpenStack的nova metadata api》 http://my.oschina.net/crooner/blog/603044   )。这里主要是介绍OpenStack API部分。
我把这部分内容抽出来
############# # OpenStack # ############# [composite:osapi_compute]use = call:nova.api.openstack.urlmap:urlmap_factory/: oscomputeversions/v1.1: openstack_compute_api_v2/v2: openstack_compute_api_v2/v2.1: openstack_compute_api_v21/v3: openstack_compute_api_v3[composite:osapi_compute]use = call:nova.api.openstack.urlmap:urlmap_factory/: oscomputeversions/v1.1: openstack_compute_api_v2/v2: openstack_compute_api_v2/v2.1: openstack_compute_api_v21/v3: openstack_compute_api_v3[composite:openstack_compute_api_v2]use = call:nova.api.auth:pipeline_factorynoauth = compute_req_id faultwrap sizelimit noauth ratelimit osapi_compute_app_v2keystone = compute_req_id faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2keystone_nolimit = compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2[composite:openstack_compute_api_v21]use = call:nova.api.auth:pipeline_factory_v21noauth = request_id faultwrap sizelimit noauth osapi_compute_app_v21keystone = request_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v21[composite:openstack_compute_api_v3]use = call:nova.api.auth:pipeline_factory_v21noauth = request_id faultwrap sizelimit noauth_v3 osapi_compute_app_v3keystone = request_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v3[filter:request_id]paste.filter_factory = nova.openstack.common.middleware.request_id:RequestIdMiddleware.factory[filter:request_id]paste.filter_factory = nova.openstack.common.middleware.request_id:RequestIdMiddleware.factory[filter:compute_req_id]paste.filter_factory = nova.api.compute_req_id:ComputeReqIdMiddleware.factory[filter:faultwrap]paste.filter_factory = nova.api.openstack:FaultWrapper.factory[filter:noauth]paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory[filter:noauth_v3]paste.filter_factory = nova.api.openstack.auth:NoAuthMiddlewareV3.factory[filter:ratelimit]paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.factory[filter:sizelimit]paste.filter_factory = nova.api.sizelimit:RequestBodySizeLimiter.factory[app:osapi_compute_app_v2]paste.app_factory = nova.api.openstack.compute:APIRouter.factory[app:osapi_compute_app_v21]paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory[app:osapi_compute_app_v3]paste.app_factory = nova.api.openstack.compute:APIRouterV3.factory[pipeline:oscomputeversions]pipeline = faultwrap oscomputeversionapp[app:oscomputeversionapp]paste.app_factory = nova.api.openstack.compute.versions:Versions.factory
这个就是一地图啊,它告诉你里面是怎么回事,如果你已经看过《Paste模块的世界》 http://my.oschina.net/crooner/blog/606895  。
你就知道这样一个配置文件描述了一套路由系统(管道走向)及对应的factory关系。
无论怎样吧,我们还是需要分析一下里面的路径。
我们以v2版的API为例,下面的部分是我抽出来,需要重点关注的:
[ composite: osapi_compute]use =  call: nova.api.openstack. urlmap: urlmap_factory/: oscomputeversions/v1 .1 : openstack_compute_api_v2/ v2:  openstack_compute_api_v2/v2 .1 : openstack_compute_api_v21/ v3:  openstack_compute_api_v3
[composite:openstack_compute_api_v2] use = call:nova.api.auth:pipeline_factorynoauth = compute_req_id faultwrap sizelimit noauth ratelimit osapi_compute_app_v2keystone = compute_req_id faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2keystone_nolimit = compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2
[ app: osapi_compute_app_v2]paste.app_factory = nova.api.openstack. compute: APIRouter.factory
根据上面三部分,我们知道composite为起分发作用(多通管道/多口的接头管道),其中v2指向openstack_compute_api_v2。
就是下面的openstack_compute_api_v2的composite,在里面我们看到noauth,keystone,keystone_nolimit通过许多个filter之后最终都是到达osapi_compute_app_v2这个app的。
来到第三部分,这就很直接了,这个app的factory是nova.api.openstack.compute:APIRouter.factory。
nova.api.openstack.compute就是路径(python的包结构,如果熟悉java的朋友com.apache.xxx,是不是倍感亲切 :)),后面APIRouter.factory就是一个类了。

3. 一探究竟
那么进入nova.api.openstack.compute,对应的就是
locate  nova/api/openstack/compute
直接找到对应目录,然后进入查看。
既然是nova.api.openstack.compute指的自然就是__init__.py这个文件了(python基础语法知识)。
打开__init__.py文件,查找到APIRouter的factory方法。
APIRouter找到了,一直往下拉都没找到factory方法,猜想一定是OpenStack的小伙伴们做了封装。
我们看到APIRouter是继承自nova.api.openstack.APIRouter的。
好吧,我们去看看nova.api.openstack.APIRouter吧
果然,里面有了factory方法。
这里说明一下,可以使用LOG.error()打印信息到/var/log/nova/nova-api.log。
这里可以得出这样的结论:
nova.api.openstack.compute.APIRouter类继承自nova.api.openstack.APIRouter,在nova.api.openstack.APIRouter内部的__init__方法中,最重要的几句:
mapper = ProjectMapper() self ._setup_routes(mapper, ext_mgr, init_only) self ._setup_ext_routes(mapper, ext_mgr, init_only)
我们知道这几句产生了整个map路由(注:这里主要涉及到routes模块),特别要注意的是
self ._setup_routes(mapper, ext_mgr, init_only)
它生成了主要的URL规则(你可以通过调试看到),在nova.api.openstack.APIRouter中的 _setup_routes方法:
它只是抛出了一个异常,那么它必然被子类所实现。
回到nova.api.openstack.compute.APIRouter中的_setup_routes方法:
非常明显,这里注意mapper.resource方法,它会自动生成相关的RESTful API URL,如果朋友们有兴趣可以试一下routes模块的mapper.resource方法。
还有要提一下:
mapper .redirect ( "" "/" )
这句起了重定向的作用
在我的博客(WSGI必知必会  http://my.oschina.net/crooner/blog/609030   )的“ 7. 路由处理 ”中的最后两个例子中都有提到使用到这句,单独的例子中易于直观感受它的作用。

4. 相关的py文件怎么与API关联起来的?
还是接着上面的例子,我们看到nova.api.openstack.compute.APIRouter中的_setup_routes方法
我们还是以servers为例:
可以看到,里面最重要的依据就是:
self .resources[ 'servers' ] = servers.create_resource(ext_mgr)
回到目录结构:
找到servers.py中的create_resource方法:
可以看到,这里将Controller对象放入wsgi.Resource方法调用。
Controller中是本servers.py中最重要的部分,里面有主要的API包括,detail,index...等方法,这里请记住detail,我们会在后面的例子中用到。
还有要注意的是,在Controller的__init__构造方法中,加载了compute.API()
self .compute_api = compute.API()
它将compute.API()实例化放入self.compute_api供调用。
回到话题的中心,.py是怎么关联API的呢,这里尤其是servers,py怎么关联API(URL:xxx/servers/xxx)的。
回到images.py头部,看到
from   nova .api.openstack   import   wsgi
那么就可以在下面的路径中找到wsgi.py
定位到Resource类中的__init__方法与register_actions方法之间看到:
在register_actions中加载进对应的controller中的所有方法。
即:将所有API通过wsgi.Resource将对应Controller中的所有方法加载进来。
看到整个的调用链:
api-paste.ini -> /v2: openstack_compute_api_v2 -> osapi_compute_app_v2 -> nova.api.openstack.compute:APIRouter -> self._setup_routes -> xx.create_resource() -> wsgi.Resource(Controller()) -> wsgi.Resource.register_actions()

5. 反推测试结论,添加自定义nova api
由上面的结论得出,在nova-api服务启动时,对应的URL与相应的类和方法就会被加载。
那么以v2版本的nova api为例,最重要的部分应该是在nova.api.openstack.compute.APIRouter中的_setup_routes,它决定了各个类和方法与URL的对应。
那么反过来,如果我们希望自己添加一个v2 API,只需要在nova.api.openstack.compute.APIRouter中的_setup_routes中添加即可。
下面我们以images为模板,添加一个test_images,进入nova.api.openstack.compute(如下目录):
执行:
cp   images .py   test_images .py nano   test_images .py
在打开test_images.py后,找到Controller中的detail方法修改如下:
接下来就应该建立URL与这个类文件的对应了
退出test_images.py,进入nova.api.openstack.compute,打开__init__.py进行编辑
首先在头部导入模块:
from   nova .api.openstack.compute   import   test_images
接着就可以来到nova.api.openstack.compute.APIRouter中的_setup_routes
将test_images添加进来
好了,一切大功告成!这个测试的api就添加完成了。

6. 测试添加的自定义API
在上面的步骤中,我们添加了自定义的API (xxx/test_images/detail),接下来我们通过curl来进行测试。
这个步骤分两步,第一步取得keystone的token,第二部执行我们自己的api。
在第一节中,我们演示过正常查看内部请求的过程
nova  -- debug  list
这里我们截取一下里面的请求为我们所用
curl  -i 'http://ubuntu80: 35357 /v2 .0 /tokens' - X   POST  - H   "Accept: application/json"  - H   "Content-Type: application/json"  - H   "User-Agent: python-novaclient"  -d '{ "auth" : { "tenantName" "admin" "passwordCredentials" : { "username" "admin" "password" "{SHA1}5705cc2e5fda0ab7529d5093c5e389fffe45d615" }}}'
修改成:
curl  -i 'http://ubuntu80: 35357 /v2 .0 /tokens' - X   POST  - H   "Accept: application/json"  - H   "Content-Type: application/json"  - H   "User-Agent: python-novaclient"  -d '{ "auth" : { "tenantName" "admin" "passwordCredentials" : { "username" "admin" "password" "ADMIN_PASS" }}}'
注意这里将{SHA1}5705cc2e5fda0ab7529d5093c5e389fffe45d615修改成ADMIN_PASS,这个ADMIN_PASS是你的OpenStack密码。
执行之后,结果如下:
root@ubuntu80:/ # curl -i 'http://ubuntu80:35357/v2.0/tokens' -X POST -H "Accept: application/json" -H "Content-Type: application/json" -H "User-Agent: python-novaclient" -d '{"auth": {"tenantName": "admin", "passwordCredentials": {"username": "admin", "password": "ADMIN_PASS"}}}' HTTP/ 1.1   200  OKVary: X-Auth-TokenX-Distribution: UbuntuContent-Type: application/jsonContent-Length:  1744 Date: Tue,  26  Jan  2016   02 : 18 : 41  GMT{ "access" : { "token" : { "issued_at" "2016-01-26T02:18:41.487301" "expires" "2016-01-26T03:18:41Z" "id" "a479cdb5f5ce47aebccd15f1790beb2c" "tenant" : { "description" "Admin Tenant" "enabled" true "id" "0e962df9db3f4469b3d9bfbc5ffdaf7e" "name" "admin" },  "audit_ids" : [ "eavCb8mjT863hnOxhdxUwQ" ]},  "serviceCatalog" : [{ "endpoints" : [{ "adminURL" "http://ubuntu80:9292" "region" "regionOne" "internalURL" "http://ubuntu80:9292" "id" "4794a2d722ab4f6bbda00d779c1410d1" "publicURL" "http://ubuntu80:9292" }],  "endpoints_links" : [],  "type" "image" "name" "glance" }, { "endpoints" : [{ "adminURL" "http://ubuntu80:8774/v2/0e962df9db3f4469b3d9bfbc5ffdaf7e" "region" "regionOne" "internalURL" "http://ubuntu80:8774/v2/0e962df9db3f4469b3d9bfbc5ffdaf7e" "id" "a8ccc19100934fc1ae7c899dc5e17bdd" "publicURL" "http://ubuntu80:8774/v2/0e962df9db3f4469b3d9bfbc5ffdaf7e" }],  "endpoints_links" : [],  "type" "compute" "name" "nova" }, { "endpoints" : [{ "adminURL" "http://ubuntu80:9696" "region" "regionOne" "internalURL" "http://ubuntu80:9696" "id" "656371fd3163415c95ff2fc0facbe5e1" "publicURL" "http://ubuntu80:9696" }],  "endpoints_links" : [],  "type" "network" "name" "neutron" }, { "endpoints" : [{ "adminURL" "http://ubuntu80:35357/v2.0" "region" "regionOne" "internalURL" "http://ubuntu80:5000/v2.0" "id" "4f1d53f12dc6485cb5816c83f68b7053" "publicURL" "http://ubuntu80:5000/v2.0" }],  "endpoints_links" : [],  "type" "identity" "name" "keystone" }],  "user" : { "username" "admin" "roles_links" : [],  "id" "96a7c834b3f8485c87d79df7b6480c92" "roles" : [{ "name" "_member_" }, { "name" "admin" }],  "name" "admin" },  "metadata" : { "is_admin" 0 "roles" : [ "9fe2ff9ee4384b1894a90878d3e92bab" "fc2574382dd74936b1bc85cc2110c3c2" ]}}}root@ubuntu80:/ #
这里获取了token的id为
a479cdb5f5ce47aebccd15f1790beb2c
这里备用
在第一节中还有第二条curl请求,我们将其修改一下:
curl  -i  'http://ubuntu80:8774/v2/0e962df9db3f4469b3d9bfbc5ffdaf7e/servers/detail'  -X GET -H  "Accept: application/json"  -H  "User-Agent: python-novaclient"  -H  "X-Auth-Project-Id: admin"  -H  "X-Auth-Token: {SHA1}5962c90ceb8f53d805382c386ac240776fe55a54"
修改如下,这里主要修改URL为http://ubuntu80:8774/v2/0e962df9db3f4469b3d9bfbc5ffdaf7e/test_images/detail,并修改token为我们第一步通过keystone获取的token:
curl  -i  'http://ubuntu80:8774/v2/0e962df9db3f4469b3d9bfbc5ffdaf7e/test_images/detail'  -X GET -H  "Accept: application/json"  -H  "User-Agent: python-novaclient"  -H  "X-Auth-Project-Id: admin"  -H  "X-Auth-Token: a479cdb5f5ce47aebccd15f1790beb2c"
执行得到结果如下:
这里,打印出了我们想要的结果。
特别声明:本文原创,欢迎大家转载,但还请注明出处,谢谢!( http://my.oschina.net/crooner/blog/609419

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值