20141002 delapsley-socalangularjs-final
DESCRIPTION
The OpenStack project was launched by Rackspace and NASA in July 2010. Since then it has gained considerable momentum, with over 200 companies joining the project. The OpenStack Horizon project provides a highly extensible web-based User Interface to OpenStack services. However, in the time since Horizon was first architected, there have been major advances in the design and best practices for web applications. In particular, the use of more sophisticated and robust client-side javascript frameworks have come to the fore. These frameworks provide a much more responsive user experience and much cleaner separation between the client and server. They also enable configuration driven interfaces, facilitate more modular testing, and can facilitate shorter development cycles, more testable software, and above all, a better user experience. In this presentation, we share some of our recent work in re-architecting parts of Horizon to take advantage of these new technologies. We will provide a brief introduction of OpenStack and Horizon, describe the recent work we have done, and then delve into architectural details and code examples of our recent work.TRANSCRIPT
Enhancing OpenStack Horizon with AngularJS
#socalangularjs
David Lapsley@devlaps, [email protected]
October 2, 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
Cloud Computing Model
Virtualization
OpenStack● Open Source Cloud Platformo Build public/private cloudso Multi-tenanto Virtual machines on demando Storage volumes
● Founded in 2010 by Rackspace and NASA● Since then, enormous growth…
Some interesting OpenStack facts …
OpenStack Source Code
OpenStack Source Code
OpenStack Contributors
19,410 People144 Countries
http://www.openstack.org
“Linux for the Cloud”
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 Structuresocalangularjs/ __init__.py dashboard.py templates/
socalangularjs/ static/
socalangularjs/ css/ img/ js/
_10_socalangularjs.py
DASHBOARD = 'socalangularjs'
DISABLED = False
ADD_INSTALLED_APPS = [
'openstack_dashboard.dashboards.socalangularjs',
]
dashboard.pyclass BasePanelGroup(horizon.PanelGroup): slug = "overview" name = _("Overview") panels = ("hypervisors",)
class OpenstackOC(horizon.Dashboard): name = _(”SoCalAngularJS") slug = ”SoCalAngularJS" panels = (BasePanelGroup,) default_panel = "hypervisors" roles = ("admin",)
horizon.register(SoCalAngularJS)
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 Structuresocalangularjs/ hypervisors/
__init__.py panel.py urls.py views.py
tests.py tables.py templates/
socalangularjs/ hypervisors/ index.html static/
socalangularjs/ hypervisors /
panel.py
from django.utils.translation import ugettext_lazy as _ import horizon from openstack_dashboard.dashboards.socalangularjs import dashboard class Hypervisors(horizon.Panel): name = _("Hypervisors") slug = 'hypervisors' dashboard.socalangularjs.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 = ’socalangularjs/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.socalangularjs.hypervisors import views
urlpatterns = patterns( 'openstack_dashboard.dashboards.socalangularjs.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 Structuresocalangularjs/ hypervisors/
__init__.py panel.py urls.py views.py
tables.py tests.py templates/socalangularjs/hypervisors/ index.html static/socalangularjs/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 = transform(data.objects); }); });
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
References• IRC channels (freenode.net)
– #openstack-dev
– #openstack-horizon
– #openstack-meeting
• Web:
– http://docs.openstack.org/developer/horizon/
– https://launchpad.net/horizon
– http://devstack.org
– https://github.com/openstack
– https://github.com/openstack/horizon
– http://docs.openstack.org/developer/horizon/
– http://docs.openstack.org/developer/horizon/topics/settings.html
– https://wiki.openstack.org/wiki/Gerrit_Workflow
References● Web:o http://www.stackalytics.como http://activity.openstack.org/dash/browser/o http://gabrielhurley.github.io/slides/
openstack/building_on_horizon/o http://www.solinea.com/blog/openstack-
grizzly-architecture-revisited
Thank You
David Lapsley@devlaps, [email protected]
If this sounds interesting…
http://jobs.metacloud.com
We are hiring!