Commit 3d99fc55 authored by Justin Pomeroy's avatar Justin Pomeroy

Add routing and load balancer detail page

This adds the routing to allow navigating between angular pages as
well as the beginning of the load balancer detail page. The detail
page is very basic but is based on both the existing angular Images
detail page and the designs from invision. It includes two empty
tabs that will eventually hold the tables for Pools and Members.

Partially-Implements: blueprint horizon-lbaas-v2-ui
Change-Id: Icbf4396238036b20712ca643e635dd84fe66a86e
parent 75fc881f
......@@ -39,3 +39,20 @@ class LoadBalancers(generic.View):
tenant_id = request.user.project_id
result = neutronclient(request).list_loadbalancers(tenant_id=tenant_id)
return {'items': result.get('loadbalancers')}
@urls.register
class LoadBalancer(generic.View):
"""API for retrieving a single load balancer.
"""
url_regex = r'lbaas/loadbalancers/(?P<loadbalancer_id>[^/]+)/$'
@rest_utils.ajax()
def get(self, request, loadbalancer_id):
"""Get a specific load balancer.
http://localhost/api/lbaas/loadbalancers/cc758c90-3d98-4ea1-af44-aab405c9c915
"""
lb = neutronclient(request).show_loadbalancer(loadbalancer_id)
return lb.get('loadbalancer')
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Load Balancers" %}{% endblock %}
{% block page_header %}{% endblock %}
{% block page_header %}
<hz-page-header header="'{% trans "Load Balancers" %}'"></hz-page-header>
{% endblock page_header %}
{% block ng_route_base %}
<base href="/"></base>
{% endblock %}
{% block main %}
<ng-include src="'{{ STATIC_URL }}dashboard/project/lbaasv2/loadbalancers/table.html'"></ng-include>
<div ng-view></div>
{% endblock %}
......@@ -20,5 +20,5 @@ from neutron_lbaas_dashboard.dashboards.project.ngloadbalancersv2 import views
urlpatterns = patterns(
'neutron_lbaas_dashboard.dashboards.project.ngloadbalancersv2.views',
url(r'^$', views.IndexView.as_view(), name='index'),
url('', views.IndexView.as_view(), name='index'),
)
......@@ -35,6 +35,7 @@ ADD_JS_FILES = [
'dashboard/project/lbaasv2/lbaasv2.module.js',
'dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.js',
'dashboard/project/lbaasv2/loadbalancers/table.controller.js',
'dashboard/project/lbaasv2/loadbalancers/detail.controller.js',
'dashboard/project/lbaasv2/loadbalancers/filters.js',
]
......@@ -43,9 +44,10 @@ ADD_JS_SPEC_FILES = [
'dashboard/project/lbaasv2/lbaasv2.module.spec.js',
'dashboard/project/lbaasv2/loadbalancers/loadbalancers.module.spec.js',
'dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js',
'dashboard/project/lbaasv2/loadbalancers/detail.controller.spec.js',
'dashboard/project/lbaasv2/loadbalancers/filters.spec.js',
]
#
# ADD_SCSS_FILES = [
#
# ]
ADD_SCSS_FILES = [
'dashboard/project/lbaasv2/lbaasv2.scss',
]
......@@ -69,6 +69,7 @@ module.exports = function (config) {
// from jasmine.html
xstaticPath + 'jquery/data/jquery.js',
xstaticPath + 'angular/data/angular.js',
xstaticPath + 'angular/data/angular-route.js',
xstaticPath + 'angular/data/angular-mocks.js',
xstaticPath + 'angular/data/angular-cookies.js',
xstaticPath + 'angular_bootstrap/data/angular-bootstrap.js',
......
......@@ -32,7 +32,8 @@
*/
function lbaasv2API(apiService, toastService) {
var service = {
getLoadBalancers: getLoadBalancers
getLoadBalancers: getLoadBalancers,
getLoadBalancer: getLoadBalancer
};
return service;
......@@ -56,5 +57,19 @@
});
}
/**
* @name horizon.app.core.openstack-service-api.lbaasv2.getLoadBalancer
* @description
* Get a single load balancer by ID
* @param {string} id
* Specifies the id of the load balancer to request.
*/
function getLoadBalancer(id) {
return apiService.get('/api/lbaas/loadbalancers/' + id)
.error(function () {
toastService.add('error', gettext('Unable to retrieve load balancer.'));
});
}
}
}());
......@@ -41,6 +41,15 @@
"method": "get",
"path": "/api/lbaas/loadbalancers/",
"error": "Unable to retrieve load balancers."
},
{
"func": "getLoadBalancer",
"method": "get",
"path": "/api/lbaas/loadbalancers/1234",
"error": "Unable to retrieve load balancer.",
"testInput": [
'1234'
]
}
];
......
......@@ -27,6 +27,31 @@
*/
angular
.module('horizon.dashboard.project.lbaasv2', [
'ngRoute',
'horizon.dashboard.project.lbaasv2.loadbalancers'
]);
])
.config(config);
config.$inject = [
'$windowProvider',
'$routeProvider',
'$locationProvider'
];
function config($windowProvider, $routeProvider, $locationProvider) {
$locationProvider.html5Mode({
enabled: true
}).hashPrefix('!');
var base = '/project/ngloadbalancersv2/';
var path = $windowProvider.$get().STATIC_URL + 'dashboard/project/lbaasv2/';
$routeProvider
.when(base, {
templateUrl: path + 'loadbalancers/table.html'
})
.when(base + 'detail/:loadbalancerId', {
templateUrl: path + 'loadbalancers/detail.html'
})
}
}());
......@@ -22,4 +22,52 @@
});
});
describe('LBaaS v2 Module Config', function () {
var $routeProvider, $locationProvider, path;
beforeEach(function() {
// Create a dummy module so that we can test $routeProvider and $locationProvider calls
// in our actual config block.
angular.module('configTest', [])
.config(function(_$routeProvider_, _$locationProvider_, $windowProvider) {
$routeProvider = _$routeProvider_;
$locationProvider = _$locationProvider_;
path = $windowProvider.$get().STATIC_URL + 'dashboard/project/lbaasv2/';
spyOn($routeProvider, 'when').and.callThrough();
spyOn($locationProvider, 'html5Mode').and.callThrough();
});
module('ngRoute')
module('configTest');
module('horizon.dashboard.project.lbaasv2')
inject();
});
it('should use html5 mode', function () {
expect($locationProvider.html5Mode).toHaveBeenCalledWith({enabled: true});
});
it('should route URLs', function () {
var base = '/ngloadbalancersv2/';
var routes = [
[
base,
{
templateUrl: path + 'loadbalancers/table.html'
}
],
[
base + 'detail/:loadbalancerId',
{
templateUrl: path + 'loadbalancers/detail.html'
}
]
];
expect($routeProvider.when.calls.count()).toBe(2);
angular.forEach($routeProvider.when.calls.all(), function(call, i) {
expect(call.args).toEqual(routes[i]);
});
});
});
})();
/*
* Copyright 2015 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import "/app/app";
/*
* TODO(jpomero): This detail page styling should become common with the following patch:
* https://review.openstack.org/158881
*/
.detail-page {
position: relative;
p {
margin-top: 6px;
}
.detail-statuses {
margin-top: 8px;
margin-bottom: 12px;
div {
display: inline;
margin-right: 20px;
> b {
margin-right: 4px;
}
}
}
div.tab-pane > dl {
margin-top: 12px;
}
}
/*
* Copyright 2015 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
'use strict';
angular
.module('horizon.dashboard.project.lbaasv2.loadbalancers')
.controller('LoadBalancerDetailController', LoadBalancerDetailController);
LoadBalancerDetailController.$inject = [
'horizon.dashboard.project.lbaasv2.loadbalancers.basePath',
'horizon.app.core.openstack-service-api.lbaasv2',
'$routeParams'
];
/**
* @ngdoc controller
* @name LoadBalancerDetailController
*
* @description
* Controller for the LBaaS v2 load balancers detail page.
*/
function LoadBalancerDetailController(basepath, api, $routeParams) {
var ctrl = this;
ctrl.loadbalancer = {};
ctrl.path = basepath;
var loadbalancerId = $routeParams.loadbalancerId;
init();
////////////////////////////////
function init() {
api.getLoadBalancer(loadbalancerId).success(success);
}
function success(response) {
ctrl.loadbalancer = response;
}
}
})();
/*
* Copyright 2015 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
'use strict';
describe('LBaaS v2 Load Balancer Detail Controller', function() {
var controller, lbaasv2API, staticUrl, loadbalancer;
function fakeAPI() {
return {
success: function(callback) {
callback(loadbalancer);
}
};
}
///////////////////////
beforeEach(module('horizon.framework.util.http'));
beforeEach(module('horizon.framework.widgets.toast'));
beforeEach(module('horizon.app.core.openstack-service-api'));
beforeEach(module('horizon.dashboard.project.lbaasv2.loadbalancers'));
beforeEach(inject(function($injector) {
loadbalancer = { id: '1234' };
lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2');
controller = $injector.get('$controller');
staticUrl = $injector.get('$window').STATIC_URL;
spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(fakeAPI);
}));
function createController() {
return controller('LoadBalancerDetailController', {
api: lbaasv2API,
$routeParams: { loadbalancerId: '1234' }
});
}
it('should set path properly', function() {
var path = staticUrl + 'dashboard/project/lbaasv2/loadbalancers/';
expect(createController().path).toEqual(path);
});
it('should invoke lbaasv2 apis', function() {
createController();
expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('1234');
});
});
})();
<div class="content" ng-controller="LoadBalancerDetailController as ctrl">
<div class='page-header'>
<h1>{$ ::ctrl.loadbalancer.name $}</h1>
<p ng-if="::ctrl.loadbalancer.description">{$ ::ctrl.loadbalancer.description $}</p>
</div>
<div class="detail-page">
<div class="detail-statuses">
<div>
<strong translate>IP Address</strong>
{$ ::ctrl.loadbalancer.vip_address $}
</div>
<div>
<strong translate>Operating Status</strong>
{$ ::ctrl.loadbalancer.operating_status | operatingStatus $}
</div>
<div>
<strong translate>Provisioning Status</strong>
{$ ::ctrl.loadbalancer.provisioning_status | provisioningStatus $}
</div>
</div>
<tabset>
<tab heading="{$ 'Overview' | translate $}">
<dl class="dl-horizontal">
<div>
<dt translate>Provider</dt>
<dd>{$ ::ctrl.loadbalancer.provider $}</dd>
</div>
<div>
<dt translate>Admin State Up</dt>
<dd>{$ ::ctrl.loadbalancer.admin_state_up | yesno $}</dd>
</div>
<div>
<dt translate>Load Balancer ID</dt>
<dd>{$ ::ctrl.loadbalancer.id $}</dd>
</div>
<div>
<dt translate>Subnet ID</dt>
<dd>
<a ng-href="/project/networks/subnets/{$ ::ctrl.loadbalancer.vip_subnet_id $}/detail">{$ ::ctrl.loadbalancer.vip_subnet_id $}</a>
</dd>
</div>
<div>
<dt translate>Port ID</dt>
<dd>
<a ng-href="/project/networks/ports/{$ ::ctrl.loadbalancer.vip_port_id $}/detail">{$ ::ctrl.loadbalancer.vip_port_id $}</a>
</dd>
</div>
</dl>
</tab>
<tab heading="{$ 'Pools' | translate $}">
<!-- TODO(jpomero) ng-include the table of pools for this load balancer -->
</tab>
<tab heading="{$ 'Members' | translate $}">
<!-- TODO(jpomero) ng-include the table of members for this load balancer -->
</tab>
</tabset>
</div>
</div>
\ No newline at end of file
......@@ -25,8 +25,6 @@
describe('LBaaS v2 Load Balancers Module Base Path', function () {
var basePath, staticUrl;
beforeEach(module('horizon.dashboard.project'));
beforeEach(module('horizon.dashboard.project.lbaasv2'));
beforeEach(module('horizon.dashboard.project.lbaasv2.loadbalancers'));
beforeEach(inject(function ($injector) {
basePath = $injector.get('horizon.dashboard.project.lbaasv2.loadbalancers.basePath');
......
<hz-page-header header="{$ 'Load Balancers' | translate $}"></hz-page-header>
<table ng-controller="loadBalancersTableController as table"
hz-table ng-cloak
st-table="table.items"
......@@ -63,12 +65,12 @@
hz-select="item">
</td>
<td class="expander">
<i class="fa fa-chevron-right"
<span class="fa fa-chevron-right"
hz-expand-detail
duration="200">
</i>
</span>
</td>
<td class="rsp-p1">{$ item.name $}</td>
<td class="rsp-p1"><a ng-href="/project/ngloadbalancersv2/detail/{$ item.id $}">{$ item.name $}</a></td>
<td class="rsp-p1">{$ item.description | noValue $}</td>
<td class="rsp-p1">{$ item.operating_status | operatingStatus $}</td>
<td class="rsp-p1">{$ item.provisioning_status | provisioningStatus $}</td>
......@@ -135,4 +137,4 @@
-->
<tfoot hz-table-footer items="table.items"></tfoot>
</table>
</table>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment