目录
前言
Openstack 新旧版本提供了不同的路由注入方式,也就是 Route Module 的代码方式不同,就二次开发而言用那一种实现方式都是可以得。但 Openstack 现在更加倾向于使用本篇这一种方式(以 Extension 动态加载),能够通过修改 nova/setup.cfg 的代码来实现修改 URL 跟具体的 Application 的 Mapping 。而不需要跟以往一般,还需要手动的实现路由注入代码。
从 Commands 到 Action 操作函数
EXAMPLE:nova --debug hypervisor-state
# Request:DEBUG (session:198) REQ: curl -g -i -X GET http://200.21.18.2:8774/v2.1/6c4e4d58cb9d4451b36e774b348e8813/os-hypervisors/statistics -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-OpenStack-Nova-API-Version: 2.6" -H "X-Auth-Token: {SHA1}73a00cca5407858b33532ae9686ff5ed5f7448b3"DEBUG (connectionpool:387) "GET /v2.1/6c4e4d58cb9d4451b36e774b348e8813/os-hypervisors/statistics HTTP/1.1" 200 260# Response:DEBUG (session:216) RESP: [200] Content-Length: 260 X-Compute-Request-Id: req-a7e8a156-49ef-4217-9679-af38d78ddfb9 Vary: X-OpenStack-Nova-API-Version Connection: keep-alive X-Openstack-Nova-Api-Version: 2.6 Date: Wed, 10 Aug 2016 11:24:23 GMT Content-Type: application/json RESP BODY: { "hypervisor_statistics": { "count": 1, "vcpus_used": 0, "local_gb_used": 0, "memory_mb": 44458, "current_workload": 0, "vcpus": 12, "running_vms": 0, "free_disk_gb": 458, "disk_available_least": 0, "local_gb": 458, "free_ram_mb": 43946, "memory_mb_used": 512}}+----------------------+-------+| Property | Value |+----------------------+-------+| count | 1 || current_workload | 0 || disk_available_least | 0 || free_disk_gb | 458 || free_ram_mb | 43946 || local_gb | 458 || local_gb_used | 0 || memory_mb | 44458 || memory_mb_used | 512 || running_vms | 0 || vcpus | 12 || vcpus_used | 0 |+----------------------+-------+
从 DEBUG 可以得到实际的 HTTP Request 为:
GET http://200.21.18.2:8774/v2.1/6c4e4d58cb9d4451b36e774b348e8813/os-hypervisors/statistics
下面介绍该 HTTP Request 如何路由到具体的 Action 操作函数。
Step 1. nova/api-paste.ini 将 HTTP Request 分发到对应的 Application
# /opt/stack/nova/etc/nova/api-paste.ini60 [composite:osapi_compute] 1 use = call:nova.api.openstack.urlmap:urlmap_factory 2 /: oscomputeversions 3 # starting in Liberty the v21 implementation replaces the v2 4 # implementation and is suggested that you use it as the default. If 5 # this causes issues with your clients you can rollback to the 6 # *frozen* v2 api by commenting out the above stanza and using the 7 # following instead:: 8 # /v1.1: openstack_compute_api_legacy_v2 9 # /v2: openstack_compute_api_legacy_v2 10 # if rolling back to v2 fixes your issue please file a critical bug 11 # at - https://bugs.launchpad.net/nova/+bugs 12 # 13 # v21 is an exactly feature match for v2, except it has more stringent 14 # input validation on the wsgi surface (prevents fuzzing early on the 15 # API). It also provides new features via API microversions which are 16 # opt into for clients. Unaware clients will receive the same frozen 17 # v2 API feature set, but with some relaxed validation 18 /v1.1: openstack_compute_api_v21_legacy_v2_compatible 19 /v2: openstack_compute_api_v21_legacy_v2_compatible 20 /v2.1: openstack_compute_api_v2189 [composite:openstack_compute_api_v21] 1 use = call:nova.api.auth:pipeline_factory_v21 2 noauth2 = compute_req_id faultwrap sizelimit noauth2 osapi_compute_app_v21 3 keystone = compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v21# 从子路径 http://200.21.18.2:8774/v2.1 我们可以定位到的 Application 为 nova.api.openstack.compute:APIRouterV21.factory123 [app:osapi_compute_app_v21] 1 paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory
剩下在子路径 /os-hypervisors/statistics 交由 Route Module 处理,首先在服务启动时,需要加载 setup.cfg 配置和路由注入 。
Step 2. Application 的代码实现节选
# /opt/stack/nova/nova/api/openstack/compute/__init__.py150 class APIRouterV21(nova.api.openstack.APIRouterV21): 1 """Routes requests on the OpenStack API to the appropriate controller 2 and method. 3 """ 4 def __init__(self, init_only=None): 5 self._loaded_extension_info = extension_info.LoadedExtensionInfo() 6 super(APIRouterV21, self).__init__(init_only) 7 8 def _register_extension(self, ext): 9 return self.loaded_extension_info.register_extension(ext.obj) 10 11 @property 12 def loaded_extension_info(self): 13 return self._loaded_extension_info# 在 APIRouterV21 这个类中没有定义路由,继续查看父类# 跳转# /opt/stack/nova/nova/api/openstack/__init__.py303 class APIRouterV21(base_wsgi.Router): 1 """Routes requests on the OpenStack v2.1 API to the appropriate controller 2 and method. 3 """ 313 @staticmethod 1 def api_extension_namespace(): 2 return 'nova.api.v21.extensions'317 def __init__(self, init_only=None, v3mode=False): # 加载 setup.cfg 中的 Extensions 来实现路由的注入364 self.api_extension_manager = stevedore.enabled.EnabledExtensionManager( 1 namespace=self.api_extension_namespace(), 2 check_func=_check_load_extension, 3 invoke_on_load=True, 4 invoke_kwds={ "extension_info": self.loaded_extension_info})
Step 3. 在 setup.cfg 中定义 Extensions 的映射
# /opt/stack/nova/setup.cfg72 nova.api.v21.extensions = ...112 hypervisors = nova.api.openstack.compute.hypervisors:Hypervisors
Step 4. 从 setup.cfg 的映射中加载 Resource 的 Controller ,等待接收 HTTP Request 的子路径
EG./os-hypervisors/statistics
# /opt/stack/nova/nova/api/openstack/compute/hypervisors.py22 from nova import compute # 这个别名对应了 URL 中的子路径 /os-hypervisors29 ALIAS = "os-hypervisors" # 一个 Resource 对应一个 Controller ,Controller 是 Resource 的 Action 操作函数集合33 class HypervisorsController(wsgi.Controller):36 def __init__(self): 1 self.host_api = compute.HostAPI() # 这个方法对应了 URL 最后一个子路径 /statistics177 def statistics(self, req): 1 context = req.environ['nova.context'] # 环境变量 2 authorize(context) # Token 认证 3 stats = self.host_api.compute_node_statistics(context) # 调用了 nova.compute:HostAPI 中的函数 4 return dict(hypervisor_statistics=stats)184 class Hypervisors(extensions.V21APIExtensionBase): 1 """Admin-only hypervisor administration.""" 2 3 name = "Hypervisors" 4 alias = ALIAS 5 version = 1 6 7 def get_resources(self): 8 resources = [extensions.ResourceExtension(ALIAS, 9 HypervisorsController(), # Controller 类 10 collection_actions={ 'detail': 'GET', 11 'statistics': 'GET'}, 12 member_actions={ 'uptime': 'GET', 13 'search': 'GET', 14 'servers': 'GET'})] 15 # 将 HTTP 的内置方法绑定到指定 Action 16 return resources 17 18 def get_controller_extensions(self): 19 return []
Step 5. HTTP Request 中子路径 /statistics
对应的具体操作函数实现
/opt/stack/nova/nova/compute/__init__.py17 from oslo_utils import importutils42 def HostAPI(*args, **kwargs): 1 """Returns the 'HostAPI' class from the same module as the configured 2 compute api 3 """ 4 compute_api_class_name = _get_compute_api_class_name() 5 compute_api_class = importutils.import_class(compute_api_class_name) 6 class_name = compute_api_class.__module__ + ".HostAPI" 7 return importutils.import_object(class_name, *args, **kwargs)