Horizon吊炸天!之前,一直认为horizon只不过是一个面板,没啥好研究的,而且我对django又不是很熟,一直懒的看horizon,今晚硬着头皮看了下去,没想到,越看越有劲,眼睛差点跟不上我的思路了!我觉得horizon牛不在对django的运用,而是对事物高度的抽象能力:D
程序的入口点在horizon/openstack_dashboard/urls.py中:
url(r'', include(horizon.urls))
然后由Horizon这个单例的Site对象,开始加载urls,自动发现并注册dashboard,然后对每一个dashboard,再自动发现并注册panel:
Horizon._lazy_url
程序的入口看起来那么平常不过,进入之后发现这大千世界,让人惊叹不已!!!
可以在horizon/base/_urls()方法中,一窥全貌:
```
def _urls(self):
"""Constructs the URLconf for Horizon from registered Dashboards."""
urlpatterns = self._get_default_urlpatterns()
self._autodiscover() # 自动发现并注册dashboard,其实就是从INSTALL_APPS中加载dashboard
# Discover each dashboard's panels.
for dash in self._registry.values():
dash._autodiscover() # 自动发现并注册panel,这个是根据路径来发现的
# Allow for override modules
if self._conf.get("customization_module", None):
customization_module = self._conf["customization_module"]
bits = customization_module.split('.')
mod_name = bits.pop()
package = '.'.join(bits)
mod = import_module(package)
try:
before_import_registry = copy.copy(self._registry)
import_module('%s.%s' % (package, mod_name))
except Exception:
self._registry = before_import_registry
if module_has_submodule(mod, mod_name):
raise
# Compile the dynamic urlconf.
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.defaults.include
return urlpatterns, self.namespace, self.slug
```
默认的,一个HorizonSite中有三个Dashboard: project, admin, setting,在HorizonSite的autodiscover过程中,注册在HorizonSite中:
```
(Pdb) pp self._registry
{<class 'openstack_dashboard.dashboards.project.dashboard.Project'>: <Dashboard: project>,
<class 'openstack_dashboard.dashboards.admin.dashboard.Admin'>: <Dashboard: admin>,
<class 'openstack_dashboard.dashboards.settings.dashboard.Settings'>: <Dashboard: settings>}
```
然后再对每一个Dashboard进行autodiscover,将Panel注册在Dashboard中,从上面的类图中可以看到Dashboard和HorizonSite都是Registry的子类,它们都具有**注册**的能力:
```
(Pdb) pp self._registry.values()[0]._registry
{<class 'openstack_dashboard.dashboards.project.overview.panel.Overview'>: <Panel: overview>,
<class 'openstack_dashboard.dashboards.project.containers.panel.Containers'>: <Panel: containers>,
<class 'openstack_dashboard.dashboards.project.instances.panel.Instances'>: <Panel: instances>,
<class 'openstack_dashboard.dashboards.project.network_topology.panel.NetworkTopology'>: <Panel: network_topology>,
<class 'openstack_dashboard.dashboards.project.images_and_snapshots.panel.ImagesAndSnapshots'>: <Panel: images_and_snapshots>,
<class 'openstack_dashboard.dashboards.project.networks.panel.Networks'>: <Panel: networks>,
<class 'openstack_dashboard.dashboards.project.access_and_security.panel.AccessAndSecurity'>: <Panel: access_and_security>,
<class 'openstack_dashboard.dashboards.project.firewalls.panel.Firewall'>: <Panel: firewalls>,
<class 'openstack_dashboard.dashboards.project.volumes.panel.Volumes'>: <Panel: volumes>,
<class 'openstack_dashboard.dashboards.project.routers.panel.Routers'>: <Panel: routers>,
<class 'openstack_dashboard.dashboards.project.loadbalancers.panel.LoadBalancer'>: <Panel: loadbalancers>,
<class 'openstack_dashboard.dashboards.project.vpn.panel.VPN'>: <Panel: vpn>,
<class 'openstack_dashboard.dashboards.project.database_backups.panel.Backups'>: <Panel: database_backups>,
<class 'openstack_dashboard.dashboards.project.stacks.panel.Stacks'>: <Panel: stacks>,
<class 'openstack_dashboard.dashboards.project.databases.panel.Databases'>: <Panel: databases>}
```
在注册每一个Panel的时候,还会将每一个Panel的template路径,保存到loaders模块中:
```
(Pdb) pp loaders.panel_template_dirs
{'admin/defaults': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/defaults/templates',
'admin/flavors': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/flavors/templates',
'admin/hypervisors': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/hypervisors/templates',
'admin/images': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/images/templates',
'admin/info': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/info/templates',
'admin/instances': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/instances/templates',
'admin/metering': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/metering/templates',
'admin/networks': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/networks/templates',
'admin/overview': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/overview/templates',
'admin/projects': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/projects/templates',
'admin/routers': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/routers/templates',
'admin/users': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/users/templates',
'admin/volumes': '/opt/stack/horizon/openstack_dashboard/dashboards/admin/volumes/templates',
'project/access_and_security': '/opt/stack/horizon/openstack_dashboard/dashboards/project/access_and_security/templates',
'project/containers': '/opt/stack/horizon/openstack_dashboard/dashboards/project/containers/templates',
'project/database_backups': '/opt/stack/horizon/openstack_dashboard/dashboards/project/database_backups/templates',
'project/databases': '/opt/stack/horizon/openstack_dashboard/dashboards/project/databases/templates',
'project/firewalls': '/opt/stack/horizon/openstack_dashboard/dashboards/project/firewalls/templates',
'project/images_and_snapshots': '/opt/stack/horizon/openstack_dashboard/dashboards/project/images_and_snapshots/templates',
'project/instances': '/opt/stack/horizon/openstack_dashboard/dashboards/project/instances/templates',
'project/loadbalancers': '/opt/stack/horizon/openstack_dashboard/dashboards/project/loadbalancers/templates',
'project/network_topology': '/opt/stack/horizon/openstack_dashboard/dashboards/project/network_topology/templates',
'project/networks': '/opt/stack/horizon/openstack_dashboard/dashboards/project/networks/templates',
'project/overview': '/opt/stack/horizon/openstack_dashboard/dashboards/project/overview/templates',
'project/routers': '/opt/stack/horizon/openstack_dashboard/dashboards/project/routers/templates',
'project/stacks': '/opt/stack/horizon/openstack_dashboard/dashboards/project/stacks/templates',
'project/volumes': '/opt/stack/horizon/openstack_dashboard/dashboards/project/volumes/templates',
'project/vpn': '/opt/stack/horizon/openstack_dashboard/dashboards/project/vpn/templates',
'settings/password': '/opt/stack/horizon/openstack_dashboard/dashboards/settings/password/templates',
'settings/user': '/opt/stack/horizon/openstack_dashboard/dashboards/settings/user/templates'}
```
接下来就是最关键的urlpatterns,整个horizon所有的路径,并不是写到一个url.py文件中的,而是分散在每一个dashboard, 每一个panel中,有层级的关系。首先找dashboard的url,到每一个dashboard,再找每一个panel的url,所以url的路径关系为: ./dashboard/panel
urlpatterns in site:
```
(Pdb) pp urlpatterns
[<RegexURLPattern user_home ^home/$>,
<RegexURLPattern jsi18n ^i18n/js/(?P<packages>\S+?)/$>,
<RegexURLPattern set_language ^i18n/setlang/$>,
<RegexURLResolver <module 'django.conf.urls.i18n' from '/usr/local/lib/python2.7/dist-packages/django/conf/urls/i18n.pyc'> (None:None) ^i18n/>,
<RegexURLPattern qunit_tests ^qunit/$>,
<RegexURLResolver <RegexURLResolver list> (admin:admin) ^admin/>,
<RegexURLResolver <RegexURLResolver list> (settings:settings) ^settings/>,
<RegexURLResolver <RegexURLResolver list> (project:project) ^project/>]
```
urlpatterns in dashboard
```
(Pdb) pp urlpatterns[-1].urlconf_name
[<RegexURLResolver <RegexURLPattern list> (instances:instances) ^instances/>,
<RegexURLResolver <RegexURLPattern list> (routers:routers) ^routers/>,
<RegexURLResolver <RegexURLPattern list> (databases:databases) ^databases/>,
<RegexURLResolver <RegexURLPattern list> (firewalls:firewalls) ^firewalls/>,
<RegexURLResolver <RegexURLPattern list> (containers:containers) ^containers/>,
<RegexURLResolver <RegexURLPattern list> (access_and_security:access_and_security) ^access_and_security/>,
<RegexURLResolver <RegexURLPattern list> (network_topology:network_topology) ^network_topology/>,
<RegexURLResolver <RegexURLPattern list> (database_backups:database_backups) ^database_backups/>,
<RegexURLResolver <RegexURLPattern list> (loadbalancers:loadbalancers) ^loadbalancers/>,
<RegexURLResolver <RegexURLPattern list> (vpn:vpn) ^vpn/>,
<RegexURLResolver <RegexURLPattern list> (volumes:volumes) ^volumes/>,
<RegexURLResolver <RegexURLPattern list> (stacks:stacks) ^stacks/>,
<RegexURLResolver <RegexURLPattern list> (networks:networks) ^networks/>,
<RegexURLResolver <RegexURLPattern list> (images_and_snapshots:images_and_snapshots) ^images_and_snapshots/>,
<RegexURLResolver <RegexURLPattern list> (overview:overview) >]
```
urlpatterns in panel
```
(Pdb) pp urlpatterns[-1].urlconf_name[-1].urlconf_name
[<RegexURLPattern index ^$>, <RegexURLPattern warning ^warning$>]
```