Horizon Is Easy, Horizon Is Complex
本文出自我的同事兼基友@monsterxx03 之手,本人稍作润色
Horizon Is Easy, Horizon Is Complex
如果要用一句话来概括Openstack Dashboard项目Horizon:它是一个基于django webframework开发的标准的python wsgi程序。如果要再加一句废话,它一般会运行在webserver(apache/nginx)上。
Why Is It Easy?
说Horizon简单,是指它的部署架构简单,就是个单纯的基于django的网站,后台甚至没有使用数据库。不像Swift,Nova这些项目,有非常多的组件(xxx-server,xxx-api…),理清楚各个组件之间的关系,数据流的走向都要费一番功夫。Horizon用到的技术也比较传统,没有异步非阻塞,消息队列,websocket这些比较时髦的东西(目前没有),前端也只是bootstrap+jquery+less,没有什么很前卫的东西。
用户从登陆到看到虚拟机列表的这个过程的数据流走向:
Why Is It Complex?
Horizon的复杂性分成两方面看:
- Horizon需要和几乎所有的OpenStack service api打交道, 而且各个服务可能就还有多个api版本,api多版本的兼容不是都能在client里完成的(比如Keystone),Horizon还需要针对各个api的多版本做兼容。
- Horizon代码本身很复杂,抽象,利用了django相当多的高级特性,可以说把oop的概念运用到了极致,但这也为过度自定制Horizon带来了麻烦。
Design Philosophy
Horizon是基于django开发的,要说Horizon的设计,不得不说django,
Django focuses on automating as much as possible and adheres to the DRY(Don't Repat Yourself) principle.
django的设计专注于代码的高度可重用,信奉DRY原则,一切面向对象,而Horizon可以说高度match了django的设计风格,good or bad就见仁见智了。
先说一点web开发的常识,网站程序基本有三部分组成,业务逻辑代码(Python),静态文件(js/css),模板(Python中的jinja,mako,nodejs中有jade), 用户向webserver发起请求之后,server程序找到当前url对应的模板,填充模板变量(输出成字符串形式的html源码),返回给浏览器,浏览器渲染页面。
如果是简单的程序,一个页面就对应一个模板,非常简单。一般一个网站有自己的统一风格,所有页面都会有一些相同的元素,每个页面单独写一遍很累赘,也不好维护,所以一般模板语言都有继承(extend),插入(include)等特性,来提高页面的复用率。
Horizon做得就更彻底一些,它将页面上所有元素模块化,网页中一些常见元素,表单,表格,标签页,全部封装成Python类,每个组件有自己对应的一小块html模板.当渲染整个页面的时候,Horizon先找到当前页面有多少组件,将各个组件分别进行渲染变成一段html片段,最后拼装成一个完整的html页面,返回浏览器。
简单总结Horizon的特点:
- 页面元素模块化
- 子面板可插拔
- All in One(从部署上来说,Horizon只有它自己这一个组件)
Dive into Horizon
Some Concepts
Horizon这套面板的设计分成三层:Dashboard → PanelGroup → Panel
Horizon中现有的dashboard有4个:
- project 普通用户登陆后看到的项目面板
- admin 管理登陆后可见,左侧的管理员面板
- settings 右上角的设置面板,里面可设置语言,时区,更改密码
- router(配置文件中将profile_support打开可见),ciso nexus 1000v的管理面板
每一个dashboard都是django中的一个app,django中的app可以理解成对业务逻辑模块化的一种手段,里面可以包含自己独有的url设定,模板,和业务逻辑代码.
每个dashboard下定义了一系列的PanelGroup,虚拟机管理对应到界面上就是一个PanelGroup(Manage Compute), 里面有一系列的子panel(Overview, Instances, Volumes…)。Swift,heat,neutron的管理面板各自都是一个PanelGroup,底下有各自的子panel.
Some Code
Horizon的源码中,包含两个代码文件夹
- horizon
- openstack_dashboard
Horizon这个包是一些在django基础上写的通用组件,表格(table),标签页(tab),表单(form),面包屑导航(browser),工作流(workflow),这些代码和openstack的具体业务逻辑没有什么关系,如果做一个新的django项目,理论上可以复用Horizon这个包中的代码。horizon/base.py中还实现了一套dashboard/panel机制,使得Horizon面板上所有的dashboard都是”可插拔”的,所有的panel都是”动态加载”的。
openstack_dashboard/dashboards/中是各个面板的具体实现代码,其中包括各个面板的模板文件, 和后端service交互的业务逻辑代码等。
FAQ
Horizon怎么实现dashboard的可插拔?
之前说过,Horizon中的dashboard就是django的app,在openstack_dashboard/settings.py中的INSTALLED_APP变量定义了目前已有的四个dashboard:
- openstack_dashboard.dashboards.project
- openstack_dashboard.dashboards.admin
- openstack_dashboard.dashboards.settings
- openstack_dashboard.dashboards.router
如果自己按照Horizon自定制dashboard的流程写了新的dashboard也需要加到这个配置文件中,新面板就能正常显示。
每个dasboard是怎么找到自己拥有的panel的?
每个dashboard模块下都有一个dasbboard.py文件里面定义了属于当前dashboard的PanelGroup,和各个PanelGroup下的Panel,在Horizon模块被导入的时候会去依次遍历Dasboard→PanelGroup→Panel,所有的dashboard注册到Horizon命名空间下,各个panel注册到自己的dashboard命名空间下。
每个panel是怎么找到自己的模板的?
这需要理解django中的template loader概念,简单的说,template loader就是一个处理request的视图寻找自己的模板文件路径的方法,django自带了好几种template加载机制,Horizon用到了filesystem,app_directories这两种,另外自己自定义了一种(horizon.loaders.TemplateLoader),loader的定义在openstack_dashboard.settings中的TEMPLATED_LOADERS变量。
TEMPLATED_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
'horizon.loaders.TemplateLoader',
)
举例:
当点击左侧project(这是个dashboard)下的instance(这是个panel)标签时,页面会跳转到http://localhost/project/instances这个url,panel在的视图类中写了template_name=“project/instances/index.html”,查找这个文件的顺序是:
- 先找openstack_dashboard/templates
- 没找到再找openstack_dashboard/dashboards/project/templates/,这个目录不存在,还是没找到。
- 最后找openstack_dashboard/dashboards/project/instances/templates, 找了index.html这个文件。
怎么显示swift的面板?怎么显示heat的面板?
这里很好的体现了Horizon的动态特性,显不显示某panel,实际上并不由Horizon控制,而是由Keystone控制。一个后端的webservice需要集成进openstack的话,第一步是在Keyston处登记,在Keyston的service,endpoint这两张表内写入该service的的一些元数据信息(包括url地址)。
Horizon控制面板是否显示用了一套叫permission的机制,比如Container(swift的panel)要显示的条件是”openstack.services.object-store”。用户的登陆后的token中又service catalog信息,catalog中必须包含object-store这个service,满足,container这个panel就会注册到project这个dashboard之下,页面上就能显示,如果Keyston中没有swift的catalog信息,Horizon就不会注册container这个panel,swift的面板就不会显示。heat同理。
要了解permission的具体实现请看openstack_auth/backend.py 模块
普通用户看不到admin面板,这个是怎么实现的?
还是用到了上个问题提到的permission,Admin dashboard这个类中写的permission是”openstack.roles.admin”,则admin这个面板显示的条件是当前登录的用户有admin这个role,满足的用户就能看到admin dashbaord.
Horizon处理登录部分的代码在哪?
参看openstack_auth部分
Customize Horizon
自定义css
要对Horizon的css样式做自定制,可以修改openstack_dashboard/static/less 目录下的less文件。
自定义js
要对Horizon中js的行为做定制,可以修改horizon/static/horizon/js/ 目录下的文件,Horizon的js文件都是针对具体某个页面组件做的行为定制,比如form提交时的行为,tab切换的行为,所以都放在Horizon目录下而不在openstack_dashboard目录下。
添加新的dashboard和panel
Horizon自带了两条命令,方便快速得生成基础代码:
- `python manage.py startdash test_dash` 自动生成一个叫test_dash的dashboard,之后将test_dash写入settings中的INSTALLED_APP中,Horizon就能加载此新面板
- `python manage.py startpanel test_panel -d openstack_dashboard.dashboards.admin –target auto` 会在Horizon的admin dashboard下生成一个叫test_panel的子面板
Event Driven Horizon
Horizon目前的实现是比较传统的全刷新网站,openstack中有不少操作是异步的api,比如创建虚拟机,snapshot,volume等等。这些api被调用后资源会进入一个状态迁移的过程,Horizon在前端页面上实现状态的实时刷新,用的是ajax轮训的方式,这种方式效率比较低,也有延迟,目前也有bp要为Horizon引进real time的特性,相关bp:
有一个实现草案:https://review.openstack.org/#/c/40198/1
原理是通过sock.io(python版基于tornado的ioloop实现)再写一个websocket的server.同时Horizon后台添加消息队列支持,监听openstack集群中的消息,监听到了自己需要的消息之后,通知websocket server,server再通知浏览器中的js.
不过这样做有一些问题,server上需要躲开一个端口专门处理websocket请求,还需要一个daemon监听消息队列的消息,这会增加Horizon的部署复杂度。社区决定将real time的实现放到I版做,这个实现草案中将整个Horizon本身也跑在了tornado server上,这里的性能问题有待商榷(主要是对eventlet的monkey_patch的原理有疑惑)。
吐槽
本来想写基于Horizon开发会带来的不便利的地方,想了一下,先写些离题的东西。
首先喷一下django的DRY原则,很多程序员都追求自己的代码里没有冗余的代码,希望系统像一个精密的机械每个齿轮都紧紧得咬合,没有一个多余的零件,也不要出现两个相同的零件,这就是所谓的DRY(don't repeat yourself)。但是也有一句话流传的很广:“DRY有毒”。DRY是不是有毒,我觉得要看写的是什么程序,如果就是一个在后台默默跑着的server,它和外部交互的协议都是定死的,那把接口实现之后无论怎么重构代码,追求架构上的DRY都不过分。
但网站这种项目,终端消费者是人而不是其他程序,它和外部交互的协议就是html页面。人是最喜新厌旧的,所以会出现网站改版这种事,说白了网站程序它和外部的交互协议三天两头都有可能变化。当你费尽奇淫巧计,用上各种Design Pattern,OOP设计,最后老板说:“那什么,我想把这个页面的按钮做成大号的,而且点了之后框给我跳两下再出来”,就开始郁闷了,你和boss说,按照我的优良OOP设计,所有页面上的按钮都继承自一个BaseButton,他们的动画效果都是预先定义好的,按钮点了之后对话框能从上下左右出来,就是不能跳两下再出来,可以改,但是会破坏我原本的优良设计。喂喂,根本没人会来理你,看页面的人在乎的只是这个按钮而已。
可能你会说,这个例子里,我设计的按钮类不够强大,它应该写的更加抽象,子类的行为要更加灵活。但要知道,程序员能实现的架构的复杂度与层次性和个人的经验,项目所给的时间有相当大的关系。
说白了,就是水平不够,偏要去追求什么没有一行重复代码这种事情,而且给的时间只有个把月,最后的代码很DRY但是不够灵活,只会让自己陷入困境.把握程序的优雅与灵活之间的平衡是一件很难的事情,有时候要量力而行。
再回到Horizon这个项目,让我评价Horizon的代码架构本身(去除一些pep8的小问题,我个人并不是很待见pep8),我的评价是Excellent!反正在一些开源的基于django的程序的实现里,见过代码行数更多的,但没见过代码写的更漂亮的,至少是没见过比它用django用的更牛的。
但是Horizon也不是一开始就是这个架构,我记忆里E版的Horizon里那个DataTable的实现真的是百转千回,死活看不懂。现在在H版里的实现将DataTable这个组件进行了很详细的拆分,仍旧很复杂,但比以前的实现要清楚的多。
我曾经将Horizon的commit记录reset到第一条,看过它早期的一些实现变化过程,它早期的代码那叫个简陋,各种脏,各种修修补补,最早的commit记录是2011年1月,一直到现在2013年10月,将近3年的开发时间,一般公司,如果说我们做个面板,做个三年,最后做出来长Horizon这样,那肯定会有人跳出来说:“丫我拿php写一个,1个礼拜就能搞定,他妈你搞上三年,php是最好的语言,php万岁!”.
但Horizon的的价值对一般人来说在openstack api的可视化体现,对开发人员来说是优秀代码的学习范例,如果你用一种传统的方式去开发一个openstack面板,他可能可以把前端做的很炫,让Horizon显得很土鳖,但代码100%比不上Horizon,我的意见是你用django开发,后台写成Horizon这样,基本可以说是优秀到头了。
Horizon Is Easy, Horizon Is Complex的更多相关文章
- Apache下配置Openstack Horizon (转)
非常详尽的Horizon配置介绍,转自 dev.cloudwatt.com Deploy Horizon from source with Apache and SSL Some companies ...
- 【云计算】OpenStack Horizon DashBoard定制化,完整实现前后台交互
项目代码见GitHub:https://github.com/junneyang/openstack-customization-example 参考资料: Install and configure ...
- 怎样修改 Openstack Horizon(Dashboard)的显示界面 (二)
上一篇文章介绍了 Dashboard 的基本结构框架,那接下来的问题就是如何在这个框架中加入我们自己想要的内容了.在真正动手之前,让我们先来看看官方的页面是怎么做出来的.首先我们进入 /usr/sha ...
- puppet 部署 horizon server 所需的参数和部署逻辑
所需要的参数: $secret_key, $bind_address = '127.0.0.1', $cache_server_ip = '127.0.0.1', $cache_ser ...
- VMware vSphere 服务器虚拟化之十六 桌面虚拟化之VMware Horizon View
VMware vSphere服务器虚拟化之十六 桌面虚拟化之VMware Horizon View VMware Horizon View (原VMware View的升级版现在版本5.2)是 ...
- openstack horizon开发第一天
horizon插件构造 创建一个dashboardmkdir opesntack_dashboard/dashboards/mydashboardpython manage.py startdash ...
- horizon源码分析(一)
源码版本:H版 一.写在前面 本来应该搭建horizon的development环境的,这样方便debug,但是由于各种报错,本人没有搭建成功,这也导致有很多源码疑问没有解决,后续可以继续补充这一部分 ...
- CentOS 6.5 部署 Horizon
以root用户进行部署,python源也可以使用 http://mirrors.aliyun.com/pypi/simple/ 修改系统 更改SElinux的配置文件 /etc/selinux/con ...
- vmware 桌面虚拟化 horizon view 介绍(使用微软的RDP协议或vmware 专有的PCoIP协议,连接到虚拟桌面,并且可以使用本地的USB设备、本地存储)
虚拟化(一):虚拟化及vmware产品介绍 虚拟化(二):虚拟化及vmware workstation产品使用 虚拟化(三):vsphere套件的安装注意及使用 虚拟化(四):vsphere高可用功能 ...
随机推荐
- C#面向服务编程技术WCF从入门到实战演练
一.WCF课程介绍 1.1.Web Service会被WCF取代吗? 对于这个问题阿笨的回答是:两者在功能特性上却是有新旧之分,但是对于特定的系统,适合自己的就是最好的.不能哪一个技术框架和行业标准作 ...
- delphi Image处理
procedure ImageDrawText(ATextEdo: IGGCCADTextEDO); var oImageBitmap: TBitmap; x1,x2,y1,y2: double; b ...
- delphi teechrt中TChart 一些属性设置
把图片设置成黑白 2.设置颜色
- win8操作系统下使用telnet客户端
一.安装Telnet客户端 今天尝试在Win8操作系统下使用telnet客户端连接上搜狐的邮件服务器时,结果出现了'telnet' 不是内部或外部命令,也不是可运行的程序,如下图所示: 上网查了一下原 ...
- 哈希小demo hashCode取模
package demo; import java.util.ArrayList; import java.util.List; class Person { private String usern ...
- Linux驱动开发——指针和错误值
参考: <Linux设备驱动程序>第三版 P294 许多内部的内核函数返回一个指针值给调用者,而这些函数中很多可能会失败.在大部分情况下,失败是通过返回一个NULL指针值来表示的.这种技巧 ...
- Delphi 完全时尚手册之 Visual Style 篇 (界面不错) 转自http://blog.csdn.net/iseekcode/article/details/4733229
这里先说说两个概念:Theme(主题)和 Visual Style .Theme 最早出现在 Microsoft Plus! for Windows 95 中,是 Windows 中 Wallpape ...
- Odoo(OpenERP)应用实践:代发货管理
原文地址:http://blog.csdn.net/wangnan537/article/details/47091857 有些分销商,在买家下单后才向供应商采购产品,并由供应商直接发货给客户.这种模 ...
- 利用MPMoviePlayerViewController 播放视频 iOS
方法一: @property (nonatomic, strong) MPMoviePlayerController *player; NSString *url = [[NSBundle mainB ...
- 百度搜索推出惊雷算法严厉打击刷点击作弊行为-SEO公司分享
百度搜索推出惊雷算法严厉打击刷点击作弊行为 2017年11月20日凌晨,百度搜索引擎发布更新惊雷算法旨在打击刷点击作弊行为. 下面是惊雷算法相关新闻报道: 百度搜索将于11月底推出惊雷算法,严厉打击通 ...