openstack horizon 学习(2) navigation、dashboard、panels
本章的主要内容是如何用horizon的navigation结构添加一个应用的面板。
Horizon中提供了两种为应用添加panel的方法,一种是通过Pluggable Settings的方式,另一种是Django’s INSTALLED_APPS setting 方式。方式一是Horizon在Django框架基础上自主开发的方式,openstack中horizon的指导文档推荐使用方法一。而两种方式从功能上是等效的。本章重点分析pluggable setting这种方式,来看看它有多么cool吧。
当同时使用两种方式(setting dashboard和pluggable dashboard)时,setting dashboard的优先级是大于pluggable dashboard的,这个细节了解即可。为了避免不必要的麻烦,尽量不要两种方式混用。
先写一句总结的话,无论是添加一个Dashboard还是Panelgroup还是Panel。两步走:1.配置 2.根据配置填写相应的实例
然后不要急,在写如何添加dashboard和panel之前,先看看它们之间的关系:
panelgroup和panel一定是在某个dashboard下的,也就是创建一个panelgroup和panel必须指定一个已经存在了的dashboard。而panel指定panelgroup这个过程是不必须的,也就是panel可以直接在某个dashboard下。
接下来看添加dashboard、panelgroup、panel的共同步骤“配置”是什么。
引用官方文档中的说明:
Horizon allows dashboards, panels and panel groups to be added without modifying the default settings. Pluggable settings are a mechanism to allow settings to be stored in separate files. Those files are read at startup and used to modify the default settings.
The default location for the dashboard configuration files is openstack_dashboard/enabled, with another directory, openstack_dashboard/local/enabled for local overrides. Both sets of files will be loaded, but the settings in openstack_dashboard/local/enabled will overwrite the default ones. The settings are applied in alphabetical order of the filenames. If the same dashboard has configuration files in enabled andlocal/enabled, the local name will be used. Note, that since names of python modules can’t start with a digit, the files are usually named with a leading underscore and a number, so that you can control their order easily.
简单解释一下就是:每当开发者想要自主添加一个一个dashboard或panel时,可以不修改openstack_dashboard这个django应用的settings文件。代替之的操作是在openstack_dashboard下的enabled或openstack_dashboard/local/enable下写一个相应的python文件,告诉horizon在渲染dashboard时,请把这两个目录下添加的dashboard和panel也加进去。其中openstack_dashboard/local/enabled是对openstack_dashboard/enabled的一个override,如果在两个目录中配置了相同的dashboard,local下注册的dashboard是最终生效的。enable目录下的dashboard和panel是按照注册时填写的python文件的文件名的字典序添加的,所以当你看到enable下的目录python文件开头是_10_*.py _20_*.py,就应该不感到奇怪了吧,它完全是为了易于控制文件名的字典序而这样命名的。
接下来先看如何添加一个dashboard:
步骤1 “配置”:
在openstack_dashboard/local/enable 创建一个python文件,如_04_test_dashboard.py:
1 # The slug of the dashboard to be added to HORIZON['dashboards']. Required. 2 DASHBOARD = 'test_dashboard' 3 4 # A list of applications to be added to INSTALLED_APPS. 5 ADD_INSTALLED_APPS = [ 6 'openstack_dashboard.dashboards.test', 7 ]
分析一下,首先需要填写'DASHBOARD'这个属性,它实际上是你想要添加的dashboard的slug。啰嗦一句,slug可以把它看成是key,dashboard的slug就是相当于标识了这个dashboard,而它和dashboar具体在浏览器中显示的名字不是一码事。然后另一个比较重要的就是'ADD_INSTALLED_APPS'这个列表,这个列表的作用是指定这个dashboard下的application的目录列表。为什么dashboard会跟APP联系?因为在horizon看来,一个panel和一个APP是对应的,而dashboard下是一组panel的集合,所以指定这个list是理所当然的了。APP_INSTALLED_APPS好像可以不必须指定,而博主目前不知道什么情况下不需要指定ADD_INSTALLED_APPS,所以这个基本也是必须的。
可能看到这里,有人会好奇究竟这个配置文件可以配置哪些内容。我暂且罗列一些可以配置的属性,而不具体一一分析。因为这样的属性实在太多了,多数暂时也用不到。本章旨在解决主要的问题——dashboard和panel的添加原理。如果对这些配置属性感兴趣,可以去horizon的开发主页去查。
following keys can be used in any pluggable settings file:
1 ADD_EXCEPTIONS 2 A dictionary of exception classes to be added to HORIZON['exceptions']. 3 4 ADD_INSTALLED_APPS 5 A list of applications to be prepended to INSTALLED_APPS. This is needed to expose static files from a plugin. 6 7 ADD_ANGULAR_MODULES 8 A list of AngularJS modules to be loaded when Angular bootstraps. These modules are added as dependencies on the root Horizon application hz. 9 10 ADD_JS_FILES 11 A list of javascript source files to be included in the compressed set of files that are loaded on every page. This is needed for AngularJS modules that are referenced in ADD_ANGULAR_MODULES and therefore need to be included in every page. 12 13 ADD_JS_SPEC_FILES 14 A list of javascript spec files to include for integration with the Jasmine spec runner. Jasmine is a behavior-driven development framework for testing JavaScript code. 15 16 ADD_SCSS_FILES 17 A list of scss files to be included in the compressed set of files that are loaded on every page. We recommend one scss file per dashboard, use @import if you need to include additional scss files for panels. 18 19 AUTO_DISCOVER_STATIC_FILES 20 If set to True, JavaScript files and static angular html template files will be automatically discovered from the static folder in each apps listed in ADD_INSTALLED_APPS. 21 22 JavaScript source files will be ordered based on naming convention: files with extension .module.js listed first, followed by other JavaScript source files. 23 24 JavaScript files for testing will also be ordered based on naming convention: files with extension .mock.js listed first, followed by files with extension .spec.js. 25 26 If ADD_JS_FILES and/or ADD_JS_SPEC_FILES are also specified, files manually listed there will be appended to the auto-discovered files. 27 28 DISABLED 29 If set to True, this settings file will not be added to the settings. 30 31 UPDATE_HORIZON_CONFIG 32 A dictionary of values that will replace the values in HORIZON_CONFIG.
仅写配置是不够的,因为horizon在渲染页面的时候要有一个dashboard实例才能够进行渲染。
步骤二 “填写实例”
先在openstack_dashboard/dashboards下新建一个名叫test的module,因为上面ADD_INSTALLED_APPS指定的路径是test。dashboard和panelgroup的实例一般是写在dashboard module下的dashboard.py这个文件下,于是/openstack_dashboard/test_dashboard/dashboard.py:
1 from django.utils.translation import ugettext_lazy as _ 2 import horizon 3 4 class Test(horizon.Dashboard): 5 name = _("TestDashboard") 6 slug = "test_dashboard" 7 panels = (TestPanels,) 8 default_panel = 'test' 9 permissions = ('openstack.roles.admin',) 10 11 horizon.register(Test)
分析一下,dashboard实例实际上就是horizon.Dashboard的一个子类,name属性即界面上显示这个dashboard实际的名称。slug属性前面介绍过,实例中的slug与配置文件中的slug是对应的,所以一定要注意保持一致。panels元组的意义比较明显,值得注意的是这里面的元素可以是panlegroup也可以是panel,TestPanel是一个panelgroup,具体定义见后文。default_panel意义也很明显,它需要指明一个panel的slug。permissions是权限控制,只有以admin身份登录才可以看到这个dashboard。permissions不是必须指明的。最后一步调用register实例化。
panelgroup的添加和dashboard是类似的,代码如下:
1.配置
/openstack_dashboard/local/enable/_05_test_panel_group.py
1 PANEL_GROUP = 'test_panel_group' 2 PANEL_GROUP_NAME = 'Test Panelgroup' 3 PANEL_GROUP_DASHBOARD = 'test_dashboard'
'PANEL_GROUP_DASHBOARD'是指定panelgroup是在哪个dashboard下的,属性的值应该是某个dashboard的slug
2.根据配置写实例
/openstack_dashboard/dashboards/test/dashboard.py
1 class TestPanels(horizon.PanelGroup): 2 slug = "test_panel_group" 3 name = _("TestPanelGroup") 4 panels = ('test',)
有了上面的基础,panel的添加也是类似的,不过panel逻辑上就是一个具体的应用。所以第二步的实例一般是写在具体应用目录的panel.py这个文件中的,先在/openstack_dashboard/dashboards/test下新建一个module,test1。
1.配置
/openstack_dashboard/local/enable/_06_test_panel.py
1 PANEL = 'test' 2 PANEL_DASHBOARD = 'test_dashboard' 3 PANEL_GROUP = 'test_panel_group' 4 ADD_PANEL = 'openstack_dashboard.dashboards.test.test1.panel.Test'
panel的配置跟dashboard和panelgroup不同,需要指定ADD_PANEL这个属性,这个属性的作用是指定第二步中的需要被实例的类名。如果不指明ADD_PANEL则不能正确添加这个panel。这里可能有人疑问为什么dashboard和panelgroup可以通过slug的对应关系找到实例的类,而panel不能效法这么去做。这个问题我也是疑问的,想要搞明白还需再往深层看,问题暂时先放这里,欢迎大牛指点。
2.根据配置写实例
/openstack_dashboard/dashboards/test/test1/panel.py
1 from django.utils.translation import ugettext_lazy as _ # noqa 2 import horizon 3 from openstack_dashboard.dashboards.admin import dashboard 4 5 class Test(horizon.Panel): 6 name = _("Test") 7 slug = 'test' 8 permissions = ('openstack.roles.admin',) 9 10 dashboard.Admin.register(Test)
最后随便在test1这个module下的urls.py和views.py随便写点视图层代码,更复杂的视图层逻辑将在后面章节讲解。
最后我们来启动server看一下效果:
在最最后,博主做了个实验,分析了一下Pluggable Settings这种添加dashboard和panel方式到底做了什么。
首先,在server启动前,找到openstack_dashboard里的settings.py里的INSTALLED_APPS列表,看看它都有什么:
然后在server启动后,写了一个视图函数display_view:
1 from django import http 2 from openstack_dashboard import settings 3 4 def display_view(request): 5 apps = settings.INSTALLED_APPS 6 apps.sort() 7 html = [] 8 for i in apps: 9 html.append('<tr><td>APP:</td><td>%s</td></tr>' % i) 10 return http.HttpResponse('<head>print INSTALLED_APPS:</head> <table>%s</table>' % '\n'.join(html))
打印下目前的INSTALLED_APPS变成了什么:
INSTALLED_APPS列表在server启动后,界面渲染前,添加了一些新的项。而这些项与horizon中的dashboard是一一对应的,刚刚写的test也里面。所以稍加分析和思考不难知道,其实Pluggable Settings这种方式是在INSTALLED_APPS读取前先读取openstack_dashboard/enable和openstack_dashboard/local/enable中的.py配置文件,解析这些配置,然后动态添加到INSTALLED_APPS中,再渲染界面。它的实质与直接修改配置文件中的INSTALLED_APPS本质并无区别。
但不觉得可扩展性提高了么?加入这么一个看似简单的机制,所有APP变得像零件一样变得可拔插。不觉得有些函数式编程的味道么,即便是修改或删除dashboard或panel,也可以不修改代码,而是定义一个新的配置。扯多了……
本章讲的很粗略,很多内容写的并不完善,可能也有很多错误在里面,通过以后的学习再慢慢细化和改正。