自定义Horizon

自定义Horizon

一、写在前面

继上篇博文,在这篇博文中说明自定义Horiozn,基于目前OpenStack rocky版本。

邮箱地址:jpzhang.ht@gmail.com
个人博客:https://jianpengzhang.github.io/
CSDN博客:http://blog.csdn.net/u011521019
Horizon 原文阅读地址:https://docs.openstack.org/horizon/ocata/topics/customizing.html



## 二、更改Horizon Web标题
可以通过将属性SITE_BRANDING添加到local_settings.py(openstack_dashboard/local/local_settings.py),其值将作为覆盖OpenStack Dashboard站点标题(即“OpenStack Dashboard”)。

## 三、更改Logo点击链接

Web Logo图标也可以用来当超链接,默认点击后的行为是重定向到“horizon:user_home”。可以通过在local_settings.py更新属性SITE_BRANDING_LINK改变超链接的目标,例如:
1
SITE_BRANDING_LINK = "https://www.baidu.com"

四、自定义页面页脚

通过theme的模板添加自定义页脚即可:openstack_dashboard/templates/_footer.html(页面页脚)、openstack_dashboard/templates/_login_footer.html(登录页面页脚)、openstack_dashboard/templates/_login_form_footer.html(登录表单页脚)。
默认:_footer.html

1
2
3
4
{% comment %}
A simple placeholder template for custom global footer content
{% endcomment %}

模板引擎会忽略掉 comment 和 endcomment 之间的所有内容,所以默认情况下,Horizon虽然引入了页脚文件,但是没有加载任何信息。

五、修改Dashboards 和 Panels

更改Horizon原生的代码或者其他代码, 可以指定一个自定义Python模块, 该模块将在整个 horizon初始化之后加载, 但在urlconf构造之前加载。例如:

  • 从现有Dashboard注册或取消注册Panel。
  • 更改Dashboard和Panel的名称。
  • 重新排序Dashboard或Panel组中的Panel。

默认Horizon面板基于openstack_dashboard/enabled/文件夹中的文件加载。这些文件是根据文件名顺序加载的,你可以添加更多文件。此文件夹中有一些示例文件(以.example后缀)。

六、Horizon定制模块(覆盖)

Horizon具有可用于执行尚未通过配置设置进行自定义的全局覆盖机制。此文件可以执行修补和其他形式的自定义,这是通过启用文件夹的自定义方法无法实现的。
要指定包含修改的python模块,请将关键的customization_module添加到local_settings.py中的HORIZON_CONFIG字典中。该值应该是一个字符串,其中包含了用点分隔的python路径符号表示的模块路径。例如:

1
HORIZON_CONFIG["customization_module"] = "my_project.overrides"

你可以在自定义模块中执行你喜欢的任何操作。例如,可以更改面板的名称:

1、在openstack_dashboard/locallocal_settings.py添加或者修改customization_module:

1
HORIZON_CONFIG["customization_module"] = "myproject.overrides"

2、新建openstack_dashboard/myproject目录,结构看起来如下(当然你可以在其他路径下定义这个python文件):

1
2
3
.
├── __init__.py
└── overrides.py

3、overrides.py添加以下代码,即可修改user panel的名称为“User Options”:

1
2
3
4
5
6
from django.utils.translation import ugettext_lazy as _
import horizon
# Rename "User Settings" to "User Options"
settings = horizon.get_dashboard("settings")
user_panel = settings.get_panel("user")
user_panel.name = _("User Options")

注意:以下的修改都在overrides.py文件中。

或者获取instances面板:

1
2
projects_dashboard = horizon.get_dashboard("project")
instances_panel = projects_dashboard.get_panel("instances")

或者完全删除它:

1
projects_dashboard.unregister(instances_panel.__class__)

无法取消注册为default_panel的panel。如果要删除default_panel,则需要在dashboard中将其他panel设置为default_panel,然后取消注册前者。 例如,如果你希望从项目dashboard中删除overview_panel,则可以执行以下操作:

1
2
3
4
project = horizon.get_dashboard('project')
project.default_panel = "instances"
overview = project.get_panel('overview')
project.unregister(overview.__class__)

还可以使用自己的版本覆盖现有方法:

1
2
3
4
5
6
from openstack_dashboard.dashboards.admin.info import tabs
from openstack_dashboard.dashboards.project.instances import tables
NO = lambda *x: False
tables.AssociateIP.allowed = NO
tables.SimpleAssociateIP.allowed = NO
tables.SimpleDisassociateIP.allowed = NO

还可以通过重新定义其Meta类的columns属性来自定义现有表中显示的列。可以通过3个步骤实现:

  • 1、扩展要修改的表;
  • 2、为此新表重新定义Meta类下的columns属性;
  • 3、修改相关视图的table_class属性,使其指向新表;

例如,如果你希望从NetworksTable中删除Admin State列,则可以执行以下操作:

1
2
3
4
5
6
7
8
9
10
from openstack_dashboard.dashboards.project.networks import tables
from openstack_dashboard.dashboards.project.networks import views
class MyNetworksTable(tables.NetworksTable):
class Meta(tables.NetworksTable.Meta):
columns = ('name', 'subnets', 'shared', 'status')
views.IndexView.table_class = MyNetworksTable

如果要添加列,可以以类似的方式覆盖父表,添加新列定义,然后使用Meta columns属性根据需要控制列顺序。

注意

添加上面的代码后,你会发现这不像上面举例一样正常生效,这是需要由运行Horizon的python进程导入myproject.overrides。如果你的模块不是作为系统范围的python包安装的,那么你可以使它可安装(例如,使用setup.py),或者你可以调整WSGI服务器使用的python路径以包括它的位置。
也许最简单的方法是在Apache的配置中向WSGIDaemonProcess行添加一个python路径参数。
看上去像:WSGIDaemonProcess [… existing options …] python-path=/home/workspace/horizon/openstack_dashboard/myproject,假设你的目录在/home/workspace/horizon/openstack_dashboard/myproject

我这里为了方便直接修改了/etc/apache2/sites-available/horizon.conf,添加:

1
WSGIDaemonProcess python-path=/home/workspace/horizon/openstack_dashboard/myproject

重启apache,刷新浏览器即可发现NetworksTable的列已经更新了。

自定义项目和用户表列

Keystone V3可以存储有关项目和用户的额外信息。使用Horizon定制模块(覆盖)中描述的覆盖机制,Horizon能够将这些额外信息显示为自定义列。 例如,如果Keystone中的用户具有属性phone_num,则可以定义新列:

1
2
3
4
5
6
7
8
9
10
11
12
13
from django.utils.translation import ugettext_lazy as _
from horizon import forms
from horizon import tables
from openstack_dashboard.dashboards.identity.users import tables as user_tables
from openstack_dashboard.dashboards.identity.users import views
class MyUsersTable(user_tables.UsersTable):
phone_num = tables.Column('phone_num', verbose_name=_('Phone Number'), form_field=forms.CharField(),)
class Meta(user_tables.UsersTable.Meta):
columns = ('name', 'description', 'phone_num')
views.IndexView.table_class = MyUsersTable

七、自定义Angular dashboards(对Angular不熟,这部分没有验证)

在Angular中,你可以编写一个插件来扩展某些功能。Horizon框架中实现此功能的两个组件是可扩展性服务和资源类型注册表服务。extensibleService允许动态扩展某些Horizon元素,包括添加、删除和替换。resourceTypeRegistry服务提供了设置和获取与资源类型对象有关的信息的方法。我们使用像OS::Glance::Image这样的Heat类型名作为我们的参考名称。
你可以在注册表中放置的一些信息包括:

  • 用于从中获取数据的API
  • 属性名
  • 动作(例如“创建卷”)
  • 详细视图或详细信息的URL路径
  • 属性信息,如标签或属性值的格式

注册表中的这些属性使用可扩展性服务(从Newton版本开始):

  • globalActions
  • batchActions
  • itemActions
  • detailViews
  • tableColumns
  • filterFacets

使用注册表中的信息,我们可以构建dashboard panel。panel使用高级指令hzResourceTable替换常用模板,因此我们不需要编写HTML和控制器代码。它为开发人员提供了一种快速构建新表或更改现有表的方法。
注意:
你仍然可以选择使用HTML模板来完全控制表单和功能。 例如,你可能想要创建自定义页脚。 你也可以直接使用hzDynamicTable指令(hzResourceTable在引擎盖下使用)。 但是,这些都不是可扩展的。 你需要完全覆盖面板。
这是一个示例模块文件,用于演示如何对Images Panel进行一些自定义:

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
(function() {
'use strict';
angular
.module('horizon.app.core.images')
.run(customizeImagePanel);
customizeImagePanel.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.app.core.images.basePath',
'horizon.app.core.images.resourceType',
'horizon.app.core.images.actions.surprise.service'
];
function customizeImagePanel(registry, basePath, imageResourceType, surpriseService) {
// get registry for ``OS::Glance::Image``
registry = registry.getResourceType(imageResourceType);
// replace existing Size column to make the font color red
var column = {
id: 'size',
priority: 2,
template: '<a style="color:red;">{$ item.size | bytes $}</a>'
};
registry.tableColumns.replace('size', column);
// add a new detail view
registry.detailsViews
.append({
id: 'anotherDetailView',
name: gettext('Another Detail View'),
template: basePath + 'demo/detail.html'
});
// set a different summary drawer template
registry.setSummaryTemplateUrl(basePath + 'demo/drawer.html');
// add a new global action
registry.globalActions
.append({
id: 'surpriseAction',
service: surpriseService,
template: {
text: gettext('Surprise')
}
});
}
})();

此外,你应该在detail.html和drawer.html中定义内容,并定义基于actions指令和允许的需求并执行定义的方法的surpriseService。

八、Icons

Horizon使用Font Awesome中的字体图标。 有关如何在代码中使用图标的说明,请参阅Font Awesome

要向“Table Action”添加图标,请使用图标属性。 例:

1
2
3
4
class CreateSnapshot(tables.LinkAction):
name = "snapshot"
verbose_name = _("Create Snapshot")
icon = "camera"

此外,可以通过将ACTION_CSS_CLASSES设置为你希望在local_settings.py文件中的所有操作按钮上显示的类的元组来配置站点范围的默认按钮类。

九、自定义CSS样式表

可以为dashboard定义自定义样式表。Horizon的基本模板openstack_dashboard/templates/base.html定义了可以覆盖的多个块。

要定义仅适用于特定dashboard的自定义css文件,请在dashboard的模板文件夹中创建基本模板,该文件夹可扩展Horizon的基本模板,例如 openstack_dashboard/dashboards/my_custom_dashboard / templates/my_custom_dashboard/base.html。

在此模板中,重新定义css块。(不要忘记包含_stylesheets.html,其中包括所有Horizon的默认样式表。):

1
2
3
4
5
6
7
8
9
10
{% extends 'base.html' %}
{% block css %}
{% include "_stylesheets.html" %}
{% load compress %}
{% compress css %}
<link href='{{ STATIC_URL }}my_custom_dashboard/scss/my_custom_dashboard.scss' type='text/scss' media='screen' rel='stylesheet' />
{% endcompress %}
{% endblock %}

然后,在openstack_dashboard/dashboards/my_custom_dashboard/static/my_custom_dashboard/scss/my_custom_dashboard.scss中自定义样式表。

所有dashboard的模板都必须从自定义dashboard中base.html继承:

1
2
{% extends 'my_custom_dashboard/base.html' %}
...

十、自定义Javascript

与添加自定义css(参见上文)类似,可以包含自定义javascript文件。

所有Horizon的javascript文件都列在openstack_dashboard/template/horizon/_scripts.html模板中,该模板包含在的Horizon基本模板js块中。

要添加自定义javascript文件,在openstack_dashboard/dashboards/my_custom_dashboard/templates/my_custom_dashboard/_scripts.html中创建,该模板扩展了horizon/_scripts.html。在此模板中覆盖包含你的自定义JavaScript文件的块custom_js_files:

1
2
3
4
5
{% extends 'horizon/_scripts.html' %}
{% block custom_js_files %}
<script src='{{ STATIC_URL }}my_custom_dashboard/js/my_custom_js.js' type='text/javascript' charset='utf-8'></script>
{% endblock %}

在dashboard自定义的基本模板中,openstack_dashboard/dashboards/ my_custom_dashboard/templates/my_custom_dashboard/base.html覆盖或包含自定义的_scripts.html的js块:

1
2
3
{% block js %}
{% include "my_custom_dashboard/_scripts.html" %}
{% endblock %}

是一个压缩的js文件,包含Horizon和dashboard的自定义脚本。
此外, 在一些情况下需要将它们放在标记。为此, 请将它们放在 horizon/_custom_head_js.html文件中。与上面提到的_scrips.html文件类似:

1
<script src='{{ STATIC_URL }}/my_custom_dashboard/js/my_marketing_js.js' type='text/javascript' charset='utf-8'></script>

或者可以将脚本直接粘贴到文件中,确保使用适当的标签

1
2
3
<script type="text/javascript">
//some javascript
</script>

十一、自定义元属性

要将自定义元数据属性添加到项目的基本模板,请将它们包含在horizon/_custom_meta.html文件中。此文件的内容将在默认Horizon元数据属性标记之后插入到页面的中

坚持原创技术分享,您的支持将鼓励我继续创作!