horizon源码解析(资源接口调用)
openstack的horizon是基于django框架开发,入口在代码库中/horizon/openstack_dashboard,了解django的会觉得该文件夹下文件比较熟悉。
urls路由入口:
# /horizon/openstack_dashboard/urls.py
from openstack_dashboard.api import rest
urlpatterns = [
re_path(r'^$', views.splash, name='splash'),
re_path(r'^api/', include(rest.urls)),
re_path(r'^header/', views.ExtensibleHeaderView.as_view()),
re_path(r'', horizon.base._wrapped_include(horizon.urls)),
]
这里主要看api的路径,nova、cinder等资源的操作都是通过api路径来进行操作的,跟着import的路径查看rest下的urls文件。
from django.urls import re_path
urlpatterns = []
# to register the URLs for your API endpoints, decorate the view class with
# @register below, and the import the endpoint module in the
# rest_api/__init__.py module
def register(view):
"""Register API views to respond to a regex pattern.
``url_regex`` on a wrapped view class is used as the regex pattern.
The view should be a standard Django class-based view implementing an
as_view() method. The url_regex attribute of the view should be a standard
Django URL regex pattern.
"""
p = re_path(view.url_regex, view.as_view())
urlpatterns.append(p)
return view
这里的register是一个路由注册器,这种写法可以让你在新增路由或者修改的时候,不需要修改urls文件,通过修改视图类中的url_regex即可完成。
接下来查看一下nova的视图文件:
# /horizon/openstack_dashboard/api/rest/nova.py
# 普通django视图类写法,使用register装饰器来注册路由,使用regex_urls值来指定路由
@urls.register
class Volumes(generic.View):
"""API over all server volumes."""
url_regex = r'nova/servers/(?P<server_id>[^/]+)/volumes/$'
@rest_utils.ajax()
def get(self, request, server_id):
"""Get a list of server volumes.
The listing result is an object with property "items". Each item is
a volume.
Example GET:
http://localhost/api/nova/servers/abcd/volumes/
"""
volumes = api.nova.instance_volumes_list(request, server_id)
return {'items': [s.to_dict() for s in volumes]}
这个的register装饰器是用来注册路由,url_regex为视图指定的url路径。ajax的装饰器用来检测认证与参数格式,同时在服务出错时,处理异常情况。
入口结构图:
如果是需要修改已有模块入口的话,在相应的文件中新增视图或者修改视图。如果是需要添加其它模块的话,新增新的模块视图py文件,并在init文件中import(注册)。
注意:新增路由的话,建议按照-模块名/资源名/资源id的格式来设计url,尽量与openstack设计风格保存一致。
openstack源码十分规范,这里只说明一个的调用路径,其它的操作基本都是一样的。如上面nova视图文件中,获取虚拟机volume信息的接口,调用instance_volumes_list,我们跟上这个函数。
# /horizon/openstack_dashboard/api/nova.py
# rest文件夹中,视图中的调用,多在api文件夹下
@profiler.trace
def instance_volumes_list(request, instance_id):
volumes = _nova.novaclient(request).volumes.get_server_volumes(instance_id)
for volume in volumes:
volume_data = cinder.cinderclient(request).volumes.get(volume.id)
volume.name = cinder.Volume(volume_data).name
return volumes
trace装饰器:
if not horizon_settings.get_dict_config('OPENSTACK_PROFILER', 'enabled'):
def trace(function):
return function
else:
def trace(function):
func_name = function.__module__ + '.' + function.__name__
decorator = profiler.trace(func_name)
return decorator(function)
这个装饰器有一点可以了解下,OPENSTACK_PROFILER这个参数。openstack在ocata版本引入的这个参数,启用这个参数之后可以在面板上方便的看到api调用情况。
client声明:
@memoized.memoized
def cached_novaclient(request, version=None):
(
username,
token_id,
project_id,
project_domain_id,
nova_url,
auth_url
) = get_auth_params_from_request(request)
if version is None:
version = VERSIONS.get_active_version()['version']
c = nova_client.Client(version,
username,
token_id,
project_id=project_id,
project_domain_id=project_domain_id,
auth_url=auth_url,
insecure=INSECURE,
cacert=CACERT,
http_log_debug=settings.DEBUG,
auth_token=token_id,
endpoint_override=nova_url)
return c
horizon调用其它服务,都是通过相应的client来进行调用的。horizon这块的调用就这么几层。最终资源的处理都是放在了相应的服务中,如虚拟机资源的处理在nova源码中,镜像资源的处理在glance源码中。