Add 'provider' select box in load balancer form
Get the list of enabled providers, filter out aliases and display a select box when creating a load balancer if there are 2 or more available providers. Change-Id: Id14b102b3d5ed079a3ef5d1cfba08744fd3cb5fa
This commit is contained in:
parent
1a29983ebc
commit
1ff3f509e6
|
@ -141,6 +141,9 @@ def create_loadbalancer(request):
|
|||
availability_zone = data['loadbalancer'].get('availability_zone')
|
||||
if availability_zone:
|
||||
build_kwargs['availability_zone'] = availability_zone
|
||||
provider = data['loadbalancer'].get('provider')
|
||||
if provider:
|
||||
build_kwargs['provider'] = provider
|
||||
|
||||
loadbalancer = conn.load_balancer.create_load_balancer(**build_kwargs)
|
||||
if data.get('listener'):
|
||||
|
@ -1413,3 +1416,24 @@ class AvailabilityZones(generic.View):
|
|||
)
|
||||
|
||||
return {'items': availability_zone_list}
|
||||
|
||||
|
||||
@urls.register
|
||||
class Providers(generic.View):
|
||||
"""API for load balancer providers.
|
||||
|
||||
"""
|
||||
url_regex = r'lbaas/providers/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""List of providers.
|
||||
|
||||
The listing result is an object with property "items".
|
||||
"""
|
||||
conn = _get_sdk_connection(request)
|
||||
provider_list = _sdk_object_to_list(
|
||||
conn.load_balancer.providers()
|
||||
)
|
||||
|
||||
return {'items': provider_list}
|
||||
|
|
|
@ -81,7 +81,8 @@
|
|||
getFlavorProfile: getFlavorProfile,
|
||||
deleteFlavorProfile: deleteFlavorProfile,
|
||||
createFlavorProfile: createFlavorProfile,
|
||||
editFlavorProfile: editFlavorProfile
|
||||
editFlavorProfile: editFlavorProfile,
|
||||
getProviders: getProviders
|
||||
};
|
||||
|
||||
return service;
|
||||
|
@ -915,5 +916,24 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Providers
|
||||
|
||||
/**
|
||||
* @name horizon.app.core.openstack-service-api.lbaasv2.getProviders
|
||||
* @description
|
||||
* Get the list of providers.
|
||||
*
|
||||
* The listing result is an object with property "items". Each item is
|
||||
* a provider.
|
||||
*/
|
||||
|
||||
function getProviders() {
|
||||
var params = {params: {}};
|
||||
return apiService.get('/api/lbaas/providers/', params)
|
||||
.error(function () {
|
||||
toastService.add('error', gettext('Unable to retrieve providers.'));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}());
|
||||
|
|
|
@ -337,6 +337,14 @@
|
|||
testInput: [],
|
||||
data: { params: {} }
|
||||
},
|
||||
{
|
||||
func: 'getProviders',
|
||||
method: 'get',
|
||||
path: '/api/lbaas/providers/',
|
||||
error: 'Unable to retrieve providers.',
|
||||
testInput: [],
|
||||
data: { params: {} }
|
||||
},
|
||||
{
|
||||
func: 'createLoadBalancer',
|
||||
method: 'post',
|
||||
|
|
|
@ -136,12 +136,35 @@
|
|||
}
|
||||
};
|
||||
|
||||
ctrl.providerColumns = [{
|
||||
label: gettext('Provider'),
|
||||
value: 'name'
|
||||
}, {
|
||||
label: gettext('Description'),
|
||||
value: 'description'
|
||||
}];
|
||||
|
||||
ctrl.providerOptions = [];
|
||||
|
||||
ctrl.providerShorthand = function(provider) {
|
||||
return provider.name;
|
||||
};
|
||||
|
||||
ctrl.setProvider = function(option) {
|
||||
if (option) {
|
||||
$scope.model.spec.loadbalancer.provider = option;
|
||||
} else {
|
||||
$scope.model.spec.loadbalancer.provider = null;
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.dataLoaded = false;
|
||||
ctrl._checkLoaded = function() {
|
||||
if ($scope.model.initialized) {
|
||||
ctrl.buildSubnetOptions();
|
||||
ctrl.buildFlavorOptions();
|
||||
ctrl.buildAvailabilityZoneOptions();
|
||||
ctrl.buildProviderOptions();
|
||||
ctrl.dataLoaded = true;
|
||||
}
|
||||
};
|
||||
|
@ -169,6 +192,9 @@
|
|||
$scope.$watchCollection('model.availability_zones', function() {
|
||||
ctrl._checkLoaded();
|
||||
});
|
||||
$scope.$watchCollection('model.providers', function() {
|
||||
ctrl._checkLoaded();
|
||||
});
|
||||
$scope.$watch('model.initialized', function() {
|
||||
ctrl._checkLoaded();
|
||||
});
|
||||
|
@ -194,5 +220,14 @@
|
|||
return $scope.model.availability_zones[key];
|
||||
});
|
||||
};
|
||||
ctrl.buildProviderOptions = function() {
|
||||
ctrl.providerOptions = Object.keys($scope.model.providers).filter(function(key) {
|
||||
// filter out the 'octavia' provider, it's an alias for 'amphora' and
|
||||
// it's deprecated
|
||||
return key !== "octavia";
|
||||
}).map(function(key) {
|
||||
return $scope.model.providers[key];
|
||||
});
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
beforeEach(module('horizon.dashboard.project.lbaasv2'));
|
||||
|
||||
describe('LoadBalancerDetailsController', function() {
|
||||
var ctrl, scope, mockSubnets, mockFlavors, mockAvailabilityZones;
|
||||
var ctrl, scope, mockSubnets, mockFlavors, mockAvailabilityZones,
|
||||
mockProviders;
|
||||
beforeEach(inject(function($controller, $rootScope) {
|
||||
mockSubnets = [{
|
||||
id: '7262744a-e1e4-40d7-8833-18193e8de191',
|
||||
|
@ -79,6 +80,14 @@
|
|||
is_enabled: true
|
||||
}];
|
||||
|
||||
mockProviders = [{
|
||||
name: 'amphora',
|
||||
description: 'amphora description'
|
||||
}, {
|
||||
name: 'octavia',
|
||||
description: 'octavia description'
|
||||
}];
|
||||
|
||||
scope = $rootScope.$new();
|
||||
scope.model = {
|
||||
networks: {
|
||||
|
@ -104,6 +113,11 @@
|
|||
is_enabled: true
|
||||
}
|
||||
},
|
||||
providers: {
|
||||
name: 'amphora',
|
||||
description: 'amphora description'
|
||||
},
|
||||
|
||||
subnets: [{}, {}],
|
||||
spec: {
|
||||
loadbalancer: {
|
||||
|
@ -118,6 +132,7 @@
|
|||
spyOn(ctrl, 'buildSubnetOptions').and.callThrough();
|
||||
spyOn(ctrl, 'buildFlavorOptions').and.callThrough();
|
||||
spyOn(ctrl, 'buildAvailabilityZoneOptions').and.callThrough();
|
||||
spyOn(ctrl, 'buildProviderOptions').and.callThrough();
|
||||
spyOn(ctrl, '_checkLoaded').and.callThrough();
|
||||
}));
|
||||
|
||||
|
@ -168,6 +183,15 @@
|
|||
);
|
||||
});
|
||||
|
||||
it('should create provider shorthand text', function() {
|
||||
expect(ctrl.providerShorthand(mockProviders[0])).toBe(
|
||||
'amphora'
|
||||
);
|
||||
expect(ctrl.providerShorthand(mockProviders[1])).toBe(
|
||||
'octavia'
|
||||
);
|
||||
});
|
||||
|
||||
it('should set subnet', function() {
|
||||
ctrl.setSubnet(mockSubnets[0]);
|
||||
expect(scope.model.spec.loadbalancer.vip_subnet_id).toBe(mockSubnets[0]);
|
||||
|
@ -189,6 +213,13 @@
|
|||
expect(scope.model.spec.loadbalancer.availability_zone).toBe(null);
|
||||
});
|
||||
|
||||
it('should set provider', function() {
|
||||
ctrl.setProvider(mockProviders[0]);
|
||||
expect(scope.model.spec.loadbalancer.provider).toBe(mockProviders[0]);
|
||||
ctrl.setProvider(null);
|
||||
expect(scope.model.spec.loadbalancer.provider).toBe(null);
|
||||
});
|
||||
|
||||
it('should initialize watchers', function() {
|
||||
ctrl.$onInit();
|
||||
|
||||
|
@ -208,6 +239,10 @@
|
|||
scope.$apply();
|
||||
expect(ctrl._checkLoaded).toHaveBeenCalled();
|
||||
|
||||
scope.model.providers = {};
|
||||
scope.$apply();
|
||||
expect(ctrl._checkLoaded).toHaveBeenCalled();
|
||||
|
||||
scope.model.initialized = true;
|
||||
|
||||
scope.$apply();
|
||||
|
@ -278,6 +313,13 @@
|
|||
//expect(ctrl.buildSubnetOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should initialize provider watcher', function() {
|
||||
ctrl.$onInit();
|
||||
|
||||
scope.model.providers = {};
|
||||
scope.$apply();
|
||||
});
|
||||
|
||||
it('should produce flavor column data', function() {
|
||||
expect(ctrl.flavorColumns).toBeDefined();
|
||||
|
||||
|
@ -298,6 +340,13 @@
|
|||
expect(ctrl.availabilityZoneColumns[0].value).toBe('name');
|
||||
});
|
||||
|
||||
it('should produce provider column data', function() {
|
||||
expect(ctrl.providerColumns).toBeDefined();
|
||||
|
||||
expect(ctrl.providerColumns[0].label).toBe('Provider');
|
||||
expect(ctrl.providerColumns[0].value).toBe('name');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -35,6 +35,28 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-if="ctrl.providerOptions.length > 1">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<label class="control-label">
|
||||
<translate>Provider</translate>
|
||||
</label>
|
||||
<!-- value="model.spec.loadbalancer.provider " -->
|
||||
<filter-select
|
||||
shorthand="ctrl.providerShorthand"
|
||||
|
||||
on-select="ctrl.setProvider(option)"
|
||||
disabled="model.context.id"
|
||||
columns="ctrl.providerColumns"
|
||||
options="ctrl.providerOptions"
|
||||
loaded="ctrl.dataLoaded"
|
||||
|
||||
ng-model="model.spec.loadbalancer.provider"
|
||||
></filter-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
|
|
|
@ -104,6 +104,7 @@
|
|||
{ label: gettext('Yes'), value: true },
|
||||
{ label: gettext('No'), value: false }
|
||||
],
|
||||
providers: {},
|
||||
|
||||
/**
|
||||
* api methods for UI controllers
|
||||
|
@ -153,7 +154,8 @@
|
|||
vip_subnet_id: null,
|
||||
flavor_id: null,
|
||||
availability_zone: null,
|
||||
admin_state_up: true
|
||||
admin_state_up: true,
|
||||
provider: null
|
||||
},
|
||||
listener: {
|
||||
id: null,
|
||||
|
@ -270,6 +272,7 @@
|
|||
return $q.all([
|
||||
lbaasv2API.getFlavors().then(onGetFlavors),
|
||||
lbaasv2API.getAvailabilityZones().then(onGetAvailabilityZones),
|
||||
lbaasv2API.getProviders().then(onGetProviders),
|
||||
neutronAPI.getSubnets().then(onGetSubnets),
|
||||
neutronAPI.getPorts().then(onGetPorts),
|
||||
neutronAPI.getNetworks().then(onGetNetworks),
|
||||
|
@ -296,6 +299,12 @@
|
|||
});
|
||||
}
|
||||
|
||||
function onGetProviders(response) {
|
||||
angular.forEach(response.data.items, function(value) {
|
||||
model.providers[value.name] = value;
|
||||
});
|
||||
}
|
||||
|
||||
function initCreateListener(keymanagerPromise) {
|
||||
model.context.submit = createListener;
|
||||
return $q.all([
|
||||
|
@ -359,9 +368,10 @@
|
|||
lbaasv2API.getFlavors().then(onGetFlavors),
|
||||
lbaasv2API.getAvailabilityZones().then(onGetAvailabilityZones),
|
||||
lbaasv2API.getLoadBalancer(model.context.id).then(onGetLoadBalancer),
|
||||
lbaasv2API.getProviders().then(onGetProviders),
|
||||
neutronAPI.getSubnets().then(onGetSubnets),
|
||||
neutronAPI.getNetworks().then(onGetNetworks)
|
||||
]).then(initSubnet).then(initFlavor).then(initAvailabilityZone);
|
||||
]).then(initSubnet).then(initFlavor).then(initAvailabilityZone).then(initProvider);
|
||||
}
|
||||
|
||||
function initEditListener() {
|
||||
|
@ -486,6 +496,10 @@
|
|||
finalSpec.loadbalancer.availability_zone = finalSpec.loadbalancer.availability_zone.name;
|
||||
}
|
||||
|
||||
if (angular.isObject(finalSpec.loadbalancer.provider)) {
|
||||
finalSpec.loadbalancer.provider = finalSpec.loadbalancer.provider.name;
|
||||
}
|
||||
|
||||
// Load balancer requires vip_subnet_id
|
||||
if (!finalSpec.loadbalancer.vip_subnet_id) {
|
||||
delete finalSpec.loadbalancer;
|
||||
|
@ -497,6 +511,7 @@
|
|||
if (context.resource === 'loadbalancer' && context.id) {
|
||||
delete finalSpec.flavor_id;
|
||||
delete finalSpec.availability_zone;
|
||||
delete finalSpec.provider;
|
||||
delete finalSpec.vip_subnet_id;
|
||||
delete finalSpec.vip_address;
|
||||
}
|
||||
|
@ -785,6 +800,7 @@
|
|||
spec.flavor_id = loadbalancer.flavor_id;
|
||||
spec.availability_zone = loadbalancer.availability_zone;
|
||||
spec.admin_state_up = loadbalancer.admin_state_up;
|
||||
spec.provider = loadbalancer.provider;
|
||||
}
|
||||
|
||||
function setListenerSpec(listener) {
|
||||
|
@ -952,6 +968,11 @@
|
|||
model.spec.loadbalancer.availability_zone];
|
||||
}
|
||||
|
||||
function initProvider() {
|
||||
model.spec.loadbalancer.provider = model.providers[
|
||||
model.spec.loadbalancer.provider];
|
||||
}
|
||||
|
||||
function mapSubnetObj(subnetId) {
|
||||
var subnet = model.subnets.filter(function mapSubnet(subnet) {
|
||||
return subnet.id === subnetId;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
describe('LBaaS v2 Workflow Model Service', function() {
|
||||
var model, $q, scope, listenerResources, barbicanEnabled,
|
||||
certificatesError, mockNetworks, mockFlavors,
|
||||
mockAvailabilityZones;
|
||||
mockAvailabilityZones, mockProviders;
|
||||
var includeChildResources = true;
|
||||
|
||||
beforeEach(module('horizon.framework.util.i18n'));
|
||||
|
@ -116,6 +116,16 @@
|
|||
id: 'az2'
|
||||
}
|
||||
};
|
||||
mockProviders = {
|
||||
amphora: {
|
||||
name: 'amphora',
|
||||
description: 'amphora description'
|
||||
},
|
||||
octavia: {
|
||||
name: 'octavia',
|
||||
description: 'octavia description'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
|
@ -141,7 +151,8 @@
|
|||
vip_subnet_id: 'subnet-1',
|
||||
flavor_id: 'flavor-1',
|
||||
availability_zone: 'az-1',
|
||||
description: ''
|
||||
description: '',
|
||||
provider: 'amphora'
|
||||
};
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
@ -291,6 +302,19 @@
|
|||
deferred.resolve({data: {items: availabilityZones}});
|
||||
return deferred.promise;
|
||||
},
|
||||
getProviders: function() {
|
||||
var providers = [{
|
||||
name: 'amphora',
|
||||
description: 'amphora description'
|
||||
}, {
|
||||
name: 'octavia',
|
||||
description: 'octavia description'
|
||||
}];
|
||||
|
||||
var deferred = $q.defer();
|
||||
deferred.resolve({data: {items: providers}});
|
||||
return deferred.promise;
|
||||
},
|
||||
createLoadBalancer: function(spec) {
|
||||
return spec;
|
||||
},
|
||||
|
@ -543,6 +567,7 @@
|
|||
expect(model.networks).toEqual(mockNetworks);
|
||||
expect(model.flavors).toEqual(mockFlavors);
|
||||
expect(model.availability_zones).toEqual(mockAvailabilityZones);
|
||||
expect(model.providers).toEqual(mockProviders);
|
||||
expect(model.members.length).toBe(2);
|
||||
expect(model.certificates.length).toBe(3);
|
||||
expect(model.listenerPorts.length).toBe(0);
|
||||
|
@ -801,6 +826,7 @@
|
|||
expect(model.networks).toEqual(mockNetworks);
|
||||
expect(model.flavors).toEqual(mockFlavors);
|
||||
expect(model.availability_zones).toEqual(mockAvailabilityZones);
|
||||
expect(model.providers).toEqual(mockProviders);
|
||||
expect(model.members.length).toBe(0);
|
||||
expect(model.certificates.length).toBe(0);
|
||||
expect(model.listenerPorts.length).toBe(0);
|
||||
|
@ -1297,7 +1323,7 @@
|
|||
// to implement tests for them.
|
||||
it('has the right number of properties', function() {
|
||||
expect(Object.keys(model.spec).length).toBe(11);
|
||||
expect(Object.keys(model.spec.loadbalancer).length).toBe(7);
|
||||
expect(Object.keys(model.spec.loadbalancer).length).toBe(8);
|
||||
expect(Object.keys(model.spec.listener).length).toBe(15);
|
||||
expect(Object.keys(model.spec.l7policy).length).toBe(8);
|
||||
expect(Object.keys(model.spec.l7rule).length).toBe(7);
|
||||
|
@ -1548,6 +1574,8 @@
|
|||
model.spec.loadbalancer.flavor_id = model.flavors[Object.keys(model.flavors)[0]];
|
||||
model.spec.loadbalancer.availability_zone = model.availability_zones[
|
||||
Object.keys(model.availability_zones)[0]];
|
||||
model.spec.loadbalancer.provider = model.providers[
|
||||
Object.keys(model.providers)[0]];
|
||||
model.spec.listener.protocol = 'TCP';
|
||||
model.spec.listener.protocol_port = 80;
|
||||
model.spec.listener.connection_limit = 999;
|
||||
|
@ -1601,6 +1629,7 @@
|
|||
expect(finalSpec.loadbalancer.vip_subnet_id).toBe(model.subnets[0].id);
|
||||
expect(finalSpec.loadbalancer.admin_state_up).toBe(true);
|
||||
expect(finalSpec.loadbalancer.availability_zone).toBe('az_1');
|
||||
expect(finalSpec.loadbalancer.provider).toBe('amphora');
|
||||
|
||||
expect(finalSpec.listener.name).toBeUndefined();
|
||||
expect(finalSpec.listener.description).toBeUndefined();
|
||||
|
@ -1653,6 +1682,8 @@
|
|||
model.spec.loadbalancer.flavor_id = model.flavors[Object.keys(model.flavors)[0]];
|
||||
model.spec.loadbalancer.availability_zone = model.availability_zones[
|
||||
Object.keys(model.availability_zones)[0]];
|
||||
model.spec.loadbalancer.provider = model.providers[
|
||||
Object.keys(model.providers)[0]];
|
||||
model.spec.listener.protocol = 'TERMINATED_HTTPS';
|
||||
model.spec.listener.protocol_port = 443;
|
||||
model.spec.listener.connection_limit = 9999;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Add the possibility to select the provider for a new load balancer. A
|
||||
select box displays the list of available providers, if only one provider is
|
||||
enabled, the box is hidden.
|
Loading…
Reference in New Issue