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调用情况。

img

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源码中。

posted @ 2022-06-07 16:38  红雨520  阅读(193)  评论(0编辑  收藏  举报