20141001 delapsley-oc-openstack-final

Post on 21-Jun-2015

310 views 5 download

Transcript of 20141001 delapsley-oc-openstack-final

Enhancing OpenStack Horizon with AngularJS

#openstackmeetupoc

David Lapsley@devlaps, david.lapsley@metacloud.com

October 1, 2014

OpenStack Horizon in Action

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 CloudsArchitecture and Model

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)

OpenStack HorizonArchitecture

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

Django Stack

Horizon Stack

Horizon UINav entries

Column sorting

Linking

RPCData retrieval

Row actions

Table actionsFiltering

Multi-select

Customized UI

AngularJS

AngularJS lets you extend HTML

vocabulary for your application. The

resulting environment is extraordinarily

expressive, readable, and quick to

develop.https://angularjs.org

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”

Core concepts

● Model View Controller framework

● Client-side templates

● Data binding

● Dependency injection

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

Hello World

Hello World

AngularJS + Horizon

Horizon Stack Extended

Adding a new PanelUsing current Horizon

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 Structureopenstackoc/ __init__.py dashboard.py templates/

openstackoc/ static/

openstackoc/ css/ img/ js/

_10_openstackoc.py

DASHBOARD = 'openstackoc'

DISABLED = False

ADD_INSTALLED_APPS = [

'openstack_dashboard.dashboards.openstackoc',

]

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)

DashboardDashboard

PanelGroup

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 Structureopenstackoc/ hypervisors/

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

tests.py tables.py templates/

openstackoc/ hypervisors/ index.html static/

openstackoc/ hypervisors /

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)

DashboardDashboard

PanelGroup

Panel

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

URLs Modules● Provides URL to View mappings

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'),)

Completed DashboardNav entries

Column sorting

Panel rendering

Linking

RPCData retrieval

Adding a new Panelwith AngularJS

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

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

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']

Controller

● Controls view logic

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. }; … });

View

● In AngularJS, view is defined in the

HTML template

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

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>

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>

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

AngularJS + Horizon in Production

Client-side Rendering

“Full” dataset search

Cache up to 1K records client-side

“Full” pagination

Real-time Data Updates every 5s

Increased platform visibility

Every node instrumented

Historical MetricsUp to 1 year of

data

Increased platform visibility

Every node instrumented

Convenient access

OpenStack HorizonContributing

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

Thank You

David Lapsley@devlaps, david.lapsley@metacloud.com

If this sounds interesting…

http://jobs.metacloud.com

We are hiring!