Add angular LBaaS V2 Listeners table and detail pages

This change introduces the missing listeners table under the 'Listeners'
tab on the load balancer details page.  It also adds the listeners detail
page which includes the details for a specific listener.

Partially-Implements: blueprint horizon-lbaas-v2-ui
Change-Id: I948497d7da5417d12078695d7c44d966432409a5
This commit is contained in:
Lucas Palm 2015-12-09 16:17:32 -06:00
parent 3c48addc20
commit 9a642ea1e1
18 changed files with 738 additions and 22 deletions

View File

@ -244,3 +244,52 @@ class LoadBalancer(generic.View):
spec['description'] = data['loadbalancer']['description']
return neutronclient(request).update_loadbalancer(
loadbalancer_id, {'loadbalancer': spec}).get('loadbalancer')
@urls.register
class Listeners(generic.View):
"""API for load balancer listeners.
"""
url_regex = r'lbaas/listeners/$'
@rest_utils.ajax()
def get(self, request):
"""List of listeners for the current project.
The listing result is an object with property "items".
"""
loadbalancer_id = request.GET.get('loadbalancerId')
tenant_id = request.user.project_id
result = neutronclient(request).list_listeners(tenant_id=tenant_id)
listener_list = result.get('listeners')
if loadbalancer_id:
listener_list = self._filter_listeners(listener_list,
loadbalancer_id)
return {'items': listener_list}
def _filter_listeners(self, listener_list, loadbalancer_id):
filtered_listeners = []
for listener in listener_list:
if listener['loadbalancers'][0]['id'] == loadbalancer_id:
filtered_listeners.append(listener)
return filtered_listeners
@urls.register
class Listener(generic.View):
"""API for retrieving a single listener.
"""
url_regex = r'lbaas/listeners/(?P<listener_id>[^/]+)/$'
@rest_utils.ajax()
def get(self, request, listener_id):
"""Get a specific listener.
http://localhost/api/lbaas/listeners/cc758c90-3d98-4ea1-af44-aab405c9c915
"""
lb = neutronclient(request).show_listener(listener_id)
return lb.get('listener')

View File

@ -51,7 +51,11 @@ ADD_JS_FILES = [
'dashboard/project/lbaasv2/workflow/listener/listener.controller.js',
'dashboard/project/lbaasv2/workflow/pool/pool.controller.js',
'dashboard/project/lbaasv2/workflow/members/members.controller.js',
'dashboard/project/lbaasv2/workflow/monitor/monitor.controller.js'
'dashboard/project/lbaasv2/workflow/monitor/monitor.controller.js',
'dashboard/project/lbaasv2/listeners/listeners.module.js',
'dashboard/project/lbaasv2/listeners/table.controller.js',
'dashboard/project/lbaasv2/listeners/detail.controller.js',
'dashboard/project/lbaasv2/listeners/filters.js'
]
ADD_JS_SPEC_FILES = [
@ -77,7 +81,11 @@ ADD_JS_SPEC_FILES = [
'dashboard/project/lbaasv2/workflow/listener/listener.controller.spec.js',
'dashboard/project/lbaasv2/workflow/pool/pool.controller.spec.js',
'dashboard/project/lbaasv2/workflow/members/members.controller.spec.js',
'dashboard/project/lbaasv2/workflow/monitor/monitor.controller.spec.js'
'dashboard/project/lbaasv2/workflow/monitor/monitor.controller.spec.js',
'dashboard/project/lbaasv2/listeners/listeners.module.spec.js',
'dashboard/project/lbaasv2/listeners/table.controller.spec.js',
'dashboard/project/lbaasv2/listeners/detail.controller.spec.js',
'dashboard/project/lbassv2/listeners/filters.spec.js'
]
ADD_SCSS_FILES = [

View File

@ -39,7 +39,9 @@
getLoadBalancers: getLoadBalancers,
getLoadBalancer: getLoadBalancer,
createLoadBalancer: createLoadBalancer,
editLoadBalancer: editLoadBalancer
editLoadBalancer: editLoadBalancer,
getListeners: getListeners,
getListener: getListener
};
return service;
@ -110,5 +112,44 @@
});
}
// Listeners
/**
* @name horizon.app.core.openstack-service-api.lbaasv2.getListeners
* @description
* Get the list of listeners.
* If a loadbalancer ID is passed as a parameter, the returning list of
* listeners will be filtered to include only those listeners under the
* specified loadbalancer.
* @param {string} id
* Specifies the id of the loadbalancer to request listeners for.
*
* The listing result is an object with property "items". Each item is
* a listener.
*/
function getListeners(id) {
var params = id ? {'params': {'loadbalancerId': id}} : {};
return apiService.get('/api/lbaas/listeners/', params)
.error(function () {
toastService.add('error', gettext('Unable to retrieve listeners.'));
});
}
/**
* @name horizon.app.core.openstack-service-api.lbaasv2.getListener
* @description
* Get a single listener by ID.
* @param {string} id
* Specifies the id of the listener to request.
*/
function getListener(id) {
return apiService.get('/api/lbaas/listeners/' + id)
.error(function () {
toastService.add('error', gettext('Unable to retrieve listener.'));
});
}
}
}());

View File

@ -51,14 +51,44 @@
'1234'
]
},
{
"func": "getListeners",
"method": "get",
"path": "/api/lbaas/listeners/",
"data": {
"params": {
"loadbalancerId": "1234"
}
},
"error": "Unable to retrieve listeners.",
"testInput": [
"1234"
]
},
{
"func": "getListeners",
"method": "get",
"path": "/api/lbaas/listeners/",
"data": {},
"error": "Unable to retrieve listeners."
},
{
"func": "getListener",
"method": "get",
"path": "/api/lbaas/listeners/1234",
"error": "Unable to retrieve listener.",
"testInput": [
'1234'
]
},
{
"func": "createLoadBalancer",
"method": "post",
"path": "/api/lbaas/loadbalancers/",
"error": "Unable to create load balancer.",
"data": { name: 'loadbalancer-1' },
"data": { "name": "loadbalancer-1" },
"testInput": [
{ name: 'loadbalancer-1' }
{ "name": "loadbalancer-1" }
]
},
{
@ -66,9 +96,10 @@
"method": "put",
"path": "/api/lbaas/loadbalancers/1234/",
"error": "Unable to update load balancer.",
"data": { name: 'loadbalancer-1' },
"data": { "name": "loadbalancer-1" },
"testInput": [
'1234', { name: 'loadbalancer-1' }
"1234",
{ "name": "loadbalancer-1" }
]
}
];

View File

@ -26,7 +26,8 @@
angular
.module('horizon.dashboard.project.lbaasv2', [
'ngRoute',
'horizon.dashboard.project.lbaasv2.loadbalancers'
'horizon.dashboard.project.lbaasv2.loadbalancers',
'horizon.dashboard.project.lbaasv2.listeners'
])
.config(config)
.constant('horizon.dashboard.project.lbaasv2.patterns', {
@ -60,6 +61,9 @@
})
.when(href + 'detail/:loadbalancerId', {
templateUrl: basePath + 'loadbalancers/detail.html'
})
.when(href + 'listeners/detail/:listenerId', {
templateUrl: basePath + 'listeners/detail.html'
});
}

View File

@ -108,10 +108,16 @@
{
templateUrl: basePath + 'loadbalancers/detail.html'
}
],
[
href + 'listeners/detail/:listenerId',
{
templateUrl: basePath + 'listeners/detail.html'
}
]
];
expect($routeProvider.when.calls.count()).toBe(2);
expect($routeProvider.when.calls.count()).toBe(3);
angular.forEach($routeProvider.when.calls.all(), function(call, i) {
expect(call.args).toEqual(routes[i]);
});

View File

@ -0,0 +1,74 @@
/*
* Copyright 2016 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.listeners')
.controller('ListenerDetailController', ListenerDetailController);
ListenerDetailController.$inject = [
'horizon.app.core.openstack-service-api.lbaasv2',
'$routeParams'
];
/**
* @ngdoc controller
* @name ListenerDetailController
*
* @description
* Controller for the LBaaS v2 listener detail page.
*
* @param api The LBaaS v2 API service.
* @param $routeParams The angular $routeParams service.
* @returns undefined
*/
function ListenerDetailController(api, $routeParams) {
var ctrl = this;
ctrl.listener = {};
ctrl.loadbalancer = {};
var listenerID = $routeParams.listenerId;
init();
////////////////////////////////
function init() {
api.getListener(listenerID).success(listenerSuccess);
}
function listenerSuccess(response) {
ctrl.listener = response;
if (ctrl.listener.hasOwnProperty('loadbalancers') &&
ctrl.listener.loadbalancers.length > 0) {
getLoadBalancerDetails(ctrl.listener.loadbalancers[0].id);
}
}
function getLoadBalancerDetails(loadbalancerId) {
api.getLoadBalancer(loadbalancerId).success(loadbalancerSuccess);
}
function loadbalancerSuccess(response) {
ctrl.loadbalancer = response;
}
}
})();

View File

@ -0,0 +1,67 @@
/*
* Copyright 2016 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 Listener Detail Controller', function() {
var controller, lbaasv2API, listener;
function fakeAPI() {
return {
success: function(callback) {
callback(listener);
}
};
}
///////////////////////
beforeEach(module('horizon.framework.util.http'));
beforeEach(module('horizon.framework.widgets.toast'));
beforeEach(module('horizon.framework.conf'));
beforeEach(module('horizon.app.core.openstack-service-api'));
beforeEach(module('horizon.dashboard.project.lbaasv2'));
beforeEach(inject(function($injector) {
lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2');
controller = $injector.get('$controller');
spyOn(lbaasv2API, 'getListener').and.callFake(fakeAPI);
spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(fakeAPI);
}));
function createController() {
return controller('ListenerDetailController', {
api: lbaasv2API,
$routeParams: { listenerId: '1234' }
});
}
it('should invoke lbaasv2 apis', function() {
listener = { id: '1234', loadbalancers: [{id: '5678'}] };
createController();
expect(lbaasv2API.getListener).toHaveBeenCalledWith('1234');
expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('5678');
});
it('should not invoke getLoadBalancer lbaasv2 api', function() {
listener = { id: '1234', loadbalancers: [] };
createController();
expect(lbaasv2API.getListener).toHaveBeenCalledWith('1234');
});
});
})();

View File

@ -0,0 +1,49 @@
<div class="content" ng-controller="ListenerDetailController as ctrl">
<div class='page-header'>
<ol class="breadcrumb">
<li><a href="project/ngloadbalancersv2/"><translate>Load Balancers</translate></a></li>
<li><a href="project/ngloadbalancersv2/detail/{$ ::ctrl.loadbalancer.id $}">{$ ::(ctrl.loadbalancer.name || ctrl.loadbalancer.id) $}</a></li>
<li class="active">{$ ::(ctrl.listener.name || ctrl.listener.id) $}</li>
</ol>
<p ng-if="::ctrl.listener.description">{$ ::ctrl.listener.description $}</p>
</div>
<div class="detail-page">
<dl class="dl-horizontal">
<div>
<dt translate>Listener ID</dt>
<dd>{$ ::ctrl.listener.id $}</dd>
</div>
<div>
<dt translate>Tenant ID</dt>
<dd>{$ ::ctrl.listener.tenant_id $}</dd>
</div>
<div>
<dt translate>Protocol</dt>
<dd>{$ ::ctrl.listener.protocol $}</dd>
</div>
<div>
<dt translate>Protocol Port</dt>
<dd>{$ ::ctrl.listener.protocol_port $}</dd>
</div>
<div>
<dt translate>Connection Limit</dt>
<dd>{$ ::ctrl.listener.connection_limit | connectionLimit $}</dd>
</div>
<div>
<dt translate>Admin State Up</dt>
<dd>{$ ::ctrl.listener.admin_state_up | yesno $}</dd>
</div>
<div>
<dt translate>Default Pool ID</dt>
<dd>
<div ng-if="ctrl.listener.default_pool_id !== null">
<a ng-href="project/ngloadbalancersv2/pools/detail/{$ ::ctrl.listener.default_pool_id $}">{$ ::ctrl.listener.default_pool_id $}</a>
</div>
<div ng-if="ctrl.listener.default_pool_id === null">
{$ 'None' | translate $}
</div>
</dd>
</div>
</dl>
</div>
</div>

View File

@ -0,0 +1,42 @@
/*
* Copyright 2016 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.listeners')
.filter('connectionLimit', connectionLimitFilter);
connectionLimitFilter.$inject = [
'horizon.framework.util.i18n.gettext'
];
/**
* @ngdoc filter
* @name connectionLimitFilter
* @description
* Takes the raw listener connection limit from the API and returns 'Unlimited' if the
* default connection limit of -1 is returned.
* @returns The function for filtering the listener connection limit.
*/
function connectionLimitFilter(gettext) {
return function (input) {
return input === -1 ? gettext('Unlimited') : input;
};
}
}());

View File

@ -0,0 +1,39 @@
/*
* Copyright 2016 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 Listeners Filters', function () {
beforeEach(module('horizon.framework.util.i18n'));
beforeEach(module('horizon.dashboard.project.lbaasv2.listeners'));
describe('connectionLimit', function () {
var connectionLimitFilter;
beforeEach(inject(function (_connectionLimitFilter_) {
connectionLimitFilter = _connectionLimitFilter_;
}));
it('Returns "Unlimited" when connection limit value is -1', function () {
expect(connectionLimitFilter(-1)).toBe('Unlimited');
});
it('Returns original connection limit when value is not -1', function () {
expect(connectionLimitFilter(100)).toBe(100);
});
});
});
})();

View File

@ -0,0 +1,31 @@
/*
* Copyright 2016 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';
/**
* @ngdoc overview
* @ngname horizon.dashboard.project.lbaasv2.listeners
*
* @description
* Provides the services and widgets required to support and display the project listeners
* for the load balancers v2 panel.
*/
angular
.module('horizon.dashboard.project.lbaasv2.listeners', []);
})();

View File

@ -0,0 +1,25 @@
/*
* Copyright 2016 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 Listeners Module', function() {
it('should exist', function() {
expect(angular.module('horizon.dashboard.project.lbaasv2.listeners')).toBeDefined();
});
});
})();

View File

@ -0,0 +1,66 @@
/*
* Copyright 2016 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.listeners')
.controller('ListenersTableController', ListenersTableController);
ListenersTableController.$inject = [
'horizon.app.core.openstack-service-api.lbaasv2',
'$routeParams',
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions'
];
/**
* @ngdoc controller
* @name ListenersTableController
*
* @description
* Controller for the LBaaS v2 listeners table. Serves as the focal point for table actions.
*
* @param api The LBaaS V2 service API.
* @param $routeParams The angular $routeParams service.
* @param batchActions The load balancer batch actions service.
* @returns undefined
*/
function ListenersTableController(api, $routeParams, batchActions) {
var ctrl = this;
ctrl.items = [];
ctrl.src = [];
ctrl.checked = {};
ctrl.batchActions = batchActions;
var loadbalancerId = $routeParams.loadbalancerId;
init();
////////////////////////////////
function init() {
api.getListeners(loadbalancerId).success(success);
}
function success(response) {
ctrl.src = response.items;
}
}
})();

View File

@ -0,0 +1,69 @@
/*
* Copyright 2016 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 Listeners Table Controller', function() {
var controller, lbaasv2API;
var items = [];
function fakeAPI() {
return {
success: function(callback) {
callback({ items: items });
}
};
}
///////////////////////
beforeEach(module('horizon.framework.widgets.toast'));
beforeEach(module('horizon.framework.conf'));
beforeEach(module('horizon.framework.util'));
beforeEach(module('horizon.app.core.openstack-service-api'));
beforeEach(module('horizon.dashboard.project.lbaasv2'));
beforeEach(module(function($provide) {
$provide.value('$modal', {});
}));
beforeEach(inject(function($injector) {
lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2');
controller = $injector.get('$controller');
spyOn(lbaasv2API, 'getListeners').and.callFake(fakeAPI);
}));
function createController() {
return controller('ListenersTableController', {
$routeParams: { loadbalancerId: '1234' }
});
}
it('should initialize correctly', function() {
var ctrl = createController();
expect(ctrl.items).toEqual([]);
expect(ctrl.src).toEqual(items);
expect(ctrl.checked).toEqual({});
expect(ctrl.batchActions).toBeDefined();
});
it('should invoke lbaasv2 apis', function() {
createController();
expect(lbaasv2API.getListeners).toHaveBeenCalled();
});
});
})();

View File

@ -0,0 +1,115 @@
<hz-page-header header="{$ 'Listeners' | translate $}"></hz-page-header>
<table ng-controller="ListenersTableController as table"
hz-table ng-cloak
st-table="table.items"
st-safe-src="table.src"
default-sort="name"
default-sort-reverse="false"
class="table-striped table-rsp table-detail modern">
<!--
TODO(jpomero): This table pattern does not allow for extensibility and should be revisited
once horizon implements a better one.
-->
<thead>
<tr>
<!--
Table-batch-actions:
This is where batch actions like searching, creating, and deleting.
-->
<th colspan="100" class="search-header">
<hz-search-bar group-classes="input-group-sm" icon-classes="fa-search">
</hz-search-bar>
</th>
</tr>
<tr>
<!--
Table-column-headers:
This is where we declaratively define the table column headers.
Include select-col if you want to select all.
Include expander if you want to inline details.
Include action-col if you want to perform actions.
-->
<th class="select-col">
<input type="checkbox" hz-select-all="table.items">
</th>
<th class="expander"></th>
<th class="rsp-p1" st-sort="name" st-sort-default="name" translate>Name</th>
<th class="rsp-p1" st-sort="description" translate>Description</th>
<th class="rsp-p1" st-sort="protocol" translate>Protocol</th>
<th class="rsp-p1" st-sort="port" translate>Port</th>
</tr>
</thead>
<tbody>
<!--
Table-rows:
This is where we declaratively define the table columns.
Include select-col if you want to select all.
Include expander if you want to inline details.
Include action-col if you want to perform actions.
rsp-p1 rsp-p2 are responsive priority as user resizes window.
-->
<tr ng-repeat-start="item in table.items track by item.id"
ng-class="{'st-selected': checked[item.id]}">
<td class="select-col">
<input type="checkbox"
ng-model="selected[item.id].checked"
hz-select="item">
</td>
<td class="expander">
<span class="fa fa-chevron-right"
hz-expand-detail
duration="200">
</span>
</td>
<td class="rsp-p1"><a ng-href="project/ngloadbalancersv2/listeners/detail/{$ ::item.id $}">{$ ::(item.name || item.id) $}</a></td>
<td class="rsp-p1">{$ ::item.description | noValue $}</td>
<td class="rsp-p1">{$ ::item.protocol$}</td>
<td class="rsp-p1">{$ ::item.protocol_port$}</td>
</tr>
<tr ng-repeat-end class="detail-row">
<!--
Detail-row:
Contains detailed information on this item.
Can be toggled using the chevron button.
Ensure colspan is greater or equal to number of column-headers.
-->
<td class="detail" colspan="100">
<div class="row">
<dl class="col-sm-2">
<dt translate>ID</dt>
<dd>{$ ::item.id $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Admin State Up</dt>
<dd>{$ ::item.admin_state_up | yesno $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Connection Limit</dt>
<dd>{$ ::item.connection_limit | connectionLimit $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Default Pool ID</dt>
<dd>{$ ::item.default_pool_id | noValue $}</dd>
</dl>
</div>
</td>
</tr>
</tbody>
<!--
Table-footer:
This is where we display number of items and pagination controls.
-->
<tfoot hz-table-footer items="table.items"></tfoot>
</table>

View File

@ -2,7 +2,7 @@
<div class='page-header'>
<ol class="breadcrumb">
<li><a href="project/ngloadbalancersv2/"><translate>Load Balancers</translate></a></li>
<li class="active">{$ ::ctrl.loadbalancer.name $}</li>
<li class="active">{$ (ctrl.loadbalancer.name || ctrl.loadbalancer.id) $}</li>
</ol>
<p ng-if="::ctrl.loadbalancer.description">{$ ::ctrl.loadbalancer.description $}</p>
</div>
@ -53,7 +53,7 @@
</dl>
</tab>
<tab heading="{$ 'Listeners' | translate $}">
<!-- TODO(jpomero) ng-include the table of listeners for this load balancer -->
<ng-include src="'static/dashboard/project/lbaasv2/listeners/table.html'"></ng-include>
</tab>
</tabset>
</div>

View File

@ -71,11 +71,11 @@
duration="200">
</span>
</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>
<td class="rsp-p2">{$ item.vip_address $}</td>
<td class="rsp-p1"><a ng-href="project/ngloadbalancersv2/detail/{$ ::item.id $}">{$ ::(item.name || item.id) $}</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>
<td class="rsp-p2">{$ ::item.vip_address $}</td>
<td class="rsp-p2">{$ item.listeners.length $}</td>
<td class="action-col">
<!--
@ -103,7 +103,7 @@
<span class="rsp-alt-p2">
<dl class="col-sm-2">
<dt translate>IP Address</dt>
<dd>{$ item.vip_address $}</dd>
<dd>{$ ::item.vip_address $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Listeners</dt>
@ -115,23 +115,23 @@
<div class="row">
<dl class="col-sm-2">
<dt translate>Provider</dt>
<dd>{$ item.provider $}</dd>
<dd>{$ ::item.provider $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Admin State Up</dt>
<dd>{$ item.admin_state_up | yesno $}</dd>
<dd>{$ ::item.admin_state_up | yesno $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>ID</dt>
<dd>{$ item.id $}</dd>
<dd>{$ ::item.id $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Subnet ID</dt>
<dd>{$ item.vip_subnet_id $}</dd>
<dd>{$ ::item.vip_subnet_id $}</dd>
</dl>
<dl class="col-sm-2">
<dt translate>Port ID</dt>
<dd>{$ item.vip_port_id $}</dd>
<dd>{$ ::item.vip_port_id $}</dd>
</dl>
</div>