20141001 delapsley-oc-openstack-final

70
Enhancing OpenStack Horizon with AngularJS #openstackmeetupoc David Lapsley @devlaps, [email protected] October 1, 2014

Transcript of 20141001 delapsley-oc-openstack-final

Page 1: 20141001 delapsley-oc-openstack-final

Enhancing OpenStack Horizon with AngularJS

#openstackmeetupoc

David Lapsley@devlaps, [email protected]

October 1, 2014

Page 2: 20141001 delapsley-oc-openstack-final

OpenStack Horizon in Action

Page 3: 20141001 delapsley-oc-openstack-final

Launching an Instance

Page 4: 20141001 delapsley-oc-openstack-final

Admin Overview

Page 5: 20141001 delapsley-oc-openstack-final

Project Overview

Page 6: 20141001 delapsley-oc-openstack-final

Launching an Instance

Page 7: 20141001 delapsley-oc-openstack-final

Launching an Instance

Page 8: 20141001 delapsley-oc-openstack-final

Launching an Instance

Page 9: 20141001 delapsley-oc-openstack-final

Launching an Instance

Page 10: 20141001 delapsley-oc-openstack-final

Launching an Instance

Page 11: 20141001 delapsley-oc-openstack-final

Launching an Instance

Page 12: 20141001 delapsley-oc-openstack-final

OpenStack CloudsArchitecture and Model

Page 13: 20141001 delapsley-oc-openstack-final

OpenStack Model

http://docs.openstack.org/openstack-ops/content/example_architecture.html

http://docs.openstack.org/training-guides/content/module001-ch004-openstack-architecture.html

Page 14: 20141001 delapsley-oc-openstack-final

OpenStack Projects

● Compute (Nova)

● Network (Nova, Neutron)

● VM Registration (Glance)

● Identity (Keystone)

● Object Storage (Swift, …)

● Block Storage (Cinder)

● Dashboard (Horizon)

Page 15: 20141001 delapsley-oc-openstack-final

OpenStack HorizonArchitecture

Page 16: 20141001 delapsley-oc-openstack-final

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

Page 17: 20141001 delapsley-oc-openstack-final

Django Stack

Page 18: 20141001 delapsley-oc-openstack-final

Horizon Stack

Page 19: 20141001 delapsley-oc-openstack-final

Horizon UINav entries

Column sorting

Linking

RPCData retrieval

Row actions

Table actionsFiltering

Multi-select

Page 20: 20141001 delapsley-oc-openstack-final

Customized UI

Page 21: 20141001 delapsley-oc-openstack-final

AngularJS

Page 22: 20141001 delapsley-oc-openstack-final

AngularJS lets you extend HTML

vocabulary for your application. The

resulting environment is extraordinarily

expressive, readable, and quick to

develop.https://angularjs.org

Page 23: 20141001 delapsley-oc-openstack-final

Develop smaller, lighter web apps that

are simple to create and easy to test,

extend and maintain as they grow

Brad Green, Shyam Seshadri, “AngularJS”

Page 24: 20141001 delapsley-oc-openstack-final

Core concepts

● Model View Controller framework

● Client-side templates

● Data binding

● Dependency injection

Page 25: 20141001 delapsley-oc-openstack-final

Hello Worldindex.html

<html ng-app><head> <script src="angular.js"></script> <script src="controllers.js"></script></head><body> <div ng-controller='HelloController'> <p>{{greeting.text}}, World</p> <button ng-click="action()">Alert</button> </div></body></html>

controllers.js

function HelloController($scope) { $scope.greeting = { text: 'Hello' }; $scope.action = function() { alert('Action!'); };}

AngularJSBy: Brad Green; Shyam SeshadriPublisher: O'Reilly Media, Inc.Pub. Date: April 23, 2013

Page 26: 20141001 delapsley-oc-openstack-final

Hello World

Page 27: 20141001 delapsley-oc-openstack-final

Hello World

Page 28: 20141001 delapsley-oc-openstack-final

AngularJS + Horizon

Page 29: 20141001 delapsley-oc-openstack-final

Horizon Stack Extended

Page 30: 20141001 delapsley-oc-openstack-final

Adding a new PanelUsing current Horizon

Page 31: 20141001 delapsley-oc-openstack-final

Dashboards & Panels

● Horizon provides a flexible framework

for creating Dashboards and Panels

● Panels grouped into PanelGroups

● PanelGroups into Dashboards

Page 32: 20141001 delapsley-oc-openstack-final

Dashboard App

● Dashboards created as Django

Applications

● Dashboard modules partitioned into:o statico templateso python modules

Page 33: 20141001 delapsley-oc-openstack-final

Directory Structureopenstackoc/ __init__.py dashboard.py templates/

openstackoc/ static/

openstackoc/ css/ img/ js/

Page 34: 20141001 delapsley-oc-openstack-final

_10_openstackoc.py

DASHBOARD = 'openstackoc'

DISABLED = False

ADD_INSTALLED_APPS = [

'openstack_dashboard.dashboards.openstackoc',

]

Page 35: 20141001 delapsley-oc-openstack-final

dashboard.pyclass BasePanelGroup(horizon.PanelGroup): slug = "overview" name = _("Overview") panels = ("hypervisors",)

class OpenstackOC(horizon.Dashboard): name = _("OpenstackOC") slug = "OpenstackOC" panels = (BasePanelGroup,) default_panel = "hypervisors" roles = ("admin",)

horizon.register(OpenstackOC)

Page 36: 20141001 delapsley-oc-openstack-final

DashboardDashboard

PanelGroup

Page 37: 20141001 delapsley-oc-openstack-final

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

Page 38: 20141001 delapsley-oc-openstack-final

Directory Structureopenstackoc/ hypervisors/

__init__.py panel.py urls.py views.py

tests.py tables.py templates/

openstackoc/ hypervisors/ index.html static/

openstackoc/ hypervisors /

Page 39: 20141001 delapsley-oc-openstack-final

panel.py

from django.utils.translation import ugettext_lazy as _ import horizon from openstack_dashboard.dashboards.openstackoc import dashboard class Hypervisors(horizon.Panel): name = _("Hypervisors") slug = 'hypervisors' dashboard.OpenstackOC.register(Hypervisors)

Page 40: 20141001 delapsley-oc-openstack-final

DashboardDashboard

PanelGroup

Panel

Page 41: 20141001 delapsley-oc-openstack-final

View Module● View module ties together everything:o Tables, Templates, API Calls

● Horizon base views:o APIView, LoginView, MultiTableView,

DataTableView, MixedDataTableView, TabView,

TabbedTableView, WorkflowView

Page 42: 20141001 delapsley-oc-openstack-final

views.py

from horizon import tables

class HypervisorsIndexView(tables.DataTableView): table_class = hv_tables.AdminHypervisorsTable template_name = ’openstackoc/hypervisors/index.html’

def get_data(self): hypervisors = [] states = {} hypervisors = api.nova.hypervisor_list(self.request) … return hypervisors

Page 43: 20141001 delapsley-oc-openstack-final

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

Page 44: 20141001 delapsley-oc-openstack-final

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)

Page 45: 20141001 delapsley-oc-openstack-final

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)

Page 46: 20141001 delapsley-oc-openstack-final

Template

● Standard Django template format

● Typically leverage base horizon

templates (e.g. base.html)

Page 47: 20141001 delapsley-oc-openstack-final

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><div class="row-fluid"> <div class="col-sm-12"> {{ tab_group.render }} </div></div>{% endblock %}

Page 48: 20141001 delapsley-oc-openstack-final

URLs Modules● Provides URL to View mappings

Page 49: 20141001 delapsley-oc-openstack-final

urls.py

from django.conf.urls import patterns from django.conf.urls import url

from openstack_dashboard.dashboards.openstackoc.hypervisors import views

urlpatterns = patterns( 'openstack_dashboard.dashboards.openstackoc.hypervisors.views' url(r'^$', views.IndexView.as_view(), name='index'),)

Page 50: 20141001 delapsley-oc-openstack-final

Completed DashboardNav entries

Column sorting

Panel rendering

Linking

RPCData retrieval

Page 51: 20141001 delapsley-oc-openstack-final

Adding a new Panelwith AngularJS

Page 52: 20141001 delapsley-oc-openstack-final

Directory Structureopenstackoc/ hypervisors/

__init__.py panel.py urls.py views.py

tables.py tests.py templates/openstackoc/hypervisors/ index.html static/openstackoc/hypervisors/js/ hypervisors-controller.jsrest/nova/ __init__.py hypervisor.py instance.py

Page 53: 20141001 delapsley-oc-openstack-final

REST Resource● Provides the source of data via RESTful API

● Resource includes/provides:o entity data/stateo configurable table_actions and

row_actionso sortingo paginationo CRUD operations

Page 54: 20141001 delapsley-oc-openstack-final

hypervisors.pyclass HypervisorResource(resource.BaseNovaResource):

pk = fields.CharField(attribute="pk", _("Primary Key"), hidden=True) hypervisor_hostname = fields.CharField(attribute='hypervisor_hostname', sortable=True, searchable=True) … actions = fields.ActionsField(attribute='actions', actions=[HypervisorViewLiveStats, HypervisorEnableAction, HypervisorDisableAction], title=_("Actions"), sortable=True) class Meta: authorization = auth.RestAuthorization() list_allowed_methods = ['get'] resource_name = '^hypervisor' field_order = ['pk', 'hypervisor_hostname', 'hypervisor_type', 'vcpus', 'vcpus_used', 'memory_mb', 'memory_mb_used', 'running_vms', 'state', 'status', 'actions']

Page 55: 20141001 delapsley-oc-openstack-final

Controller

● Controls view logic

Page 56: 20141001 delapsley-oc-openstack-final

hypervisor-controller.jshorizonApp.controller('TableController', function($scope, $http) { $scope.headers = headers; $scope.title = title; $http.get('/rest/api/v1/nova/instance/').success( function(data, status, headers, config) { $scope.instances = data; }); });

horizonApp.controller('ActionDropdownController', function($scope) { $scope.status = { isopen: false }; $scope.toggleDropdown = function($event) { $event.preventDefault(); $event.stopPropagation(); $scope.status.isopen = !$scope.status.isopen; }; $scope.action = function(action, id) { // Perform action. }; … });

Page 57: 20141001 delapsley-oc-openstack-final

View

● In AngularJS, view is defined in the

HTML template

Page 58: 20141001 delapsley-oc-openstack-final

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 %}

Page 59: 20141001 delapsley-oc-openstack-final

index.html

<div ng-controller="TableController"> <table class="..."> <thead> <tr class="..."> <th class="..."> <h3 class="...">{$ title $}</h3> </th> </tr> <tr class="..."> <th class="..." ng-repeat='header in headers'> <div class="...">{$ header.name $}</div> </th> </tr> </thead>

Page 60: 20141001 delapsley-oc-openstack-final

index.html <tr ng-repeat="instance in instances"> <td ng-repeat="datum in instance.data">{$ datum $}</td> <td class="..."> <div ng-controller="ActionDropdownController"> <div class="..." dropdown> <button class="..." ng-click="action(instance.actions[0], instance.name)"> {$ instance.actions[0].verbose_name $} </button> ... <div class="..."> <li class="..." ng-repeat="action in instance.actions"> <a href="#" class="..." ng-click="$parent.action(action,parent.instance.name)"> {$ action.verbose_name $} </a> </li> </ul> </div> </td> </tr> </table>

Page 61: 20141001 delapsley-oc-openstack-final

Advantages

● Clean split between server and client

side

● Significantly cleaner, terser, easier to

understand client-side code

● Significant easier to improve UX

● Client- and server-side code can be

developed and tested independently

● Faster feature velocity

Page 62: 20141001 delapsley-oc-openstack-final

AngularJS + Horizon in Production

Page 63: 20141001 delapsley-oc-openstack-final

Client-side Rendering

“Full” dataset search

Cache up to 1K records client-side

“Full” pagination

Page 64: 20141001 delapsley-oc-openstack-final

Real-time Data Updates every 5s

Increased platform visibility

Every node instrumented

Page 65: 20141001 delapsley-oc-openstack-final

Historical MetricsUp to 1 year of

data

Increased platform visibility

Every node instrumented

Convenient access

Page 66: 20141001 delapsley-oc-openstack-final

OpenStack HorizonContributing

Page 67: 20141001 delapsley-oc-openstack-final

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

Page 68: 20141001 delapsley-oc-openstack-final

Thank You

David Lapsley@devlaps, [email protected]

Page 69: 20141001 delapsley-oc-openstack-final

If this sounds interesting…

http://jobs.metacloud.com

We are hiring!

Page 70: 20141001 delapsley-oc-openstack-final