Upload
david-lapsley
View
532
Download
0
Embed Size (px)
DESCRIPTION
The OpenStack Horizon project provides a web-based User Interface to OpenStack services. It is constructed in two parts: (1) a core set of libraries for implementing a Dashboard; (2) the dashboard implementation that uses the core set of libraries. Horizon uses python django — server side technology Django is a wonderful framework, but a little dated. Pre-dates the rise in client-side and single page applications. Javascript is used for enhancing the user experience 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 like BackboneJS, AngularJS, MeteorJS, have come to the fore. These applications provide a much more responsive user experience, much cleaner separation between the client and server, enable configuration driven interfaces, and facilitate more modular testing. This in turn, results in 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 discuss some of the technologies we use, our application architecture, and some of the pitfalls to avoid.
Citation preview
Client-side Rendering with AngularJS
OpenStack Summit, Paris
David Lapsley
@devlaps, [email protected]
November 3, 2014
Client-side rendering in production
Client-side Rendering
“Full” dataset search
Cache up to 1K records client-side“Full” pagination
Real-time DataUpdates every 5s
Increased platform visibility
Every node instrumented
Historical Metrics
Up to 1 year of data
Increased platform visibility Every node instrumented
Convenient access
OpenStack Horizon
Architecture
Django Stack
Horizon Stack
Horizon Stack Extended
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!');
};
}
AngularJS
By: Brad Green; Shyam Seshadri
Publisher: O'Reilly Media, Inc.
Pub. Date: April 23, 2013
Hello World
Hello World
Adding a new Horizon feature
with AngularJS
Directory Structureopenstacksummit/
hypervisors/
__init__.py
panel.py
urls.py
views.py
tables.py
tests.py
templates/openstacksummit/hypervisors/
index.html
static/openstacksummit/hypervisors/js/
hypervisors-controller.js
rest/nova/
__init__.py
hypervisor.py
instance.py
REST Resource: 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: hypervisor-controller.js
horizonApp.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: 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 %}
View: 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>
View: 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>
Client-side Rendering
“Full” dataset search
Cache up to 1K records client-side“Full” pagination
Why?
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
Better UX
Faster!
If this sounds interesting…
http://jobs.metacloud.com
We are hiring!