OpenStack中的API结构地图

摘要

本文详细介绍了OpenStack中的paste相关配置,从中找到OpenStack API 与相关类和方法的映射规律。本文以nova API为例,步步深入研究其中的调用关系,并在最后通过添加自定义API的方式对映射关系进行了验证。

正文:

1. OpenStack中的API结构地图

当你执行如下命令的时候:

里面做了什么呢?

在这张图中我们加了–debug就看得更清楚了。

具体来说它是由两次http请求构成的。

分别是

(1)向keystone验证

1 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取得所有实例(虚机)

1 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,打开它吧。

1 nano  /etc/api-paste.ini

看到里面的注释,Metadata,EC2,OpenStack,社区还是很贴心的哇,一看就很清楚。

因为nova api主要就分成三个部分,关于metadata的部分可以参看我的另外一篇(《扩展OpenStack的nova metadata api》http://my.oschina.net/crooner/blog/603044  )。这里主要是介绍OpenStack API部分。

我把这部分内容抽出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#############
# 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_factory
noauth = compute_req_id faultwrap sizelimit noauth ratelimit osapi_compute_app_v2
keystone = compute_req_id faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2
keystone_nolimit = compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2
 
[composite:openstack_compute_api_v21]
use = call:nova.api.auth:pipeline_factory_v21
noauth = request_id faultwrap sizelimit noauth osapi_compute_app_v21
keystone = request_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v21
 
[composite:openstack_compute_api_v3]
use = call:nova.api.auth:pipeline_factory_v21
noauth = request_id faultwrap sizelimit noauth_v3 osapi_compute_app_v3
keystone = 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为例,下面的部分是我抽出来,需要重点关注的:

1
2
3
4
5
6
7
[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

 

1
2
3
4
5
[composite:openstack_compute_api_v2]
use = call:nova.api.auth:pipeline_factory
noauth = compute_req_id faultwrap sizelimit noauth ratelimit osapi_compute_app_v2
keystone = compute_req_id faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2
keystone_nolimit = compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2
1
2
[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,对应的就是

1
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__方法中,最重要的几句:

1
2
3
4
5
mapper  =  ProjectMapper()
 
self ._setup_routes(mapper, ext_mgr, init_only)
 
self ._setup_ext_routes(mapper, ext_mgr, init_only)

我们知道这几句产生了整个map路由(注:这里主要涉及到routes模块),特别要注意的是

 

1
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方法。

还有要提一下:

1
mapper.redirect(" ", " / ")

这句起了重定向的作用

在我的博客(WSGI必知必会 http://my.oschina.net/crooner/blog/609030  )的“7. 路由处理”中的最后两个例子中都有提到使用到这句,单独的例子中易于直观感受它的作用。

4. 相关的py文件怎么与API关联起来的?

还是接着上面的例子,我们看到nova.api.openstack.compute.APIRouter中的_setup_routes方法

我们还是以servers为例:

可以看到,里面最重要的依据就是:

1
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()

1
self .compute_api  =  compute.API()

它将compute.API()实例化放入self.compute_api供调用。

回到话题的中心,.py是怎么关联API的呢,这里尤其是servers,py怎么关联API(URL:xxx/servers/xxx)的。

回到images.py头部,看到

1
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(如下目录):

执行:

1
2
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进行编辑

首先在头部导入模块:

1
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。

在第一节中,我们演示过正常查看内部请求的过程

1
nova --debug list

这里我们截取一下里面的请求为我们所用

1
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"}}}'

修改成:

 

1
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密码。

执行之后,结果如下:

1
2
3
4
5
6
7
8
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 OK
Vary: X-Auth-Token
X-Distribution: Ubuntu
Content-Type: application/json
Content-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为

1
a479cdb5f5ce47aebccd15f1790beb2c

这里备用

在第一节中还有第二条curl请求,我们将其修改一下:

1
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:

1
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"

执行得到结果如下:

这里,打印出了我们想要的结果。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值