Date post: | 15-Jan-2015 |
Category: |
Documents |
Upload: | david-lapsley |
View: | 226 times |
Download: | 2 times |
OPENSTACK THAT JUST WORKS
OpenStack Horizon:Controlling the Cloud using Django
David Lapsley@devlaps, [email protected]
August 21, 2014
OpenStack Model
http://docs.openstack.org/openstack-ops/content/example_architecture.html
http://docs.openstack.org/training-guides/content/module001-ch004-openstack-architecture.html
OpenStack Projects
● Compute (Nova)
● Network (Nova, Neutron)
● VM Registration (Glance)
● Identity (Keystone)
● Object Storage (Swift, …)
● Block Storage (Cinder)
● Dashboard (Horizon)
Horizon Overview
● Django-based application deployed via
Apache and WSGI
● Provides access to OpenStack services
● Leverages existing technologieso Bootstrap, jQuery, Underscore.js,
AngularJS, D3.js, Rickshaw, LESS CSS
● Extends Django to enhance
extensibility
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 statico templateso python modules
Directory Structurecloudopen/ __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.pyclass BasePanelGroup(horizon.PanelGroup): slug = "overview" name = _("Overview") panels = ("hypervisors",)
class CloudOpen(horizon.Dashboard): name = _("Cloudopen") slug = "cloudopen" panels = (BasePanelGroup,) default_panel = "hypervisors" roles = ("admin",)
horizon.register(CloudOpen)
Panel● Panels are created as Python Modules● Panel modules partitioned into:o static/o templates/o python modules:
urls.py, views.py, panel.pytables.py, forms.py, tabs.py, tests.py
Directory Structurecloudopen/ hypervisors/
__init__.py panel.py urls.py views.py
tests.py tables.py templates/
cloudopen/ hypervisors/ index.html static/
cloudopen/ hypervisors /
panel.py
from django.utils.translation import ugettext_lazy as _ import horizon from openstack_dashboard.dashboards.cloudopen import dashboard class Hypervisors(horizon.Panel): name = _(”Hypervisors") slug = 'hypervisors' dashboard.CloudOpen.register(Hypervisors)
View Module● View module ties together everything:o Tables, Templates, API Calls
● Horizon base views:o APIView, LoginView, MultiTableView,
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 feelo configurable table_actions and
row_actionso select/multi-select columno sortingo pagination
● Functionality is split server- and client-side
tables.pyclass 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.pyclass 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 %}<div class="quota-dynamic"> <h3>{% trans "Hypervisor Summary" %}</h3> <div class="d3_quota_bar">
<div class="d3_pie_chart" …></div> </div> …</div>
{{ table.render }} {% endblock %}
urls.py
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
Column sorting
Panel rendering
Linking
RPCData retrieval
Row actions
Customization Hooks● Change Site Title, Logo, Brand Links● Modify Dashboards and Panels● Change Button Styles● Use Custom Stylesheets● Use Custom Javascript
Custom Overrides Module● For site-wide customization, Horizon allows for a
user-defined python customization module● Customizations can include:o Registering/unregistering panelso Modifying dashboard or panel attributeso Moving panels between dashboardso Modifying attributes of existing 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)
Pluggable Settings
● Since Icehouse release, Horizon enables
pluggable settings to control structureo Enable/Disable new Dashboardso Add new PanelGroupso 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
PANEL_GROUP = 'tests'
PANEL_GROUP_NAME = 'Tests'
PANEL_GROUP_DASHBOARD = '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'
Pluggable Settings
_30_tests_add_panel.py
PANEL = 'test'
PANEL_DASHBOARD = 'cloudopen'
PANEL_GROUP = 'tests'
ADD_PANEL = \
'openstack_dashboard.dashboards.cloudopen.test.panel.Tests'
Pluggable Settings
_30_overview_add_panel.py
PANEL = 'test'
PANEL_DASHBOARD = 'cloudopen'
PANEL_GROUP = 'overview'
ADD_PANEL = \
'openstack_dashboard.dashboards.cloudopen.test.panel.Tests'
Custom CSS and Javascript● Horizon templates provides blocks for custom
CSS and Javascript● To add custom CSS/JS, can either extend existing
templates, or replace with your own custom
templates
base.html<!DOCTYPE html> <html> <head> <title>{% block title %}{% endblock %} - {% site_branding %}</title> {% block css %} {% include "_stylesheets.html" %} {% endblock %} . . . </head> <body id="{% block body_id %}{% endblock %}"> {% block content %} . . . {% endblock %} <div id="footer">{% block footer %}{% endblock %}</div> {% block js %} {% include "horizon/_scripts.html" %} {% endblock %} </body> </html>
index.html{% extends "base.html" %} {% load i18n %} {% block title %}{% trans "Volumes" %}{% endblock %}
{% block css %} {% include "cloudopen/_stylesheets.html" %} {% endblock %}
{% block page_header %} {% include "horizon/common/_page_header.html" with title=_("Volumes") %} {% endblock page_header %}
{% block main %} <div id="volumes">{{ volumes_table.render }}</div> <div id="volume-types">{{ volume_types_table.render }}</div> {% endblock %}
{% block js%} {% include "cloudopen/_scripts.html" %} {% endblock %}
Historical MetricsUp to 1 year of
data
Increased platform visibility
Every node instrumented
Convenient access
Devstack and Contributing● Devstack:o “A documented shell script to build complete
OpenStack development environments.”o http://devstack.org
● Contributing to Horizon:
– http://docs.openstack.org/developer/
horizon/contributing.html