【翻译】Building on Horizon

注:openstack horion项目和其他的项目有所不同,它主要提供一套工具,我们可以自己定制开发我们想要的dashboard(控制面板)。

翻译自:http://docs.openstack.org/developer/horizon/topics/tutorial.html


本教程讨论如何使用Horizon中多样的组件来建立一个dashboard和panel通过 数据表和tabs。

作为例子,我们讲以Nova 实例化API为基础来创建一个新的可视化的dashboard通过一个"floking"面板通过不同的方表示实例的数据。

你可以找到一个参考实现的的代码在github https://github.com/gabrielhurley/horizon_demo.

注意:你或许需要先阅读一些有帮助的资源,因为这是一个高级的教程。例如,你或许想开始Horizon quickstart guide 或者 Django tutorial.

创建一个 dashboard(控制面板)

注意:你可以创建一个panel而不是dashboard,然后整合它到一个已经存在的dashboard。看章节overrides 。

快速版本

Horizon 提供了一套自定义管理命令来创建一个典型的基于dashboard的结构。下列的命令生成了大部分的模板代码:

./run_tests.sh -m sstartdash visualizations
非常推荐你阅读剩余的章节来理解这个命令创建了什么和为什么要创建。


结构

dashboard(或者panel)推荐的结构适合于典型的Django应用布局。我们将命名我们的dashboard "visualizations":

visualizations
  |--__init__.py
  |--dashboard.py
  |--templates/
  |--static/
dashboard.py模块将通过使用Horizon包含我们要使用的dashboard类;templates和static目录分别是我们的Django模板文件和静态媒体文件。


在static和templates目录中,不错的命名空间应该是像这样:

templates/
  |--visualizations/
static/
  |--visualizations/
     |--css/
     |--js/
     |--img/
在那些文件和目录的地方,我们可以继续写我们自己的dashboard类。


定义一个dashboard

一个dashboard类可以极为简单(最少3行),至少定义一个name和一个slug:

import horizon

class VizDash(horizon.Dashboard):
    name = _("Visualizations")
    slug = "visualizations"

在实践中,一个dashboard类将通常包含更多的信息,例如一系列的panels。就是那些默认的panel,和任何访问dashboard的权限:

class VizDash(horizon.Dashboard):
    name = _("Visualizations")
    slug = "visualizations"
    panels = ('flocking',)
    default_panel = 'flocking'
    permissions = ('openstack.roles.admin')

从之前构建的例子我们也可以想要定义一组panels共享一个主题而且在导航上有一个子标题:

class InstanceVisualizations(horizon.PanelGroup):
    slug = "instance_visualizations"
    name = _("Instance Visualizations")
    panels = ('flocking',)

class VizDash(horizon.Dashboard):
    name = _("Visualizations")
    slug = "visualizations"
    panels = (InstanceVisualizations,)
    default_panel = 'flocking'
    permissions = ('openstack.roles.admin',)
PanelGroup可以添加到dashboard类的panels列表就像 panels的slug那样。


一旦我们的dashboard类完成,我们需要做的是注册它:

horizon.register(VizDash)
这个操作的典型的位置是dashboard.py文件的底部,但是它可以在任何的其他地方,例如在一个重写的文件中。


创建一个panel

现在我们已经有了我们写的dashboard,我们也可以创建我们的panel。我们讲称之为"flocking"。

注意:你不需要为了添加一个panel而写一个dashboard。这个结构是为了教程的完整性

快速版本

Horizon提供了一套自定义管理命令来创建一个典型的基于panel的结构。下面的命令生成大部分的模板代码:

./run_test.sh -m startpanel flocking --dashboard=visualizations --target=auto
dashboard参数是必选的,告诉命令panel注册的dashboard。target参数是可选的,auto意味着创建的panel文件应该在dashboard模块内相对于当前的目录(默认)。


非常推荐你阅读剩余的章节来理解这个命令创建了什么和为什么要创建。

结构

一个Panel是一个相对flat结构,dashboard的panel的templates在dashboard的templates目录而不是在panel的templates目录。继续我们的vizulaization/flocking例子,让我们看看它们应该看起来像:

# stand-alone panel structure
flocking/
  |--__init__.py
  |--panel.py
  |--urls.py
  |--views.py
  |--templates/
     |--flocking/
        |--index.html

# panel-in-a-dashboard structure
visualizations/
|--__init__.py
|--dashboard.py
|--flocking/
   |--__init__.py
   |--panel.py
   |--urls.py
   |--views.py
|--templates/
   |--visualizations/
      |--flocking/
         |--index.html
跟随标准的Django命名规范。同样工作在Django的自动模板查找特性下。


定义一个panel

上面指定panel.py文件有一个特殊的意义。在一个dashboard中,任何模块的名字排列了panels属性在dashboard上将自动在panel.py文件中查找在相应的目录(详细情况有些神奇,但是被彻底的审查在Django的admin代码库中)。

在panel.py模块中我们定义我们的 Panel类:

class Flocking(horizon.Panel):
    name = _("Flocking")
    slug = 'flocking'
简单吧?一旦我们定义了它,我们就在dashboard中注册它:



from visualizations import dashboard

dashboard.VizDash.register(Flocking)
简单! 你可以设置更多的可选的自定义项目在Panel类中,但是聪明的建议是想一下默认下应该有什么。


URls

一个充满智慧的设想就是Panel类可以被找到在一个urls.py文件在你的panel目录,我们定义一个叫做index的视图作为panel的默认处理视图。下面是你的urls.py看起来应该像:

from django.conf.urls.defaults import patterns, url
from .views import IndexView

urlpatterns = patterns('',
    url(r'^$', IndexView.as_view(), name='index')
)

这不是100%标准的Django代码。这个例子(Horizon通常上)使用了基于类的视图在Django1.3中被介绍,使代码更加能重用。因此视图类被import在上面的例子中,as_view()方法被调用在URL pattern。


当然,假定你有一个视图类,然后带我们到写panel中去。

Tables, Tabs, Views

现在我们要去真正令人激动的部分了;在这之前的任何事情都是结构性的。

从高层次的视图开始,我们最终的目标是创建一个视图(我们的IndexView类)使用Horizion的DataTable类来展示数据,使用Horizon的TabGroup类给我们一个用户友好的分页式的结构在浏览器中。

我们将先开始table,整合它与tabs,然后建立我们的视图。

定义一个table

Horizon提供了一个DataTable类简化了绝大多数的显示数据给最终用户。我们这里只是浏览表面,但是它有一个巨大数量的能力。

这种情况下,我们使用tables呈现数据,所以让我们开始定义我们的table(一个tables.py模块:

from horizon import tables

class FlockingInstancesTable(tables.DataTable):
    host = tables.Column("OS-EXT-SRV-ATTR:host", verbose_name=_("Host"))
    tenant = tables.Column('tenant_name', verbose_name=_("Tenant"))
    user = tables.Column('user_name', verbose_name=_("user"))
    vcpus = tables.Column('flavor_vcpus', verbose_name =_("VCPUs"))
    memory - tables.Column('flavor_memory', verbose_name=_("Memory"))
    age = tables.Column('age', verbose_name=_("Age"))

    class Meta:
        name = "instances"
        verbose_name = _("Instances")
有几件事情发生了,我们创建了一个table子类,定义了六列。每列定义了它访问实例类的属性作为第一个参数,由于我们希望所有的事情都是可以被翻译的,我们给没列一个verbose_name标记它们可以被翻译。


最后,我们添加了一个Meta类定义了一些属性关于我们的table,尤其是(翻译)verbose name,一个"slug"用来识别它。

注意:这里简化了实际中的实例对象的真是结构。访问flavor,tenant,用户属性需要另一个步骤,这块的代码可以在代码示例中看到在github上。

定义tabs

因为我们有了table,可以接受数据,我们可以直接的到一个视图,但是我们可以考虑跟多。在这种情况下我们同样适用Horizon的TabGroup类。它给我们一个干净,简化的tabs接口来显示我们的可视化和可以的我们的数据。

首先,建立我们的tab

class VizTab(tabs.Tab)
    name = _("Visualization")
    slug = "viz"
    template_name = "visualizations/flocking/_flocking.html"

    def get_context_data(self, request):
        return None

这是你能做的最简单的。由于我们的可视化将使用AJAX来加载数据我们不需要传递任何内容到template上,所有我们需要做的是定义个需要使用的template的名字。


现在,我们需要给我们的数据table一个tab:

from .tables import FlockingInstancesTable

class DataTab(tabs.TableTab):
    name = _("Data")
    slug = "data"
    table_classes = (FlockingInstancesTable,)
    preload = False

    def get_instances_data(self):
        try:
            instances = utils.get_instances_data(self.tab_group.request)
        except:
            instances = []
            exceptions.handle(self.tab_group.request, _('Unable to retrieve instance list.'))
        return instances


tab稍微有点复杂。最重要的,tab指定类型-处理数据tables(所有有关的特性)-它可以使用preload属性来指定这个tab不应该被加载在默认的情况下。它将通过AJAX加载当有人点击它,保存我们的API调用在绝大多数情况下。

最后,代码介绍了Horion中的错误处理的概念。horizon.exceptions.handle()功能集中的错误处理机制。

把他们整合到一个视图中

有很多基于类的预建视图在Horizon。我们试着提供起始点给所有的通用整合组件。

这种情况下我们想要一个起始的视图类型与tab和tables一起工作,那应该是TabbedTableView类,它很好的动态延时加载能力tab组提供而且混合在actions中,同时AJAX更新tables在用户端没有做任何工作。让我们看看代码应该是什么样子:

from .tables import FlockingInstancesTable
from .tabs import FlockingTabs

class IndexView(tabs.TabbedTableView):
    tab_group_class = FlockingTabs
    table_class = FlockingInstanceTable
    template_name = 'visualizations/flocking/index.html'
这提供了我们100%的方法给我们想要的,如果这个特殊的例子不包含一个额外的AJAX调用来回收我们的可视化数据通过AJAX。因此我们需要重写类中的get方法来返回一个正确的数据给一个AJAX调用:


from .tables import FlockingInstancesTable
from .tabs import FlockingTabs

class IndexView(tabs.TabbedTableView):
    tab_group_class = FlockingTabs
    table_class = FlockingInstanceTable
    template_name = 'visualizations/flocking/index.html'

    def get(self, request, *args, **kwargs):
        if self.request.is_ajax() and self.request.GET.get("json", False):
            try:
                instances = utils.get_instances_data(self.request)
            except:
                instances = []
                exceptions.handle(request, _('Unable to retrieve instance list.'))
            data = json.dumps([i._apiresource._info for i in instances])
            return http.HttpResponse(data)
        else:
            return super(IndexView, self).get(request, *args, **kwargs)
在这个例子中,我们重写了get()方法这样如果不是一个AJAX请求但是有 我们寻找的GET参数,返回我们的实例数据用JSON格式;否则只是像平常一样返回视图功能。


模板

这里我们需要三个模板:一个给视图,一个给我们的每个两个tabs。视图模板可以继承其他的dashboard:

{% extends 'syspanel/base.html' %}
{% load il8n %}
{% block title %}{% trans "Flocking" %}{% endblock %}

{% block page_header %}
   {% include "horizon/common/_page_header.html" with title=_("Flocking") %}
{% endblock page_header %}

{% block syspanel_main %}
<div class="row-fluid">
 <div class="span12">
   {{ tab_group.render }}
 </div>
</div>
{% endblock %}
这给了我们一个自定义的页面标题,一个头,和渲染视图提供的tab组、


对于tabs,使用的table是重用的模板,"horizon/common/_detail_table.html"。这适合任何的只显示一个table的tab。

第二个tab是一个隐藏的,但是它非常简单而且可以在github的例子中研究它。

每个tab需要一个模板与它关联。

通过这些本地的代码,唯一的剩下的事情就是去整合它们到我们的 OpenStack Dashboard网站。

建立一个项目

大部分的人会依靠Horizon定制自己的Dashboard。所以,这个教程只是想说明它可以被定制。

结构

一个基于Horizon的网站形式是一个典型的Django项目:

site/
  |--__init__.py
  |--manage.py
  |--demo_dashboard/
     |--__init__.py
     |--models.py  # required for Django even if unused
     |--settings.py
     |--templates/
     |--static/
demo_dashboard在我们的python路径下,settings.py文件将包含我们的定制的Horizon配置。


配置文件

有一些关键的事情你需要定制化你网站的配置文件:指定定制的dashboard和panels,获取你的客户端的异常类,和指定一个高级覆写文件。

指定dashboards

最基础的事情是添加你自己的定制的dashboard使用HORIZON_CONFIG字典在配置文件中:

HORIZON_CONFIG = {
    'dashboards': ('nova', 'syspanel', 'visualizations', 'settings',),
}
在这种情况下,我们使用了默认的Horizon配置然后添加了我们的visualizations dashboard。注意这里的名字就是pyhton路径下dashboard的模块的名字。它会找到我们的dashboard.py文件在里面,然后自动从这加载dashboard和它的panesl。

错误处理

添加定制的错误处理给你的API客户端非常的简单。在这个例子中不是必须的。通过定制'exceptions'的值在HORIZON_CONFIG字典:


import my_api.exceptions as my_api

'exceptions': {'recoverable': [my_api.Error,
                               my_api.ClientConnectionError],
               'not_found': [my_api.NotFound],
               'unauthorized': [my_api.NotAuthorized]}

重写文件

重写文件是"god-mode"dashboard编辑。通过一个重写文件你可以修改任何你想存在在代码中的行为。这个教程并不深入,但是我们只能说能力越大责任越大。

为了指定一个重写文件,你设置customization_module''值在HORIZON_CONFIG字典。

HORIZON_CONFIG = {
    'customization_module': 'demo_dashboard.overrides'
}
这个文件能够添加dashboard,添加panel到存在的dashboard,重命名存在的dashboards和panels(或者改变他们的属性),从存在的dashboard上删除panels,等等。

我们可以说的跟多,但是只会更加危险 ....

结论

教程中的信息从不意味着给你一个可以工作的dashboard,因为这里涉及太多的javascript在可视化,这和Horizon本身是不相干的。

如果你想看到最终的产品,查找github例子。

clone repository 然后执行./run_tests.sh --runserver.这会给你一个可以工作的dashboard使用了每一个教程中的技巧。


你可能感兴趣的:(openstack,horizon)