OpenStack Horizon: Controlling the Cloud using Django David Lapsley @devlaps, david.lapsley@metacloud.com August 21, 2014 OPENSTACK THAT JUST WORKS
OpenStack Horizon in Action OPENSTACK THAT JUST WORKS
Launching an Instance
Admin Overview
Project Overview
Launching an Instance
Launching an Instance
Launching an Instance
Launching an Instance
Launching an Instance
Launching an Instance
OpenStack Clouds Architecture and Model OPENSTACK THAT JUST WORKS
OpenStack Model http://docs.openstack.org/training-guides/content/module001-ch004-openstack-architecture.html http://docs.openstack.org/openstack-ops/content/example_architecture.html
OpenStack Projects ● Nova (Compute) ● Nova (Network) ● VM Registration (Glance) ● Identity (Keystone) ● Object Storage (Swift) ● Block Storage (Cinder) ● Dashboard (Horizon)
OpenStack Horizon Controlling the Cloud with Django OPENSTACK THAT JUST WORKS
Horizon Overview ● Django-based application deployed via Apache and WSGI ● Provides access to OpenStack services ● Leverages existing technologies o Bootstrap, jQuery, Underscore.js, AngularJS, D3.js, Rickshaw, LESS CSS ● Extends Django to enhance extensibility
Django Stack
Horizon Stack
Horizon UI Structure User Info Dashboard Projects Branding Panel Group Panel Panel Content Sidebar
Admin Dashboard
Admin Dashboard
Project Dashboard
Horizon CSS Structure
OpenStack Horizon Panels and Features OPENSTACK THAT JUST WORKS
Instance List
Filtering
Sorting
Sorting
Row Actions
Table Actions
Table Actions
Table Actions
Table Actions
Instance Details
Instance Details
Instance Log
Instance Console
OpenStack Horizon Interesting Patterns OPENSTACK THAT JUST WORKS
Dashboards & Panels ● Horizon provides a flexible framework for creating Dashboards and Panels ● Panels grouped into PanelGroups ● PanelGroups into Dashboards
Dashboard App ● Dashboards created as Django Applications ● Dashboard modules partitioned into: o static o templates o python modules
Directory Structure cloudopen/ __init__.py dashboard.py templates/ cloudopen/ static/ cloudopen/ css/ img/ js/
settings.py INSTALLED_APPS = ( ... 'horizon', 'openstack_dashboard.dashboards.project', 'openstack_dashboard.dashboards.admin', 'openstack_dashboard.dashboards.metacloud', 'openstack_dashboard.dashboards.settings', 'openstack_dashboard.dashboards.cloudopen', ... )
dashboard.py class BasePanelGroup(horizon.PanelGroup): slug = "overview" name = _("Overview") panels = ("hypervisors",) class CloudOpen(horizon.Dashboard): name = _("Linuxcon") slug = "linuxcon" panels = (BasePanelGroup,) default_panel = "hypervisors" roles = ("admin",) horizon.register(CloudOpen)
CloudOpen Dashboard Dashboard PanelGroup
View ¡Module ¡ ● View ¡module ¡,es ¡together ¡everything: ¡ o Tables, ¡Templates, ¡API ¡Calls ¡ ● Horizon ¡base ¡views: ¡ o APIView, ¡LoginView, ¡Mul,TableView, ¡ DataTableView, ¡MixedDataTableView, ¡ TabView, ¡TabbedTableView, ¡WorkflowView ¡
views.py from horizon import tables class HypervisorsIndexView(tables.DataTableView): table_class = hv_tables.AdminHypervisorsTable template_name = ’cloudopen/hypervisors/index.html’ def get_data(self): hypervisors = [] states = {} hypervisors = api.nova.hypervisor_list(self.request) … return hypervisors
Table ¡Module ¡ ● Table classes provide framework for tables: o consistent look and feel o configurable table_actions and row_actions o select/multi-select column o sorting o pagination ● Functionality is split server- and client-side
tables.py class EnableAction(tables.BatchAction): … class DisableAction(tables.BatchAction): name = 'disable' classes = ('btn-danger',) def allowed(self, request, hv): return hv.service.get('status') == 'enabled' def action(self, request, obj_id): hv = api.nova.hypervisor_get(request, obj_id) host = getattr(hv, hv.NAME_ATTR) return api.nova.service_disable(request, host, 'nova-compute') def search_link(x): return '/admin/instances?q={0}'.format(x.hypervisor_hostname)
tables.py class AdminHypervisorsTable(tables.DataTable): hypervisor_hostname = tables.Column( 'hypervisor_hostname', verbose_name=_('Hostname')) state = tables.Column( lambda hyp: hyp.service.get('state', _('UNKNOWN')).title(), verbose_name=_('State')) running_vms = tables.Column( 'running_vms', link=search_link, verbose_name=_('Instances')) ... class Meta: name = 'hypervisors' verbose_name = _('Hypervisors') row_actions = (EnableAction, DisableAction)
Template ● Standard Django template format ● Typically leverage base horizon templates (e.g. base.html )
index.html {% extends 'base.html' %} {% load i18n horizon humanize sizeformat %} {% block title %}{% trans 'Hypervisors' %}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('All Hypervisors') %} {% endblock page_header %} {% block main %} {{ table.render }} {% endblock %}
URLs ¡Modules ¡ ● Provides ¡URL ¡to ¡View ¡mappings ¡
index.html from django.conf.urls import patterns from django.conf.urls import url from openstack_dashboard.dashboards.cloudopen.hypervisors import views urlpatterns = patterns( 'openstack_dashboard.dashboards.cloudopen.hypervisors.views' url(r'^$', views.IndexView.as_view(), name='index'), )
Completed Dashboard! Nav entries Panel rendering Column sorting Linking Row actions Data retrieval RPC
Click through to Instances
Authen,ca,on ¡ ● Keystone ¡manages ¡all ¡Authen,ca,on ¡for ¡ OpenStack ¡ ● To ¡access ¡an ¡OpenStack ¡service: ¡ o authen,cate ¡with ¡Keystone ¡and ¡obtain ¡a ¡ TOKEN ¡ o Use ¡TOKEN ¡for ¡transac,ons ¡with ¡Service ¡ ● Horizon ¡passes ¡all ¡Auth ¡requests ¡to ¡Keystone ¡
backend.py class MetacloudKeystoneBackend(KeystoneBackend): def authenticate(self, request=None, username=None, password=None, user_domain_name=None, auth_url=None): keystone_client = get_keystone_client() client = keystone_client.Client( user_domain_name=user_domain_name, username=username, password=password, auth_url=auth_url, …) # auth_ref gets assigned here… … # If we made it here we succeeded. Create our User! user = create_user_from_token(request, Token(auth_ref)) request.user = user return user
Customiza,on ¡Hooks ¡ ● Change ¡Site ¡Title, ¡Logo, ¡Brand ¡Links ¡ ● Modify ¡Dashboards ¡and ¡Panels ¡ ● Change ¡BuRon ¡Styles ¡ ● Use ¡Custom ¡Stylesheets ¡ ● Use ¡Custom ¡Javascript ¡
Custom ¡Overrides ¡Module ¡ ● For ¡site-‑wide ¡customiza,on, ¡Horizon ¡allows ¡for ¡a ¡ user-‑defined ¡python ¡customiza,on ¡module ¡ ● Customiza,ons ¡can ¡include: ¡ o Registering/unregistering ¡panels ¡ o Modifying ¡dashboard ¡or ¡panel ¡aRributes ¡ o Moving ¡panels ¡between ¡dashboards ¡ o Modifying ¡aRributes ¡of ¡exis,ng ¡UI ¡elements ¡
local_settings.py HORIZON_CONFIG = { ... 'customization_module': 'openstack_dashboard.dashboards.cloudopen.overrides', 'test_enabled': True, }
overrides.py from openstack_dashboard.dashboards.cloudopen.test import panel\ as test_panel from openstack_dashboard.dashboards.cloudopen import dashboard \ as cloudopen_dashboard from django.conf import settings import horizon CLOUDOPEN_DASHBOARD_SETTINGS = horizon.get_dashboard('cloudopen') if settings.HORIZON_CONFIG.get('test_enabled'): CLOUDOPEN_DASHBOARD_SETTINGS .register(test_panel.Tests) cloudopen_dashboard.BasePanels.panels += ('ui', )
Full CloudOpen Dashboard
Test Panel
Pluggable Settings ● Since Icehouse release, Horizon enables pluggable settings to control structure o Enable/Disable new Dashboards o Add new PanelGroups o Add/Remove Panels to/from PanelGroups ● Settings all live in: o openstack_dashboard/local/enabled
Pluggable Settings _10_cloudopen.py _20_cloudopen_add_panel_group.py _30_tests_add_panel.py __init__.py
Pluggable Settings _10_cloudopen.py DASHBOARD = 'cloudopen' DISABLED = False ADD_INSTALLED_APPS = [ 'openstack_dashboard.dashboards.cloudopen', ]
Pluggable Settings _20_cloudopen_add_panel_group.py DASHBOARD = 'cloudopen' DISABLED = False ADD_INSTALLED_APPS = [ 'openstack_dashboard.dashboards.cloudopen', ]
Pluggable Settings _30_tests_add_panel.py PANEL = 'test' PANEL_DASHBOARD = 'cloudopen' PANEL_GROUP = 'tests' ADD_PANEL = \ 'openstack_dashboard.dashboards.cloudopen.test.panel.Tests'
Recommend
More recommend