自定义OpenStack Horizon(Mitaka)

时间:2024-11-12 11:08:37

一、写在前面

这篇文章主要介绍了OpenStack Horizon官方介绍自定义配置,从而进行简单的翻译学习,这里主要基于目前最新的Mitaka来说明,提高自己在horizon组件开发方面的理解。因为时间仓促以及个人理解有限,固有错误的地方请指出,后续将会不定期的完善,近期将专门写一篇博文详细介绍自定义开发主题模版,谢谢!
如果转载,请保留作者信息。
邮箱地址:@

二、主题(Themes)

Openstack Kilo 版本以来,OpenStack Dashboard的Theme可以改变,通过使用一个主题。主题是一个目录,包含_variables.scss文件用来覆盖整个SCSS,并用另外一个的样式文件:“_styles.scss”指定Dashboard 加载的颜色代码。

Openstack Mitaka 版本, Horizon可以被配置为在运行时可用多个主题运行。它使用一个浏览器cookie,让用户在配置的主题之间切换。默认情况下,“Horizon”主题提供“default”和“material”。要配置或改变现有的主题,在local_settings.py设置AVAILABLE_THEMES元组列表,这样,(’name’, ‘label’, ‘path)

name:主题的关键值是存储在cookie中。
label:用户菜单下的主题切换显示的标签。
path:主题的目录位置。路径必须是相对于openstack_dashboard目录或绝对路径到可访问的。

要使用自定义主题,需要在local_settings.py设置AVAILABLE_THEME。如果你想以类似传统Horizon模式运行,设置可用的主题用一个元组,主题切换将无法使用。

例如,多个主题的配置:

AVAILABLE_THEMES = [
    ('default', 'Default', 'themes/default'),
    ('material', 'Material', 'themes/material'),
]

一个单一主题的配置:

AVAILABLE_THEMES = [
    ('default', 'Default', 'themes/default'),
]

自定义添加一个test主题:
/openstack_dashboard/local/local_settings.py

AVAILABLE_THEMES = [
   ('default', 'Default', 'themes/default'),
   ('material', 'Material', 'themes/material'),
   ('test', 'Test', 'themes/test'),
]

无论是Dashboard自定义变量和Bootstrap变量都可以被覆盖。Dashboard SCSS变量的完整列表是可以改变的,见变量文件openstack_dashboard/static/dashboard/scss/_variables.scss。

建立一个自定义主题,同时需要_variables.scss和_styles.scss样式文件,_variables.scss必须提供所有默认的Bootstrap变量值。

- 继承现有主题(Inherit from an Existing Theme):

自定义主题必须实现所有的_variables.scss和_styles.scss样式,实现Horizon需要的Bootstrap变量。为了更加容易的实现,你可以继承默认主题中所需要的变量,并且只重写那些需要自定义的变量。从默认的主题继承,把这个放在你的主题样式文件_variables.scss第一行上:

@import "/themes/default/variables";

一旦你的主题设置进行了修改,你就必须重新生成静态文件,通过以下命令:
在horizon的目下没有找到该文件,有./run_tests.sh 测试脚本,后续验证。这里通过“python compress”进行压缩静态文件重新生成。

./run_tests.py -m collectstatic

默认情况下,所有的主题由AVAILABLE_THEMES配置设置,在Horizon 运行collectstatic的过程中收集。默认情况下,主题被收集到static/themes目录中,但是这个位置可以通过local_settings.py变量进行定制:THEME_COLLECTION_DIR(默认没有定义,可以自己定义一个,如果没有自定义引用在定义的值)。一旦收集,任何主题配置是通过AVAILABLE_THEMES配置设置,可以继承其他主题的变量和样式。
下面是一个从material主题继承的例子:

@import "/themes/material/variables";
@import "/themes/material/styles";

说明:
在/openstack_dashboard/ ,引入了’’ APP

INSTALLED_APPS = [
    'openstack_dashboard',
    '',
    '',
    '',
    '',
    '',
    '',
    'django_pyscss',
    'openstack_dashboard.django_pyscss_fix',
    'compressor',
    'horizon',
    'openstack_auth',
]

staticfiles:这是一个静态资源管理的app,。老的版本中,静态资源管理一直是一个问题,部分app发布的时候需要带上静态资源,在部署的时候必须将每个app存在的static静态资源复制到同一个static目录。引入staticfiles之后,执行命令:Python collectstatic 就可以方便的将所用到的app中的静态资源复制到同一目录。

staticfiles的主要相关配置与分析:

  • STATIC_ROOT:运行上边提到的命令:python collectstatic 之后静态文件将要复制到的目录,这个目录只有在运行collectstatic时候才会用到,不能想当然的以为这个目录和MEDIA_ROOT的作用是相同的,否则在开发环境的时候可能一直无法找到静态文件。
    Horizon :

    if STATIC_ROOT is None:
    STATIC_ROOT = (.join(ROOT_PATH, '..', 'static'))
  • STATIC_URL:设置的static file的起始url,这个只是在template里边引用到,这个参数和MEDIA_URL的含义相同。
    STATICFILES_DIRS:和TEMPLATE_DIRS的含义差不多,就是除了各个app的static目录以外还需要管理的静态文件设置,比如项目的公共文件差不多。
    Horizon:

    if STATIC_URL is None:
        STATIC_URL = WEBROOT + 'static/'

各个app目录下的静态文件static/django会自动找到,这个和app下的templates目录下差不多。
在中加入静态文件处理的代码:

```
#/openstack_dashboard/
from  import staticfiles_urlpatterns  # noqa
......
# Development static app and project media serving using the staticfiles app.
urlpatterns += staticfiles_urlpatterns()

# Convenience function for serving user-uploaded media during
# development. Only active if DEBUG==True and the URL prefix is a local
# path. Production media should NOT be served by Django.
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
......
```
1)、Bootswatch

Bootswatch 是一款基于 Bootstrap 的免费主题包,其中包含了丰富的 Bootstrap 主题,你可以下载安装这些主题的 CSS 文件,实现各种各样漂亮的 Bootstrap 主题风格。
Horizon material主题使用Bootswatch SCSS包。正因为如此,它是简单的使用一个现存的Bootswatch主题为基础。Bootstrap为第三方的静态资源库,添加到/horizon/lib/,下面是一个例子,如何从bootswatch的darkly主题的继承:

@import "/horizon/lib/bootswatch/darkly/variables";
@import "/horizon/lib/bootswatch/darkly/bootswatch";

- 组织主题目录(Organizing Your Theme Directory):

自定义主题目录可以不同,这取决于所期望的,因为它可以包含静态文件以及Django模板。它可以根据使用不同包括特殊的子目录:static,templates和img。

1)、static 目录(The static Folder)

如果主题文件夹包含称为静态(static)的子文件夹,那么该子文件将被用来作为为主题的静态文件根目录。即,Horizon在子文件夹的_variables.scss和_styles.scss文件的内容也将在/static/custom中。

2)、templates 目录(The templates Folder)

如果主题文件夹包含子文件夹的模板,那么子文件夹的路径将设置到TEMPLATE_DIRS元组,允许主题特定的模板定制。

3)、使用模板文件夹(Using the templates Folder)

任何Django模板用于通过一个主题可以通过覆盖。这允许在一定范围内高度定制的用户UI可以存在不同的主题。任何模板覆盖必须遵守相同的目录结构。
例如,如果你希望定制侧边栏,Horizon定义在horizon/_sidebar.html模板中。你将需要在你的主题模版目录下建立 { theme_path }/templates/horizon/_sidebar.html,用来覆盖原有的侧边栏。

4)、img文件夹(The img Folder)

如果主题文件夹包含一个img的静态根目录,那么所有的图像利用{ % themable_asset % } templatetag可以覆盖。
这些图片资源包括: , 和 ,但是覆盖dashboard/img文件夹中使用的SVG/GIF目前不支持。

- 自定义Logo(Customizing the Logo):

1)、简单的(Simple)

如果你想自定义Horizon启动界面(登录框)上或在顶部导航栏使用的Logo,那么你需要在你的主题的静态根目录下创建一个img目录并放置你的自定义或者。

如果你想用以前的方法来覆盖,如果使用的图像比顶部导航的高度大,则图像将限制,以适应高度值。 你可以通过自定义SCSS变量$navbar-height自定义顶部导航栏的高度。如果图像的高度大于导航栏高度,那么图像将保持其原有的分辨率和尺寸,并简单地在可用空间垂直居中。

在Kilo版本中,需要在horizon img文件夹中替换成你自己的图片,或者修改horizon样式文件改变图片指向,指向你的图片地址。

2)、高级的(Advanced)

如果你需要做更多的自定义logo不是简单地替换现有PNG,那么你也可以通过自定义主题_brand.html进行覆盖。要使用这种技术,只是简单的将 templates/header/_brand.html 添加到您的自定义主题的根目录下,并直接添加logo到文件中。有关如何做到这一点,见openstack_dashboard/themes/material/templates/header/_brand.html一个例子。

splash/login 登录面板也可以通过添加templates/auth/_splash.html定制。见:openstack_dashboard/themes/material/templates/auth/_splash.html 为例。

三、Branding Horizon

作为Liberty发布以来,Horiozn已经开始以符合更严格地Bootstrap标准,努力去拥抱更加适应网页设计以及缓解未来需要为每一个版本重新全新的功能。

- 支持组件(Supported Components):

以下组件,由发布组织,是唯一的,充分利用Bootstrap的主题架构。

1)、8.0.0 (Liberty):
  • Top Navbar
  • Side Nav
  • Pie Charts
2)、9.0.0 (Mitaka):
  • Tables
  • Bar Charts
  • Login
  • Tabs
  • Alerts
  • Checkboxes

- 步骤一(Step 1):

为“Horizon”创建自定义主题所需的第一步是创建自定义Bootstrap主题。有几个工具可以帮助这:

  • Bootswatchr
  • Paintstrap
  • Bootstrap

注意:Bootstrap默认使用LESS,但是我们使用SCSS,以上所有的工具将提供文件,将需要转换_variables.scss

- 顶部导航栏(Top Navbar):

在Horizon上方导航栏现在使用的是原生Bootstrap navbar导航栏。有若干可用于定制这个元素的变量。请参阅Navbar部分(Bootstrap官网),导航栏变量可以设置的细节,navbar-default:使用导航栏默认的变量。

<nav class="navbar navbar-default">
......

重要的是还要注意,导航现在使用Bootstrap下拉框,变量是可定制的。请参见下拉部分变量的文件。

顶部导航栏,针对小屏幕做了相应式设计。当屏幕窗口大小缩小时($screen-sm),导航栏将压缩成一种更适合小屏幕设计。

- 侧边航栏(Side Nav):

侧导航组件已经使用Bootstrap Pills重构。请查看你的变量文件中的具体变量,以自定义。

- 图表(Charts):

1)、饼图(Pie Charts):

饼图SVG元素。 SVG元素只允许一个基本元素的外观和感觉自定义的CSS(即颜色,大小)。
因为在Bootstrap 框架中没有专门的“Charts”,图表的外观和感觉是从主题的其他元素中继承的。请看_pie_charts.scss细节。

2)、条形图(Bar Charts):

条形图可以是Bootstrap进度条或SVG元素。任何一种实现都将使用Bootstrap 框架的进度条样式。
SVG实现不使用自定义进度条的高度,所以建议尽可能使用Bootstrap进度条。
请参阅 可以定制什么细节。请参阅Progress bars进度条部分的变量为特定变量来定制。

表格(Tables)

该标准的Django 表格利用原生Bootstrap的表格。请参阅Tables 表格部分的变量来定制。

默认标准Bootstrap表格无边框。如果您希望添加边框,如默认主题,见openstack_dashboard/themes/default/horizon/components/_tables.scss

- 登录(Login):

1)、登录页面(Login Splash Page):

登录页现在使用一个标准Bootstrap面板。查看Bootstrap Panels面板部分变量,以方便自定义。

2)、模态登录(Modal Login):

该模式的登录体验,切换区域时使用,使用标准的Bootstrap对话框。请参阅Bootstrap Modal模块部分的变量进行特定变量定制。

- 标签(Tabs):

标准标签使用Bootstrap标签标记。请参阅Bootstrap Tabs模块部分的变量进行特定变量定制。

- 警报(Alerts):

消息提醒使用基本的Bootstrap brand的颜色。请参阅Bootstrap Alerts模块部分的变量进行特定变量定制。

- 复选框(Checkboxes):

Horizon使用图标字体表示复选框。为了定制这个,你只需要重写标准的说明。例子参考:themes/material/static/horizon/components/_checkboxes.scss

- bootswatch和Material设计(Bootswatch and Material Design):

bootswatch是一个Bootstrap免费主题可以使用在Horizon。

为了展示什么可以做,以提高现有的Bootstrap主题,Horizon现在包括一个次要主题,基于Google’s Material Design。

bootswatch提供了许多其他的主题,这一些主题与Horizon完全兼容,允许容易的切换和自定义。

- 开发技巧(Development Tips):

当开发用于Horizon一个新的主题,它要求该动态生成静态目录中的每个变化之后被清除和服务器重新启动。这并不总是理想。如果你想开发,而不是每次都重新启动服务器,建议你配置开发环境,不是在离线模式下运行。简单地配置在local_settings.py以下设置:

COMPRESS_OFFLINE = False
COMPRESS_ENABLED = False

四、更改站点标题(Changing the Site Title)

OpenStack的 Dashboard网站标题(即“OpenStack Dashboard”),可以通过在local_settings.py添加SITE_BRANDING参数的值进行覆盖。
文件local_settings.py在Horizon上目录路径:
openstack_dashboard/local/local_settings.py

五、更改Brand链接(Changing the Brand Link)

Logo也用作超链接,默认是重定向到Horizon:user_home。通过在local_settings.py 设置SITE_BRANDING_LINK的值,例如:,超链接的目标可以改变。

六、自定义页脚(Customizing the Footer)

可以使用自定义主题的模板覆盖全局和登录页脚。只需添加_footer.html为全局页脚覆盖或_login_footer.html登录页面的页脚在你的主题模板目录。

七、修改现有的Horizon和Panels(Modifying Existing Dashboards and Panels)

如果你想改变dashboards或panels,成为你代码库的一部分,你可以指定整个Horizon网站被初始化后,载入一个自定义的Python模块,在URLconf建立前。常见的定制站点的需求:

  • 从现有的dashboard注册或注销panels。
  • 改变dashboards和panels的名字。
  • dashboards或panel group中panels重新排序。

加载默认Horizon panels基于openstack_dashboard/enabled/ 文件夹内的文件。如果有多个文件,这些文件加载基于文件名顺序。默认文件夹中提供了一些示例文件,有.EXAMPLE后缀的文件。开发人员和部署人员应该努力尽可能地使用这种方法定制,并支持这个偏好给出更的方法如:monkey patch (猴子补丁) 用来在运行时动态修改已有的代码,而不需要修改原始代码和覆盖文件。

八、Horizon定制/重写模块(Horizon customization module (overrides))

Horizon有一个全局覆盖机制可进行自定义,尚未定制通过配置设置。这个文件可以通过启用文件夹的自定义方法来执行猴子补丁和其他形式的定制。
这种方法的定制是部署和使用Horizon,应该不惜一切代价避免horizon插件。插件需要这个级别的猴子补丁和灵活性,应该寻找改变自己的init.py文件和通过其他方式进行自定义。

指定包含你修改的Python模块,在local_settings.py HORIZON_CONFIG字典添加关键customization_module。该值应包含你的python模块路径的字符串。 例:

HORIZON_CONFIG = {
    "customization_module": "my_project.overrides"
}

你可以做任何你喜欢的模块定制。例如,可以更改panel的名称:

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")

或获得panel实例:

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

并限制访问Keystone Admin角色的用户:

permissions = list(getattr(instances_panel, 'permissions', []))
('')
instances_panel.permissions = tuple(permissions)

或者完全删除它:

projects_dashboard.unregister(instances_panel.__class__)

你不能注销default_panel。如果你想删除一个默认的面板,你需要做一个不同的面板作为default_panel,然后注销前者。

例如,如果你希望从项目中删除overview_panel dashboard,你可以做以下修改:

project = horizon.get_dashboard('project')
project.default_panel = "instances"
overview = project.get_panel('overview')
project.unregister(overview.__class__)

你还可以用自己的版本重写现有的方法:

# Disable Floating IPs
from openstack_dashboard.dashboards.project.access_and_security import tabs
from openstack_dashboard.dashboards.project.instances import tables

NO = lambda *x: False

tabs.FloatingIPsTab.allowed = NO
tables.AssociateIP.allowed = NO
tables.SimpleAssociateIP.allowed = NO
tables.SimpleDisassociateIP.allowed = NO

你也可以定制哪些列显示在一个现有表,通过重新定义的columns属性的 Meta class。这可以通过3个步骤实现:
1、扩展你希望修改的表
2、在新表的Meta class下重新定义columns属性
3、修改有关table_class属性使其指向新的表
例如,如果你想从NetworksTable删除Admin State列,你可以做以下:

from openstack_dashboard. import tables
from openstack_dashboard. import views

class MyNetworksTable():

    class Meta():
        columns = ('name', 'subnets', 'shared', 'status')

.table_class = MyNetworksTable

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

注意:my_project.overrides必须通过运行Horizon Python过程可导入。如果你的模块没有安装为一个系统范围的python包,你可以使它安装(例如:运行)或者你可以调整WSGI服务器所使用的python路径包括它的位置。
也许最简单的方法是在Apache配置文件的WSGIDaemonProcess Horizon添加一个python-path路径参数。
假如你的my_project模块在 /opt/python/my_project,你要使其看起来像下面这样:

WSGIDaemonProcess [... existing options ...] python-path=/opt/python

九、图标(Icons)

Horizon 使用 Font Awesome字体图标,在代码中如何使用,请参阅Font Awesome。
Table Action添加图标,使用图标属性。例子:

class CreateSnapshot(tables.LinkAction):
name = “snapshot” verbose_name = _(“Create Snapshot”) icon = “camera”

此外,站点范围内的默认按钮类可以通过设置ACTION_CSS_CLASSES,在你的local_settings.py文件中的所有操作按钮类的元组进行配置。

十、自定义样式表(Custom Stylesheets)

自定义dashboards样式表。Horizon的基本模板openstack_dashboard/templates/定义了多个blocks,可以覆盖。

定义自定义css文件仅适用于一个特定的dashboards,dashboards的模板文件夹中创建一个基础模板,继承Horizon的基本模板如openstack_dashboard/dashboards/my_custom_dashboard/ templates/my_custom_dashboard/。

在这个模板中,重新定义CSS block。 (不要忘了包含,其中包括所有的Horizon的默认样式表。):

{% extends '' %}

{% 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 %}

自定义样式表,然后添加到dashboard自己的静态文件夹openstack_dashboard/dashboards/my_custom_dashboard/static/my_custom_dashboard/scss/my_custom_dashboard.scss。

所有dashboard的模板必须从dashboard的继承:

{% extends 'my_custom_dashboard/' %}
...

十一、自定义JavaScript(Custom Javascript)

同样,添加自定义样式(见上文),它可以包括自定义的JavaScript文件。

所有Horizon默认的JavaScript文件在openstack_dashboard/templates/horizon/_scripts.html 模板中定义引用。

添加自定义的JavaScript文件,在你的dashboard openstack_dashboard/dashboards/my_custom_dashboard/templates/my_custom_dashboard/_scripts.html 继承horizon/_scripts.html创建一个_scripts.html。在此模板覆盖 block custom_js_files包含你的自定义JavaScript文件:

{% 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/ 中的 block js:

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

其结果生成一个由horizon js脚本和dashboard自定义js脚本压缩的一个js文件。

此外,一些其他地方要求把他们放到页面的<head>标签内。要做到这一点,将它们放置在horizon/_custom_head_js.html 文件中。类似上面提到的_scripts.html文件:

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

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

<script type="text/javascript">
//some javascript
</script>

十二、定制的元数据属性(Customizing Meta Attributes)

要添加自定义元数据属性到项目的基本模板,包括在horizon/_custom_meta.html文件。这个文件的内容将只是默认Horizon meta标签后插入到页面的<HEAD>