Add associate and disassociate floating IP actions
This adds the load balancer actions for associating and disassociating a floating IP address. Partially-Implements: blueprint horizon-lbaas-v2-ui Change-Id: Ie62cbaa6e4e6664a4d266f01557386d6d40cc2b1
This commit is contained in:
parent
988bd51b2e
commit
51661642c0
@ -21,6 +21,7 @@ from django.views import generic
|
||||
|
||||
from horizon import conf
|
||||
|
||||
from openstack_dashboard.api import network
|
||||
from openstack_dashboard.api import neutron
|
||||
from openstack_dashboard.api.rest import urls
|
||||
from openstack_dashboard.api.rest import utils as rest_utils
|
||||
@ -348,6 +349,21 @@ def update_member_list(request, **kwargs):
|
||||
thread.start_new_thread(poll_loadbalancer_status, args)
|
||||
|
||||
|
||||
def add_floating_ip_info(request, loadbalancers):
|
||||
"""Add floating IP address info to each load balancer.
|
||||
|
||||
"""
|
||||
floating_ips = network.tenant_floating_ip_list(request)
|
||||
for lb in loadbalancers:
|
||||
floating_ip = {}
|
||||
associated_ip = next((fip for fip in floating_ips
|
||||
if fip['fixed_ip'] == lb['vip_address']), None)
|
||||
if associated_ip is not None:
|
||||
floating_ip['id'] = associated_ip['id']
|
||||
floating_ip['ip'] = associated_ip['ip']
|
||||
lb['floating_ip'] = floating_ip
|
||||
|
||||
|
||||
@urls.register
|
||||
class LoadBalancers(generic.View):
|
||||
"""API for load balancers.
|
||||
@ -362,8 +378,11 @@ class LoadBalancers(generic.View):
|
||||
The listing result is an object with property "items".
|
||||
"""
|
||||
tenant_id = request.user.project_id
|
||||
result = neutronclient(request).list_loadbalancers(tenant_id=tenant_id)
|
||||
return {'items': result.get('loadbalancers')}
|
||||
loadbalancers = neutronclient(request).list_loadbalancers(
|
||||
tenant_id=tenant_id).get('loadbalancers')
|
||||
if request.GET.get('full') and network.floating_ip_supported(request):
|
||||
add_floating_ip_info(request, loadbalancers)
|
||||
return {'items': loadbalancers}
|
||||
|
||||
@rest_utils.ajax()
|
||||
def post(self, request):
|
||||
@ -409,8 +428,11 @@ class LoadBalancer(generic.View):
|
||||
|
||||
http://localhost/api/lbaas/loadbalancers/cc758c90-3d98-4ea1-af44-aab405c9c915
|
||||
"""
|
||||
lb = neutronclient(request).show_loadbalancer(loadbalancer_id)
|
||||
return lb.get('loadbalancer')
|
||||
loadbalancer = neutronclient(request).show_loadbalancer(
|
||||
loadbalancer_id).get('loadbalancer')
|
||||
if request.GET.get('full') and network.floating_ip_supported(request):
|
||||
add_floating_ip_info(request, [loadbalancer])
|
||||
return loadbalancer
|
||||
|
||||
@rest_utils.ajax()
|
||||
def put(self, request, loadbalancer_id):
|
||||
|
@ -60,13 +60,14 @@
|
||||
* @name horizon.app.core.openstack-service-api.lbaasv2.getLoadBalancers
|
||||
* @description
|
||||
* Get a list of load balancers.
|
||||
*
|
||||
* @param {boolean} full
|
||||
* The listing result is an object with property "items". Each item is
|
||||
* a load balancer.
|
||||
*/
|
||||
|
||||
function getLoadBalancers() {
|
||||
return apiService.get('/api/lbaas/loadbalancers/')
|
||||
function getLoadBalancers(full) {
|
||||
var params = { full: full };
|
||||
return apiService.get('/api/lbaas/loadbalancers/', { params: params })
|
||||
.error(function () {
|
||||
toastService.add('error', gettext('Unable to retrieve load balancers.'));
|
||||
});
|
||||
@ -77,11 +78,13 @@
|
||||
* @description
|
||||
* Get a single load balancer by ID
|
||||
* @param {string} id
|
||||
* @param {boolean} full
|
||||
* Specifies the id of the load balancer to request.
|
||||
*/
|
||||
|
||||
function getLoadBalancer(id) {
|
||||
return apiService.get('/api/lbaas/loadbalancers/' + id)
|
||||
function getLoadBalancer(id, full) {
|
||||
var params = { full: full };
|
||||
return apiService.get('/api/lbaas/loadbalancers/' + id, { params: params })
|
||||
.error(function () {
|
||||
toastService.add('error', gettext('Unable to retrieve load balancer.'));
|
||||
});
|
||||
|
@ -37,144 +37,110 @@
|
||||
|
||||
var tests = [
|
||||
{
|
||||
"func": "getLoadBalancers",
|
||||
"method": "get",
|
||||
"path": "/api/lbaas/loadbalancers/",
|
||||
"error": "Unable to retrieve load balancers."
|
||||
func: 'getLoadBalancers',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/loadbalancers/',
|
||||
error: 'Unable to retrieve load balancers.',
|
||||
testInput: [ true ],
|
||||
data: { params: { full: true } }
|
||||
},
|
||||
{
|
||||
"func": "getLoadBalancer",
|
||||
"method": "get",
|
||||
"path": "/api/lbaas/loadbalancers/1234",
|
||||
"error": "Unable to retrieve load balancer.",
|
||||
"testInput": [
|
||||
'1234'
|
||||
]
|
||||
func: 'getLoadBalancer',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/loadbalancers/1234',
|
||||
error: 'Unable to retrieve load balancer.',
|
||||
testInput: [ '1234', true ],
|
||||
data: { params: { full: true } }
|
||||
},
|
||||
{
|
||||
"func": "deleteLoadBalancer",
|
||||
"method": "delete",
|
||||
"path": "/api/lbaas/loadbalancers/1234",
|
||||
"error": "Unable to delete load balancer.",
|
||||
"testInput": [
|
||||
'1234'
|
||||
]
|
||||
func: 'deleteLoadBalancer',
|
||||
method: 'delete',
|
||||
path: '/api/lbaas/loadbalancers/1234',
|
||||
error: 'Unable to delete load balancer.',
|
||||
testInput: [ '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/',
|
||||
error: 'Unable to retrieve listeners.',
|
||||
testInput: [ '1234' ],
|
||||
data: { params: { loadbalancerId: '1234' } }
|
||||
},
|
||||
{
|
||||
"func": "getListeners",
|
||||
"method": "get",
|
||||
"path": "/api/lbaas/listeners/",
|
||||
"data": {},
|
||||
"error": "Unable to retrieve listeners."
|
||||
func: 'getListeners',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/listeners/',
|
||||
data: {},
|
||||
error: 'Unable to retrieve listeners.'
|
||||
},
|
||||
{
|
||||
"func": "getListener",
|
||||
"method": "get",
|
||||
"path": "/api/lbaas/listeners/1234",
|
||||
"data": {
|
||||
"params": {
|
||||
"includeChildResources": true
|
||||
}
|
||||
},
|
||||
"error": "Unable to retrieve listener.",
|
||||
"testInput": [
|
||||
'1234',
|
||||
true
|
||||
]
|
||||
func: 'getListener',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/listeners/1234',
|
||||
data: { params: { includeChildResources: true } },
|
||||
error: 'Unable to retrieve listener.',
|
||||
testInput: [ '1234', true ]
|
||||
},
|
||||
{
|
||||
"func": "getListener",
|
||||
"method": "get",
|
||||
"path": "/api/lbaas/listeners/1234",
|
||||
"data": {},
|
||||
"error": "Unable to retrieve listener.",
|
||||
"testInput": [
|
||||
'1234',
|
||||
false
|
||||
]
|
||||
func: 'getListener',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/listeners/1234',
|
||||
data: {},
|
||||
error: 'Unable to retrieve listener.',
|
||||
testInput: [ '1234', false ]
|
||||
},
|
||||
{
|
||||
"func": "getPool",
|
||||
"method": "get",
|
||||
"path": "/api/lbaas/pools/1234",
|
||||
"error": "Unable to retrieve pool.",
|
||||
"testInput": [
|
||||
'1234'
|
||||
]
|
||||
func: 'getPool',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/pools/1234',
|
||||
error: 'Unable to retrieve pool.',
|
||||
testInput: [ '1234' ]
|
||||
},
|
||||
{
|
||||
"func": "getMembers",
|
||||
"method": "get",
|
||||
"path": "/api/lbaas/pools/1234/members/",
|
||||
"error": "Unable to retrieve members.",
|
||||
"testInput": [
|
||||
'1234'
|
||||
]
|
||||
func: 'getMembers',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/pools/1234/members/',
|
||||
error: 'Unable to retrieve members.',
|
||||
testInput: [ '1234' ]
|
||||
},
|
||||
{
|
||||
"func": "getMember",
|
||||
"method": "get",
|
||||
"path": "/api/lbaas/pools/1234/members/5678",
|
||||
"error": "Unable to retrieve member.",
|
||||
"testInput": [
|
||||
'1234',
|
||||
'5678'
|
||||
]
|
||||
func: 'getMember',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/pools/1234/members/5678',
|
||||
error: 'Unable to retrieve member.',
|
||||
testInput: [ '1234', '5678' ]
|
||||
},
|
||||
{
|
||||
"func": "getHealthMonitor",
|
||||
"method": "get",
|
||||
"path": "/api/lbaas/healthmonitors/1234",
|
||||
"error": "Unable to retrieve health monitor.",
|
||||
"testInput": [
|
||||
'1234'
|
||||
]
|
||||
func: 'getHealthMonitor',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/healthmonitors/1234',
|
||||
error: 'Unable to retrieve health monitor.',
|
||||
testInput: [ '1234' ]
|
||||
},
|
||||
{
|
||||
"func": "createLoadBalancer",
|
||||
"method": "post",
|
||||
"path": "/api/lbaas/loadbalancers/",
|
||||
"error": "Unable to create load balancer.",
|
||||
"data": { "name": "loadbalancer-1" },
|
||||
"testInput": [
|
||||
{ "name": "loadbalancer-1" }
|
||||
]
|
||||
func: 'createLoadBalancer',
|
||||
method: 'post',
|
||||
path: '/api/lbaas/loadbalancers/',
|
||||
error: 'Unable to create load balancer.',
|
||||
data: { name: 'loadbalancer-1' },
|
||||
testInput: [ { name: 'loadbalancer-1' } ]
|
||||
},
|
||||
{
|
||||
"func": "editLoadBalancer",
|
||||
"method": "put",
|
||||
"path": "/api/lbaas/loadbalancers/1234",
|
||||
"error": "Unable to update load balancer.",
|
||||
"data": { "name": "loadbalancer-1" },
|
||||
"testInput": [
|
||||
"1234",
|
||||
{ "name": "loadbalancer-1" }
|
||||
]
|
||||
func: 'editLoadBalancer',
|
||||
method: 'put',
|
||||
path: '/api/lbaas/loadbalancers/1234',
|
||||
error: 'Unable to update load balancer.',
|
||||
data: { name: 'loadbalancer-1' },
|
||||
testInput: [ '1234', { name: 'loadbalancer-1' } ]
|
||||
},
|
||||
{
|
||||
"func": "editListener",
|
||||
"method": "put",
|
||||
"path": "/api/lbaas/listeners/1234",
|
||||
"error": "Unable to update listener.",
|
||||
"data": { "name": "listener-1" },
|
||||
"testInput": [
|
||||
"1234",
|
||||
{ "name": "listener-1" }
|
||||
]
|
||||
func: 'editListener',
|
||||
method: 'put',
|
||||
path: '/api/lbaas/listeners/1234',
|
||||
error: 'Unable to update listener.',
|
||||
data: { name: 'listener-1' },
|
||||
testInput: [ '1234', { name: 'listener-1' } ]
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.loadbalancers')
|
||||
.controller('AssociateFloatingIpModalController', AssociateFloatingIpModalController);
|
||||
|
||||
AssociateFloatingIpModalController.$inject = [
|
||||
'$modalInstance',
|
||||
'horizon.app.core.openstack-service-api.network',
|
||||
'horizon.framework.util.i18n.gettext',
|
||||
// Dependencies injected with resolve by $modal.open
|
||||
'loadbalancer',
|
||||
'floatingIps',
|
||||
'floatingIpPools'
|
||||
];
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name AssociateFloatingIpModalController
|
||||
* @description
|
||||
* Controller used by the modal service for associating a floating IP address to a
|
||||
* load balancer.
|
||||
*
|
||||
* @param $modalInstance The angular bootstrap $modalInstance service.
|
||||
* @param api The horizon network API service.
|
||||
* @param gettext The horizon gettext function for translation.
|
||||
* @param loadbalancer The load balancer to associate the floating IP with.
|
||||
* @param floatingIps List of available floating IP addresses.
|
||||
* @param floatingIpPools List of available floating IP pools.
|
||||
*
|
||||
* @returns The Associate Floating IP modal controller.
|
||||
*/
|
||||
|
||||
function AssociateFloatingIpModalController(
|
||||
$modalInstance, api, gettext, loadbalancer, floatingIps, floatingIpPools
|
||||
) {
|
||||
var ctrl = this;
|
||||
var port = loadbalancer.vip_port_id + '_' + loadbalancer.vip_address;
|
||||
|
||||
ctrl.cancel = cancel;
|
||||
ctrl.save = save;
|
||||
ctrl.saving = false;
|
||||
ctrl.options = initOptions();
|
||||
ctrl.selected = ctrl.options.length === 1 ? ctrl.options[0] : null;
|
||||
|
||||
function save() {
|
||||
ctrl.saving = true;
|
||||
if (ctrl.selected.type === 'pool') {
|
||||
allocateIpAddress(ctrl.selected.id);
|
||||
} else {
|
||||
associateIpAddress(ctrl.selected.id);
|
||||
}
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
}
|
||||
|
||||
function onSuccess() {
|
||||
$modalInstance.close();
|
||||
}
|
||||
|
||||
function onFailure() {
|
||||
ctrl.saving = false;
|
||||
}
|
||||
|
||||
function initOptions() {
|
||||
var options = [];
|
||||
floatingIps.forEach(function addFloatingIp(ip) {
|
||||
// Only show floating IPs that are not already associated with a fixed IP
|
||||
if (!ip.fixed_ip) {
|
||||
options.push({
|
||||
id: ip.id,
|
||||
name: ip.ip || ip.id,
|
||||
type: 'ip',
|
||||
group: gettext('Floating IP addresses')
|
||||
});
|
||||
}
|
||||
});
|
||||
floatingIpPools.forEach(function addFloatingIpPool(pool) {
|
||||
options.push({
|
||||
id: pool.id,
|
||||
name: pool.name || pool.id,
|
||||
type: 'pool',
|
||||
group: gettext('Floating IP pools')
|
||||
});
|
||||
});
|
||||
return options;
|
||||
}
|
||||
|
||||
function allocateIpAddress(poolId) {
|
||||
return api.allocateFloatingIp(poolId).then(getId).then(associateIpAddress);
|
||||
}
|
||||
|
||||
function associateIpAddress(addressId) {
|
||||
return api.associateFloatingIp(addressId, port).then(onSuccess, onFailure);
|
||||
}
|
||||
|
||||
function getId(response) {
|
||||
return response.data.id;
|
||||
}
|
||||
}
|
||||
})();
|
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 Load Balancers Table Associate IP Controller', function() {
|
||||
var ctrl, network, floatingIps, floatingIpPools, $controller, $modalInstance;
|
||||
var associateFail = false;
|
||||
|
||||
beforeEach(module('horizon.framework.util.i18n'));
|
||||
beforeEach(module('horizon.dashboard.project.lbaasv2'));
|
||||
|
||||
beforeEach(function() {
|
||||
floatingIps = [{ id: 'ip1', ip: '1', fixed_ip: '1' },
|
||||
{ id: 'ip2', ip: '2' }];
|
||||
floatingIpPools = [{ id: 'pool1', name: 'pool' }];
|
||||
});
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
var fakePromise = function(response, returnPromise) {
|
||||
return {
|
||||
then: function(success, fail) {
|
||||
if (fail && associateFail) {
|
||||
return fail();
|
||||
}
|
||||
var res = success(response);
|
||||
return returnPromise ? fakePromise(res) : res;
|
||||
}
|
||||
};
|
||||
};
|
||||
$provide.value('$modalInstance', {
|
||||
close: angular.noop,
|
||||
dismiss: angular.noop
|
||||
});
|
||||
$provide.value('loadbalancer', {
|
||||
vip_port_id: 'port',
|
||||
vip_address: 'address'
|
||||
});
|
||||
$provide.value('floatingIps', floatingIps);
|
||||
$provide.value('floatingIpPools', floatingIpPools);
|
||||
$provide.value('horizon.app.core.openstack-service-api.network', {
|
||||
allocateFloatingIp: function() {
|
||||
return fakePromise({ data: { id: 'foo' } }, true);
|
||||
},
|
||||
associateFloatingIp: function() {
|
||||
return fakePromise();
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(inject(function ($injector) {
|
||||
network = $injector.get('horizon.app.core.openstack-service-api.network');
|
||||
$controller = $injector.get('$controller');
|
||||
$modalInstance = $injector.get('$modalInstance');
|
||||
}));
|
||||
|
||||
it('should define controller properties', function() {
|
||||
ctrl = $controller('AssociateFloatingIpModalController');
|
||||
expect(ctrl.cancel).toBeDefined();
|
||||
expect(ctrl.save).toBeDefined();
|
||||
expect(ctrl.saving).toBe(false);
|
||||
});
|
||||
|
||||
it('should initialize options', function() {
|
||||
ctrl = $controller('AssociateFloatingIpModalController');
|
||||
expect(ctrl.options.length).toBe(2);
|
||||
expect(ctrl.options[0].id).toBe('ip2');
|
||||
expect(ctrl.options[1].id).toBe('pool1');
|
||||
});
|
||||
|
||||
it('should use ids instead of ip or name if not provided', function() {
|
||||
delete floatingIps[1].ip;
|
||||
delete floatingIpPools[0].name;
|
||||
ctrl = $controller('AssociateFloatingIpModalController');
|
||||
expect(ctrl.options.length).toBe(2);
|
||||
expect(ctrl.options[0].name).toBe('ip2');
|
||||
expect(ctrl.options[1].name).toBe('pool1');
|
||||
});
|
||||
|
||||
it('should initialize selected option when only one option', function() {
|
||||
floatingIps[1].fixed_ip = '2';
|
||||
ctrl = $controller('AssociateFloatingIpModalController');
|
||||
expect(ctrl.options.length).toBe(1);
|
||||
expect(ctrl.selected).toBe(ctrl.options[0]);
|
||||
});
|
||||
|
||||
it('should not initialize selected option when more than one option', function() {
|
||||
ctrl = $controller('AssociateFloatingIpModalController');
|
||||
expect(ctrl.options.length).toBe(2);
|
||||
expect(ctrl.selected).toBeNull();
|
||||
});
|
||||
|
||||
it('should associate floating IP if floating IP selected', function() {
|
||||
ctrl = $controller('AssociateFloatingIpModalController');
|
||||
ctrl.selected = ctrl.options[0];
|
||||
spyOn(network, 'associateFloatingIp').and.callThrough();
|
||||
spyOn($modalInstance, 'close');
|
||||
ctrl.save();
|
||||
expect(ctrl.saving).toBe(true);
|
||||
expect(network.associateFloatingIp).toHaveBeenCalledWith('ip2', 'port_address');
|
||||
expect($modalInstance.close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should allocate floating IP if floating IP pool selected', function() {
|
||||
ctrl = $controller('AssociateFloatingIpModalController');
|
||||
ctrl.selected = ctrl.options[1];
|
||||
spyOn(network, 'allocateFloatingIp').and.callThrough();
|
||||
spyOn(network, 'associateFloatingIp').and.callThrough();
|
||||
spyOn($modalInstance, 'close');
|
||||
ctrl.save();
|
||||
expect(ctrl.saving).toBe(true);
|
||||
expect(network.allocateFloatingIp).toHaveBeenCalledWith('pool1');
|
||||
expect(network.associateFloatingIp).toHaveBeenCalledWith('foo', 'port_address');
|
||||
expect($modalInstance.close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should dismiss modal if cancel clicked', function() {
|
||||
ctrl = $controller('AssociateFloatingIpModalController');
|
||||
spyOn($modalInstance, 'dismiss');
|
||||
ctrl.cancel();
|
||||
expect($modalInstance.dismiss).toHaveBeenCalledWith('cancel');
|
||||
});
|
||||
|
||||
it('should not dismiss modal if save fails', function() {
|
||||
ctrl = $controller('AssociateFloatingIpModalController');
|
||||
ctrl.selected = ctrl.options[0];
|
||||
associateFail = true;
|
||||
spyOn($modalInstance, 'dismiss');
|
||||
ctrl.save();
|
||||
expect($modalInstance.dismiss).not.toHaveBeenCalled();
|
||||
expect(ctrl.saving).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
@ -0,0 +1,33 @@
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">
|
||||
<span translate>Associate Floating IP Address</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p translate>Select a floating IP address to associate with the load balancer or a floating IP pool in which to allocate a new floating IP address.</p>
|
||||
<div ng-form="form">
|
||||
<div class="row form-group">
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<div class="form-field required">
|
||||
<label translate class="on-top" for="floating-ip">Floating IP address or pool</label>
|
||||
<select class="form-control input-sm" name="floating-ip" id="floating-ip"
|
||||
ng-options="item.name group by item.group for item in modal.options"
|
||||
ng-model="modal.selected" ng-required="true">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-sm btn-default" ng-click="modal.cancel()">
|
||||
<span class="fa fa-close"></span>
|
||||
<span translate>Cancel</span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-primary"
|
||||
ng-click="modal.save()"
|
||||
ng-disabled="form.$invalid || modal.saving">
|
||||
<span class="fa" ng-class="modal.saving ? 'fa-spinner fa-spin' : 'fa-check'"></span>
|
||||
<span translate>Associate</span>
|
||||
</button>
|
||||
</div>
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.loadbalancers')
|
||||
.factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip.modal.service',
|
||||
modalService);
|
||||
|
||||
modalService.$inject = [
|
||||
'$q',
|
||||
'$modal',
|
||||
'$route',
|
||||
'horizon.dashboard.project.lbaasv2.basePath',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.app.core.openstack-service-api.network',
|
||||
'horizon.framework.util.q.extensions',
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.framework.util.i18n.gettext'
|
||||
];
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @ngname horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip.modal.service
|
||||
*
|
||||
* @description
|
||||
* Provides the service for the Load Balancer Associate Floating IP action.
|
||||
*
|
||||
* @param $q The angular service for promises.
|
||||
* @param $modal The angular bootstrap $modal service.
|
||||
* @param $route The angular $route service.
|
||||
* @param basePath The LBaaS v2 module base path.
|
||||
* @param policy The horizon policy service.
|
||||
* @param network The horizon network API service.
|
||||
* @param qExtensions Horizon extensions to the $q service.
|
||||
* @param toastService The horizon toast service.
|
||||
* @param gettext The horizon gettext function for translation.
|
||||
*
|
||||
* @returns The Associate Floating IP modal service.
|
||||
*/
|
||||
|
||||
function modalService(
|
||||
$q,
|
||||
$modal,
|
||||
$route,
|
||||
basePath,
|
||||
policy,
|
||||
network,
|
||||
qExtensions,
|
||||
toastService,
|
||||
gettext
|
||||
) {
|
||||
var service = {
|
||||
perform: open,
|
||||
allowed: allowed
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
////////////
|
||||
|
||||
function allowed(item) {
|
||||
return $q.all([
|
||||
qExtensions.booleanAsPromise(item.floating_ip && !item.floating_ip.ip),
|
||||
// This rule is made up and should therefore always pass. At some point there will
|
||||
// likely be a valid rule similar to this that we will want to use.
|
||||
policy.ifAllowed({ rules: [['neutron', 'loadbalancer_associate_floating_ip']] })
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name open
|
||||
*
|
||||
* @description
|
||||
* Open the modal.
|
||||
*
|
||||
* @param item The row item from the table action.
|
||||
* @returns undefined
|
||||
*/
|
||||
|
||||
function open(item) {
|
||||
var spec = {
|
||||
backdrop: 'static',
|
||||
controller: 'AssociateFloatingIpModalController as modal',
|
||||
templateUrl: basePath + 'loadbalancers/actions/associate-ip/modal.html',
|
||||
resolve: {
|
||||
loadbalancer: function() {
|
||||
return item;
|
||||
},
|
||||
floatingIps: function() {
|
||||
return network.getFloatingIps().then(getResponseItems);
|
||||
},
|
||||
floatingIpPools: function() {
|
||||
return network.getFloatingIpPools().then(getResponseItems);
|
||||
}
|
||||
}
|
||||
};
|
||||
$modal.open(spec).result.then(onModalClose);
|
||||
}
|
||||
|
||||
function onModalClose() {
|
||||
toastService.add('success', gettext('Associating floating IP with load balancer.'));
|
||||
$route.reload();
|
||||
}
|
||||
|
||||
function getResponseItems(response) {
|
||||
return response.data.items;
|
||||
}
|
||||
|
||||
}
|
||||
})();
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 Load Balancers Table Associate IP Service', function() {
|
||||
var service, policy, $scope, $route, item, $modal, toast;
|
||||
|
||||
function allowed(item) {
|
||||
spyOn(policy, 'ifAllowed').and.returnValue(true);
|
||||
var promise = service.allowed(item);
|
||||
var allowed;
|
||||
promise.then(function() {
|
||||
allowed = true;
|
||||
}, function() {
|
||||
allowed = false;
|
||||
});
|
||||
$scope.$apply();
|
||||
expect(policy.ifAllowed).toHaveBeenCalledWith(
|
||||
{rules: [['neutron', 'loadbalancer_associate_floating_ip']]});
|
||||
return allowed;
|
||||
}
|
||||
|
||||
beforeEach(module('horizon.framework.util'));
|
||||
beforeEach(module('horizon.framework.conf'));
|
||||
beforeEach(module('horizon.framework.widgets'));
|
||||
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||
beforeEach(module('horizon.dashboard.project.lbaasv2'));
|
||||
|
||||
beforeEach(function() {
|
||||
item = { id: '1', name: 'First', floating_ip: {} };
|
||||
});
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
var fakePromise = function(response) {
|
||||
return {
|
||||
then: function(func) {
|
||||
return func(response);
|
||||
}
|
||||
};
|
||||
};
|
||||
$provide.value('$modal', {
|
||||
open: function() {
|
||||
return {
|
||||
result: fakePromise()
|
||||
};
|
||||
}
|
||||
});
|
||||
$provide.value('horizon.app.core.openstack-service-api.network', {
|
||||
getFloatingIps: function() {
|
||||
return fakePromise({ data: { items: 'foo' } });
|
||||
},
|
||||
getFloatingIpPools: function() {
|
||||
return fakePromise({ data: { items: 'bar' } });
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(inject(function ($injector) {
|
||||
policy = $injector.get('horizon.app.core.openstack-service-api.policy');
|
||||
toast = $injector.get('horizon.framework.widgets.toast.service');
|
||||
$scope = $injector.get('$rootScope').$new();
|
||||
$route = $injector.get('$route');
|
||||
$modal = $injector.get('$modal');
|
||||
service = $injector.get(
|
||||
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip.modal.service');
|
||||
}));
|
||||
|
||||
it('should have the "allowed" and "perform" functions', function() {
|
||||
expect(service.allowed).toBeDefined();
|
||||
expect(service.perform).toBeDefined();
|
||||
});
|
||||
|
||||
it('should check policy to allow the action', function() {
|
||||
expect(allowed(item)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not allow action if floating IP already associated', function() {
|
||||
item.floating_ip.ip = 'foo';
|
||||
expect(allowed(item)).toBe(false);
|
||||
});
|
||||
|
||||
it('should open the modal', function() {
|
||||
spyOn($modal, 'open').and.callThrough();
|
||||
service.perform(item);
|
||||
$scope.$apply();
|
||||
expect($modal.open.calls.count()).toBe(1);
|
||||
});
|
||||
|
||||
it('should resolve data for passing into the modal', function() {
|
||||
spyOn($modal, 'open').and.callThrough();
|
||||
service.perform(item);
|
||||
$scope.$apply();
|
||||
|
||||
var resolve = $modal.open.calls.argsFor(0)[0].resolve;
|
||||
expect(resolve).toBeDefined();
|
||||
expect(resolve.loadbalancer).toBeDefined();
|
||||
expect(resolve.loadbalancer()).toEqual(item);
|
||||
expect(resolve.floatingIps).toBeDefined();
|
||||
expect(resolve.floatingIps()).toBe('foo');
|
||||
expect(resolve.floatingIpPools).toBeDefined();
|
||||
expect(resolve.floatingIpPools()).toBe('bar');
|
||||
});
|
||||
|
||||
it('should show message and reload page upon closing modal', function() {
|
||||
spyOn(toast, 'add');
|
||||
spyOn($route, 'reload');
|
||||
service.perform(item);
|
||||
$scope.$apply();
|
||||
expect(toast.add).toHaveBeenCalledWith('success',
|
||||
'Associating floating IP with load balancer.');
|
||||
expect($route.reload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.loadbalancers')
|
||||
.factory(
|
||||
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip.modal.service',
|
||||
modalService);
|
||||
|
||||
modalService.$inject = [
|
||||
'$q',
|
||||
'$route',
|
||||
'horizon.framework.widgets.modal.deleteModalService',
|
||||
'horizon.app.core.openstack-service-api.network',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.framework.util.q.extensions',
|
||||
'horizon.framework.util.i18n.gettext'
|
||||
];
|
||||
|
||||
/**
|
||||
* @ngDoc factory
|
||||
* @name horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip.modal.service
|
||||
* @description
|
||||
* Brings up the disassociate floating IP confirmation modal dialog.
|
||||
* On submit, dsiassociates the floating IP address from the load balancer.
|
||||
* On cancel, does nothing.
|
||||
* @param $q The angular service for promises.
|
||||
* @param $route The angular $route service.
|
||||
* @param deleteModal The horizon delete modal service.
|
||||
* @param network The horizon network API service.
|
||||
* @param policy The horizon policy service.
|
||||
* @param qExtensions Horizon extensions to the $q service.
|
||||
* @param gettext The horizon gettext function for translation.
|
||||
* @returns The load balancers table row delete service.
|
||||
*/
|
||||
|
||||
function modalService($q, $route, deleteModal, network, policy, qExtensions, gettext) {
|
||||
var loadbalancer;
|
||||
var context = {
|
||||
labels: {
|
||||
title: gettext('Confirm Disassociate Floating IP Address'),
|
||||
/* eslint-disable max-len */
|
||||
message: gettext('You are about to disassociate the floating IP address from load balancer "%s". Please confirm.'),
|
||||
/* eslint-enable max-len */
|
||||
submit: gettext('Disassociate'),
|
||||
success: gettext('Disassociated floating IP address from load balancer: %s.'),
|
||||
error: gettext('Unable to disassociate floating IP address from load balancer: %s.')
|
||||
},
|
||||
deleteEntity: disassociate
|
||||
};
|
||||
|
||||
var service = {
|
||||
perform: perform,
|
||||
allowed: allowed
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
//////////////
|
||||
|
||||
function perform(item) {
|
||||
loadbalancer = item;
|
||||
deleteModal.open({ $emit: actionComplete }, [item], context);
|
||||
}
|
||||
|
||||
function allowed(item) {
|
||||
return $q.all([
|
||||
qExtensions.booleanAsPromise(item.floating_ip && !!item.floating_ip.ip),
|
||||
// This rule is made up and should therefore always pass. At some point there will
|
||||
// likely be a valid rule similar to this that we will want to use.
|
||||
policy.ifAllowed({ rules: [['neutron', 'loadbalancer_disassociate_floating_ip']] })
|
||||
]);
|
||||
}
|
||||
|
||||
function disassociate() {
|
||||
return network.disassociateFloatingIp(loadbalancer.floating_ip.id);
|
||||
}
|
||||
|
||||
function actionComplete() {
|
||||
$route.reload();
|
||||
}
|
||||
|
||||
}
|
||||
})();
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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 Load Balancers Table Disassociate IP Service', function() {
|
||||
var service, policy, modal, network, $scope, $route, item;
|
||||
|
||||
function allowed(item) {
|
||||
spyOn(policy, 'ifAllowed').and.returnValue(true);
|
||||
var promise = service.allowed(item);
|
||||
var allowed;
|
||||
promise.then(function() {
|
||||
allowed = true;
|
||||
}, function() {
|
||||
allowed = false;
|
||||
});
|
||||
$scope.$apply();
|
||||
expect(policy.ifAllowed).toHaveBeenCalledWith(
|
||||
{rules: [['neutron', 'loadbalancer_disassociate_floating_ip']]});
|
||||
return allowed;
|
||||
}
|
||||
|
||||
beforeEach(module('horizon.framework.util'));
|
||||
beforeEach(module('horizon.framework.conf'));
|
||||
beforeEach(module('horizon.framework.widgets'));
|
||||
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||
beforeEach(module('horizon.dashboard.project.lbaasv2'));
|
||||
|
||||
beforeEach(function() {
|
||||
item = { id: '1', name: 'First', floating_ip: { id: 'ip1', ip: '1' } };
|
||||
});
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
var fakePromise = {
|
||||
then: function(func) {
|
||||
func();
|
||||
}
|
||||
};
|
||||
$provide.value('$modal', {
|
||||
open: function() {
|
||||
return {
|
||||
result: fakePromise
|
||||
};
|
||||
}
|
||||
});
|
||||
$provide.value('horizon.app.core.openstack-service-api.network', {
|
||||
disassociateFloatingIp: function() {
|
||||
return fakePromise;
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(inject(function ($injector) {
|
||||
policy = $injector.get('horizon.app.core.openstack-service-api.policy');
|
||||
network = $injector.get('horizon.app.core.openstack-service-api.network');
|
||||
modal = $injector.get('horizon.framework.widgets.modal.deleteModalService');
|
||||
$scope = $injector.get('$rootScope').$new();
|
||||
$route = $injector.get('$route');
|
||||
service = $injector.get(
|
||||
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip.modal.service');
|
||||
}));
|
||||
|
||||
it('should have the "allowed" and "perform" functions', function() {
|
||||
expect(service.allowed).toBeDefined();
|
||||
expect(service.perform).toBeDefined();
|
||||
});
|
||||
|
||||
it('should check policy to allow action', function() {
|
||||
expect(allowed(item)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not allow action if floating IP not associated', function() {
|
||||
delete item.floating_ip.ip;
|
||||
expect(allowed(item)).toBe(false);
|
||||
});
|
||||
|
||||
it('should open the delete modal', function() {
|
||||
spyOn(modal, 'open');
|
||||
service.perform(item);
|
||||
$scope.$apply();
|
||||
expect(modal.open.calls.count()).toBe(1);
|
||||
var args = modal.open.calls.argsFor(0);
|
||||
expect(args.length).toBe(3);
|
||||
expect(args[0]).toEqual({ $emit: jasmine.any(Function) });
|
||||
expect(args[1]).toEqual([jasmine.objectContaining({ id: '1' })]);
|
||||
expect(args[2]).toEqual(jasmine.objectContaining({
|
||||
labels: jasmine.any(Object),
|
||||
deleteEntity: jasmine.any(Function)
|
||||
}));
|
||||
expect(args[2].labels.title).toBe('Confirm Disassociate Floating IP Address');
|
||||
});
|
||||
|
||||
it('should pass function to modal that disassociates the IP address', function() {
|
||||
spyOn(modal, 'open').and.callThrough();
|
||||
spyOn(network, 'disassociateFloatingIp').and.callThrough();
|
||||
service.perform(item);
|
||||
$scope.$apply();
|
||||
expect(network.disassociateFloatingIp.calls.count()).toBe(1);
|
||||
expect(network.disassociateFloatingIp).toHaveBeenCalledWith('ip1');
|
||||
});
|
||||
|
||||
it('should reload page after action completes', function() {
|
||||
spyOn($route, 'reload');
|
||||
service.perform(item);
|
||||
$scope.$apply();
|
||||
expect($route.reload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
})();
|
@ -26,7 +26,10 @@
|
||||
'$route',
|
||||
'horizon.dashboard.project.lbaasv2.workflow.modal',
|
||||
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.delete',
|
||||
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.associate-ip.modal.service',
|
||||
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.disassociate-ip.modal.service',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.app.core.openstack-service-api.network',
|
||||
'horizon.framework.util.q.extensions',
|
||||
'horizon.framework.util.i18n.gettext'
|
||||
];
|
||||
@ -42,14 +45,27 @@
|
||||
* @param $route The angular $route service.
|
||||
* @param workflowModal The LBaaS workflow modal service.
|
||||
* @param deleteService The load balancer delete service.
|
||||
* @param associateIp The associate floating IP modal service.
|
||||
* @param disassociateIp The disassociate floating IP modal service.
|
||||
* @param policy The horizon policy service.
|
||||
* @param network The horizon network API service.
|
||||
* @param qExtensions Horizon extensions to the $q service.
|
||||
* @param gettext The horizon gettext function for translation.
|
||||
* @returns Load balancers table batch actions service object.
|
||||
* @returns Load balancers table row actions service object.
|
||||
*/
|
||||
|
||||
function tableRowActions($q, $route, workflowModal, deleteService, policy, qExtensions, gettext) {
|
||||
|
||||
function tableRowActions(
|
||||
$q,
|
||||
$route,
|
||||
workflowModal,
|
||||
deleteService,
|
||||
associateIp,
|
||||
disassociateIp,
|
||||
policy,
|
||||
network,
|
||||
qExtensions,
|
||||
gettext
|
||||
) {
|
||||
var edit = workflowModal.init({
|
||||
controller: 'EditLoadBalancerWizardController',
|
||||
message: gettext('The load balancer has been updated.'),
|
||||
@ -71,7 +87,17 @@
|
||||
template: {
|
||||
text: gettext('Edit')
|
||||
}
|
||||
}, {
|
||||
},{
|
||||
service: associateIp,
|
||||
template: {
|
||||
text: gettext('Associate Floating IP')
|
||||
}
|
||||
},{
|
||||
service: disassociateIp,
|
||||
template: {
|
||||
text: gettext('Disassociate Floating IP')
|
||||
}
|
||||
},{
|
||||
service: deleteService,
|
||||
template: {
|
||||
text: gettext('Delete Load Balancer'),
|
||||
|
@ -69,9 +69,11 @@
|
||||
}));
|
||||
|
||||
it('should define correct table row actions', function() {
|
||||
expect(actions.length).toBe(2);
|
||||
expect(actions.length).toBe(4);
|
||||
expect(actions[0].template.text).toBe('Edit');
|
||||
expect(actions[1].template.text).toBe('Delete Load Balancer');
|
||||
expect(actions[1].template.text).toBe('Associate Floating IP');
|
||||
expect(actions[2].template.text).toBe('Disassociate Floating IP');
|
||||
expect(actions[3].template.text).toBe('Delete Load Balancer');
|
||||
});
|
||||
|
||||
it('should allow editing an ACTIVE load balancer', function() {
|
||||
|
@ -53,7 +53,7 @@
|
||||
////////////////////////////////
|
||||
|
||||
function init() {
|
||||
api.getLoadBalancer($routeParams.loadbalancerId).success(success);
|
||||
api.getLoadBalancer($routeParams.loadbalancerId, true).success(success);
|
||||
}
|
||||
|
||||
function success(response) {
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
it('should invoke lbaasv2 apis', function() {
|
||||
createController();
|
||||
expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('1234');
|
||||
expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('1234', true);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -32,7 +32,11 @@
|
||||
</div>
|
||||
<div>
|
||||
<dt translate>Admin State Up</dt>
|
||||
<dd>{$ ::ctrl.loadbalancer.admin_state_up | yesno $}</dd>
|
||||
<dd>{$ ctrl.loadbalancer.admin_state_up | yesno $}</dd>
|
||||
</div>
|
||||
<div ng-if="ctrl.loadbalancer.floating_ip !== undefined">
|
||||
<dt translate>Floating IP Address</dt>
|
||||
<dd>{$ ctrl.loadbalancer.floating_ip.ip || 'None' | translate $}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt translate>Load Balancer ID</dt>
|
||||
|
@ -57,7 +57,7 @@
|
||||
////////////////////////////////
|
||||
|
||||
function init() {
|
||||
api.getLoadBalancers().success(success);
|
||||
api.getLoadBalancers(true).success(success);
|
||||
}
|
||||
|
||||
function success(response) {
|
||||
|
@ -117,6 +117,10 @@
|
||||
<dt translate>Provider</dt>
|
||||
<dd>{$ ::item.provider $}</dd>
|
||||
</dl>
|
||||
<dl class="col-sm-2" ng-if="item.floating_ip !== undefined">
|
||||
<dt translate>Floating IP Address</dt>
|
||||
<dd>{$ item.floating_ip.ip || 'None' | translate $}</dd>
|
||||
</dl>
|
||||
<dl class="col-sm-2">
|
||||
<dt translate>Admin State Up</dt>
|
||||
<dd>{$ ::item.admin_state_up | yesno $}</dd>
|
||||
@ -127,11 +131,11 @@
|
||||
</dl>
|
||||
<dl class="col-sm-2">
|
||||
<dt translate>Subnet ID</dt>
|
||||
<dd>{$ ::item.vip_subnet_id $}</dd>
|
||||
<dd><a target="_self" ng-href="project/networks/subnets/{$ ::item.vip_subnet_id $}/detail">{$ ::item.vip_subnet_id $}</a></dd>
|
||||
</dl>
|
||||
<dl class="col-sm-2">
|
||||
<dt translate>Port ID</dt>
|
||||
<dd>{$ ::item.vip_port_id $}</dd>
|
||||
<dd><a target="_self" ng-href="project/networks/ports/{$ ::item.vip_port_id $}/detail">{$ ::item.vip_port_id $}</a></dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user