horizon源码分析(一)
源码版本:H版
一、写在前面
二、horizon前端请求
主要用浏览器chrome的开发者工具分析页面发出的具体请求,此处是分析前端的一个button按钮执行情况。
请求链接地址:
其他相关元素:
总结如下:
三、URL后端绑定及APP的加载
根据Django的框架结构,使用URLconf文件(https://docs.djangoproject.com/en/1.4/topics/http/urls/)进行链接请求和后端处理(view)的绑定,使用view进行后端处理,使用template进行页面渲染。
1、目录结构:
horizon-------------组件,提供部分功能
|---__init__.py-----------控制着horizon包导入行为
|---base.py-----------horizon提供的Site类、Dashboard类、Panel类,负责整个基本架构
|---site_urls.py
openstack_dashboard----------------网站project根目录
|---settings.py------------网站基本设置【1】
|---urls.py----------------网站基本URL设置
|---views.py---------------网站基本view
|---templates--------------网站基本template
|---dashboards
|---admin ---------------dashboard【2】
|---instances --------------panel【3】
|---panel.py----------------------负责往dashboard中注册panel
|---urls.py
|---views.py
...
|---dashboard.py --------------负责往Horizon中注册dashboard
|---models.py
...
Horizon项目架构:主要分为两个部分:horizon和openstack_dashboard。horizon提供库和功能组件,openstack_dashboard是一个使用了horizon的Django项目。

2、URL绑定分析:
ROOT_URLCONF = 'openstack_dashboard.urls'
openstack_dashboard/urls.py
这里使用了一个小trick,在导入horizon这个package时,进行如下处理:
可见,在package的__init__.py中控制包的导入行为,这样可以使包的使用更加简洁方便。接着分析如下:
part2:
part3:
综上所述,最终的include()导入的是Site类的_lazy_urls。
3、Site、Dashboard、Panel三者的加载
这里先说明Site、Dashboard和Panel三者的加载过程以便后面的进一步分析。Horizon采用了注册机制,即以一个对象为根对象,将其他组件注册到根对象的属性中,这种机制使得软件的可扩展性更强,并且条理清晰,貌似像一种设计模式来着。Horizon是一个Site类对象,往其中注册Dashboard类时会构建一个Dashboard对象注册到其属性_registry字典中;Panel类往Dashboard类中注册,注册时会构建Panel对象注册到Dashboard对象的_registry字典里。具体情况如下:
3.1 一个dashboard注册过程
import horizon class SystemPanels(horizon.PanelGroup):
slug = "admin"
name = _("System Panel")
panels = ('overview', 'metering', 'hypervisors', 'instances', 'volumes', 'flavors', 'images', 'networks', 'routers', 'defaults', 'info') class IdentityPanels(horizon.PanelGroup):
slug = "identity"
name = _("Identity Panel")
panels = ('domains', 'projects', 'users', 'groups', 'roles') class Admin(horizon.Dashboard):
name = _("Admin")#用于display
slug = "admin"#用于内部引用
"""panels可以用来发现与该Dashboard相关的所有Panel,以便在以后往Dashboard中注册这些Panel"""
panels = (SystemPanels, IdentityPanels)
default_panel = 'overview'
permissions = ('openstack.roles.admin',) horizon.register(Admin)
horizon/base.py
Site类:
def register(self, dashboard):
"""Registers a :class:`~horizon.Dashboard` with Horizon."""
return self._register(dashboard)
Registry类:
def _register(self, cls):
if not inspect.isclass(cls):
raise ValueError('Only classes may be registered.')
elif not issubclass(cls, self._registerable_class):
raise ValueError('Only %s classes or subclasses may be registered.'
% self._registerable_class.__name__) if cls not in self._registry:
cls._registered_with = self
self._registry[cls] = cls() return self._registry[cls]
在horizon/base.py中的Horizon对象的_registy映射中添加了Dashboard类à类实例的映射!
3.2 一个panel的注册过程
以admin这个Dashboard中的instancs为例:
import horizon
"""将admin这个Dashboard的dashboard.py导入"""
from openstack_dashboard.dashboards.admin import dashboard class Aggregates(horizon.Panel):
name = _("Host Aggregates")
slug = 'aggregates'
permissions = ('openstack.services.compute',) """在Dashboard为Admin中进行注册"""
dashboard.Admin.register(Aggregates)
horizon/base.py
Dashboard类:
@classmethod
def register(cls, panel): """检查cls是否已经注册到Horizon对象中,并且在cls中注册panel"""
panel_class = Horizon.register_panel(cls, panel)
panel_mod = import_module(panel.__module__)
panel_dir = os.path.dirname(panel_mod.__file__)
template_dir = os.path.join(panel_dir, "templates")
if os.path.exists(template_dir):
key = os.path.join(cls.slug, panel.slug)
loaders.panel_template_dirs[key] = template_dir
return panel_class
panel的注册会先从Horizon对象中找出对应的已经注册的Dashboard对象,然后在该Dashboard对象里面注册Panel,注册过程和Dashboard的注册过程类似。
Site类:
def _urls(self): """实际调用HorizonComponent._get_default_urlpatterns,获取site_urls.py中的urlpatterns"""
urlpatterns = self._get_default_urlpatterns()【1】 """实际调用Site._autodiscover ,导入openstack_dashboard/settings.py中的HORIZON_CONFIG, INSTALLED_APPS配置的模块中的dashboard.py和panel.py,此处会进行Dashboard的注册"""
"""可参考文档:http://docs.openstack.org/developer/horizon/topics/settings.html"""
self._autodiscover()【2】 """发现每个Dashboard中的Panel,导入每个Panel的panel.py,从而注册每个Panel"""
for dash in self._registry.values():
dash._autodiscover()【】 ... # Compile the dynamic urlconf.
"""dash为Dashboard类"""
for dash in self._registry.values():
urlpatterns += patterns('',
url(r'^%s/' % dash.slug, include(dash._decorated_urls))) # Return the three arguments to django.conf.urls.include
return urlpatterns, self.namespace, self.slug
Dashboard类:
@property
def _decorated_urls(self): urlpatterns = self._get_default_urlpatterns()【1】
default_panel = None
# Add in each panel's views except for the default view.
"""panel为Panel类"""
for panel in self._registry.values():
if panel.slug == self.default_panel:
default_panel = panel
continue
url_slug = panel.slug.replace('.', '/')
urlpatterns += patterns('',
url(r'^%s/' % url_slug, include(panel._decorated_urls)))
...
# Return the three arguments to django.conf.urls.include
return urlpatterns, self.slug, self.slug
Panel类:
@property
def _decorated_urls(self):
"""即查找panel的urls.py文件"""
urlpatterns = self._get_default_urlpatterns()【1】
# Apply access controls to all views in the patterns
permissions = getattr(self, 'permissions', [])
_decorate_urlconf(urlpatterns, require_perms, permissions)
_decorate_urlconf(urlpatterns, _current_component, panel=self)
# Return the three arguments to django.conf.urls.include
return urlpatterns, self.slug, self.slug
标记处解释如下:
def _autodiscover(self):
"""Discovers modules to register from ``settings.INSTALLED_APPS``.
This makes sure that the appropriate modules get imported to register
themselves with Horizon.
"""
if not getattr(self, '_registerable_class', None):
raise ImproperlyConfigured('You must set a '
'"_registerable_class" property '
'in order to use autodiscovery.') # Discover both dashboards and panels, in that order
for mod_name in ('dashboard', 'panel'):
"""settings.INSTALLED_APPS感觉应该和openstack_dashboard/settings.py中的
ORIZON_CONFIG, INSTALLED_APPS有关"""
for app in settings.INSTALLED_APPS:
mod = import_module(app)
try:
before_import_registry = copy.copy(self._registry)
import_module('%s.%s' % (app, mod_name))
except Exception:
self._registry = before_import_registry
if module_has_submodule(mod, mod_name):
raise
导入settings.INSTALLED_APPS中的各个模块中的dashboard.py或panel.py,举例说就是openstack_dashboard/dashboards中各个dashboard的dashboard.py,在导入dashboard.py或panel.py时会执行相应的注册行为
4、结论如下:
对于请求:
horizon源码分析(一)的更多相关文章
- openstack之horizon源码分析
一.基础准备: Horizon是基于django webframework开发的标准的Python wsgi程序,django的设计专注于代码的高度可重用,信奉DRY原则,一切面向对象,而Horizo ...
- horizon源码分析(二)
源码版本:H版 一.简要回顾 对于请求: 地址:/dashboard/admin/instances/ 方式:POST 参数: instances_filter_q: action:instances ...
- openstack之horizon源码分析之二
一.概述: django基础入手: django新建project:#django-admin startproject mysite 生成如下目录: mysite ├── manage.py └── ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
- zookeeper源码分析之五服务端(集群leader)处理请求流程
leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
- zookeeper源码分析之三客户端发送请求流程
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...
随机推荐
- 7.hdfs工作流程及机制
1. hdfs基本工作流程 1. hdfs初始化目录结构 hdfs namenode -format 只是初始化了namenode的工作目录 而datanode的工作目录是在datanode启动后自己 ...
- mac react-native从零开始android真机测试
1. 安装android相关jdk,(https://blog.csdn.net/vvv_110/article/details/72897142) 2. 手机和mac使用usb连接, 手机开发者设置 ...
- 动态规划——最长公共子序列LCS及模板
摘自 https://www.cnblogs.com/hapjin/p/5572483.html 这位大佬写的对理解DP也很有帮助,我就直接摘抄过来了,代码部分来自我做过的题 一,问题描述 给定两个字 ...
- C++:const_cast的简单理解
前言:const_cast是我比较头疼的一个知识点,最近查阅了很多资料,也翻看了很多他人的博客,故在此将自己目前学习到的有关const_cast知识做一个简单的总结 一.什么是const_cast 简 ...
- [图算法] 1003. Emergency (25)
As an emergency rescue team leader of a city, you are given a special map of your country. The map s ...
- Scrum 项目准备3.0
SCRUM 流程的步骤2: Spring 计划 1. 确保product backlog井然有序.(参考示例图1) 2. Sprint周期,一个冲刺周期,长度定为两周,本学期还有三个冲刺周期. Spr ...
- ant 安装及基础教程 !
这篇文章主要介绍了ant使用指南详细入门教程,本文详细的讲解了安装.验证安装.使用方法.使用实例.ant命令等内容,需要的朋友可以参考下 一.概述 ant 是一个将软件编译.测试.部署等步骤联系在 ...
- linux mysql表名大小写
1.用ROOT登录,修改/etc/my.cnf 2.在[mysqld]下加入一行:lower_case_table_names=1 0:区分大小写,1:不区分大小写 3.重新启动数据库即可
- nodejs之Buffer
Buffer是什么? 简单点理解,buff就是固定长度的uint8array.(es6已实现TypedArray). 由于是固定长度所以没有了splice,concat方法. 由于是固定类型所以没有了 ...
- hdu-题目1421:搬寝室
http://acm.hdu.edu.cn/showproblem.php?pid=1421 搬寝室 Time Limit: 2000/1000 MS (Java/Others) Memory ...