Add initial workflow for creating load balancer

This adds the action for creating a new load balancer. This only
includes the first panel of the workflow which allows creating the
load balancer object itself. Follow on patches will add support for
the listener, pool, monitor, and members.

This also adds the missing .eslintrc file and fixes code that was
failing the eslint rules.

There is a slight problem with the styling of the action button that
is probably an issue in the horizon action-list widget. This can be
treated as a bug and fixed later.

Depends-On: I230995c069d94ca41d70582b9c665289f0acb86e
Partially-Implements: blueprint horizon-lbaas-v2-ui
Change-Id: Ie0bfe6bd41a60259bc8642d4e046b420a50df55f
This commit is contained in:
Justin Pomeroy 2015-12-02 14:47:34 -06:00
parent 8d863ab27b
commit f344259472
32 changed files with 1281 additions and 94 deletions

55
.eslintrc Normal file
View File

@ -0,0 +1,55 @@
# Set up globals
globals:
angular: false
extends: openstack
# Most environment options are not explicitly enabled or disabled, only
# included here for completeness' sake. They are commented out, because the
# global updates.py script would otherwise override them during a global
# requirements synchronization.
#
# Individual projects should choose which platforms they deploy to.
env:
# browser global variables.
browser: true
# Adds all of the Jasmine testing global variables for version 1.3 and 2.0.
jasmine: true
# Enable eslint-plugin-angular
plugins:
- angular
# Below we adjust rules specific to horizon's usage of openstack's linting
# rules, and its own plugin inclusions.
rules:
#############################################################################
# Disabled Rules from eslint-config-openstack
#############################################################################
valid-jsdoc: 1
no-undefined: 1
brace-style: 1
strict: 1
no-extra-parens: 1
eqeqeq: 1
consistent-return: 1
callback-return: 1
guard-for-in: 1
block-scoped-var: 1
semi-spacing: 1
no-redeclare: 1
no-new: 1
#############################################################################
# Angular Plugin Customization
#############################################################################
angular/ng_controller_as_vm:
- 1
- "ctrl"
# Remove after migrating to angular 1.4 or later.
angular/ng_no_cookiestore:
- 1

View File

@ -40,6 +40,27 @@ class LoadBalancers(generic.View):
result = neutronclient(request).list_loadbalancers(tenant_id=tenant_id)
return {'items': result.get('loadbalancers')}
@rest_utils.ajax()
def post(self, request):
"""Create a new load balancer.
Creates a new load balancer as well as other optional resources such as
a listener, pool, monitor, etc.
"""
data = request.DATA
spec = {
'vip_subnet_id': data['loadbalancer']['subnet']
}
if data['loadbalancer'].get('name'):
spec['name'] = data['loadbalancer']['name']
if data['loadbalancer'].get('description'):
spec['description'] = data['loadbalancer']['description']
if data['loadbalancer'].get('ip'):
spec['vip_address'] = data['loadbalancer']['ip']
loadbalancer = neutronclient(request).create_loadbalancer(
{'loadbalancer': spec}).get('loadbalancer')
return loadbalancer
@urls.register
class LoadBalancer(generic.View):

View File

@ -4,7 +4,7 @@
{% block page_header %}{% endblock %}
{% block ng_route_base %}
<base href="/"></base>
<base href="{{ WEBROOT }}">
{% endblock %}
{% block main %}

View File

@ -37,6 +37,15 @@ ADD_JS_FILES = [
'dashboard/project/lbaasv2/loadbalancers/table.controller.js',
'dashboard/project/lbaasv2/loadbalancers/detail.controller.js',
'dashboard/project/lbaasv2/loadbalancers/filters.js',
'dashboard/project/lbaasv2/loadbalancers/actions/batch-actions.service.js',
'dashboard/project/lbaasv2/loadbalancers/actions/create/modal.service.js',
'dashboard/project/lbaasv2/loadbalancers/actions/create/model.service.js',
('dashboard/project/lbaasv2/loadbalancers/actions/create/'
'wizard.controller.js'),
('dashboard/project/lbaasv2/loadbalancers/actions/create/'
'workflow.service.js'),
('dashboard/project/lbaasv2/loadbalancers/actions/create/details/'
'details.controller.js'),
]
ADD_JS_SPEC_FILES = [
@ -46,6 +55,18 @@ ADD_JS_SPEC_FILES = [
'dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js',
'dashboard/project/lbaasv2/loadbalancers/detail.controller.spec.js',
'dashboard/project/lbaasv2/loadbalancers/filters.spec.js',
('dashboard/project/lbaasv2/loadbalancers/actions/'
'batch-actions.service.spec.js'),
('dashboard/project/lbaasv2/loadbalancers/actions/create/'
'modal.service.spec.js'),
('dashboard/project/lbaasv2/loadbalancers/actions/create/'
'model.service.spec.js'),
('dashboard/project/lbaasv2/loadbalancers/actions/create/'
'wizard.controller.spec.js'),
('dashboard/project/lbaasv2/loadbalancers/actions/create/'
'workflow.service.spec.js'),
('dashboard/project/lbaasv2/loadbalancers/actions/create/details/'
'details.controller.spec.js'),
]
ADD_SCSS_FILES = [

View File

@ -29,11 +29,16 @@
* @ngdoc service
* @name horizon.app.core.openstack-service-api.loadbalancers
* @description Provides direct pass through to neutron LBaaS v2 with NO abstraction.
* @param apiService The horizon core API service.
* @param toastService The horizon toast service.
* @returns The LBaaS V2 service API.
*/
function lbaasv2API(apiService, toastService) {
var service = {
getLoadBalancers: getLoadBalancers,
getLoadBalancer: getLoadBalancer
getLoadBalancer: getLoadBalancer,
createLoadBalancer: createLoadBalancer
};
return service;
@ -50,6 +55,7 @@
* The listing result is an object with property "items". Each item is
* a load balancer.
*/
function getLoadBalancers() {
return apiService.get('/api/lbaas/loadbalancers/')
.error(function () {
@ -64,6 +70,7 @@
* @param {string} id
* Specifies the id of the load balancer to request.
*/
function getLoadBalancer(id) {
return apiService.get('/api/lbaas/loadbalancers/' + id)
.error(function () {
@ -71,5 +78,20 @@
});
}
/**
* @name horizon.app.core.openstack-service-api.lbaasv2.createLoadBalancer
* @description
* Create a new load balancer
* @param {object} spec
* Specifies the data used to create the new load balancer.
*/
function createLoadBalancer(spec) {
return apiService.post('/api/lbaas/loadbalancers/', spec)
.error(function () {
toastService.add('error', gettext('Unable to create load balancer.'));
});
}
}
}());

View File

@ -50,6 +50,16 @@
"testInput": [
'1234'
]
},
{
"func": "createLoadBalancer",
"method": "post",
"path": "/api/lbaas/loadbalancers/",
"error": "Unable to create load balancer.",
"data": { name: 'loadbalancer-1' },
"testInput": [
{ name: 'loadbalancer-1' }
]
}
];

View File

@ -20,38 +20,43 @@
* @ngdoc overview
* @name horizon.dashboard.project.lbaasv2
* @description
*
* # horizon.dashboard.project.lbaasv2
*
* The LBaaS v2 dashboard's top level module.
*/
angular
.module('horizon.dashboard.project.lbaasv2', [
'ngRoute',
'horizon.dashboard.project.lbaasv2.loadbalancers'
])
.config(config);
.config(config)
/* eslint-disable max-len */
.constant('horizon.dashboard.project.lbaasv2.patterns', {
ipv4: '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$',
ipv6: '^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$'
});
/* eslint-enable max-len */
config.$inject = [
'$provide',
'$windowProvider',
'$routeProvider',
'$locationProvider'
];
function config($windowProvider, $routeProvider, $locationProvider) {
$locationProvider.html5Mode({
enabled: true
}).hashPrefix('!');
function config($provide, $windowProvider, $routeProvider, $locationProvider) {
$locationProvider.html5Mode(true).hashPrefix('!');
var base = '/project/ngloadbalancersv2/';
var path = $windowProvider.$get().STATIC_URL + 'dashboard/project/lbaasv2/';
var href = '/project/ngloadbalancersv2/';
var basePath = $windowProvider.$get().STATIC_URL + 'dashboard/project/lbaasv2/';
$provide.constant('horizon.dashboard.project.lbaasv2.basePath', basePath);
$routeProvider
.when(base, {
templateUrl: path + 'loadbalancers/table.html'
})
.when(base + 'detail/:loadbalancerId', {
templateUrl: path + 'loadbalancers/detail.html'
.when(href, {
templateUrl: basePath + 'loadbalancers/table.html'
})
.when(href + 'detail/:loadbalancerId', {
templateUrl: basePath + 'loadbalancers/detail.html'
});
}
}());

View File

@ -22,8 +22,27 @@
});
});
describe('LBaaS v2 Module Base Path', function () {
var basePath, staticUrl;
beforeEach(module('horizon.dashboard.project.lbaasv2'));
beforeEach(inject(function ($injector) {
basePath = $injector.get('horizon.dashboard.project.lbaasv2.basePath');
staticUrl = $injector.get('$window').STATIC_URL;
}));
it('should be defined', function () {
expect(basePath).toBeDefined();
});
it('should be correct', function () {
expect(basePath).toEqual(staticUrl + 'dashboard/project/lbaasv2/');
});
});
describe('LBaaS v2 Module Config', function () {
var $routeProvider, $locationProvider, path;
var $routeProvider, $locationProvider, basePath;
beforeEach(function() {
// Create a dummy module so that we can test $routeProvider and $locationProvider calls
@ -32,33 +51,33 @@
.config(function(_$routeProvider_, _$locationProvider_, $windowProvider) {
$routeProvider = _$routeProvider_;
$locationProvider = _$locationProvider_;
path = $windowProvider.$get().STATIC_URL + 'dashboard/project/lbaasv2/';
basePath = $windowProvider.$get().STATIC_URL + 'dashboard/project/lbaasv2/';
spyOn($routeProvider, 'when').and.callThrough();
spyOn($locationProvider, 'html5Mode').and.callThrough();
});
module('ngRoute')
module('ngRoute');
module('configTest');
module('horizon.dashboard.project.lbaasv2')
module('horizon.dashboard.project.lbaasv2');
inject();
});
it('should use html5 mode', function () {
expect($locationProvider.html5Mode).toHaveBeenCalledWith({enabled: true});
expect($locationProvider.html5Mode).toHaveBeenCalledWith(true);
});
it('should route URLs', function () {
var base = '/ngloadbalancersv2/';
var href = '/project/ngloadbalancersv2/';
var routes = [
[
base,
href,
{
templateUrl: path + 'loadbalancers/table.html'
templateUrl: basePath + 'loadbalancers/table.html'
}
],
[
base + 'detail/:loadbalancerId',
href + 'detail/:loadbalancerId',
{
templateUrl: path + 'loadbalancers/detail.html'
templateUrl: basePath + 'loadbalancers/detail.html'
}
]
];

View File

@ -0,0 +1,64 @@
/*
* 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')
.factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions',
tableBatchActions);
tableBatchActions.$inject = [
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.modal',
'horizon.dashboard.project.lbaasv2.basePath',
'horizon.framework.util.i18n.gettext'
];
/**
* @ngdoc service
* @ngname horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions
*
* @description
* Provides the service for the Load Balancers table batch actions.
*
* @param createModal The create action modal service.
* @param basePath The lbaasv2 module base path.
* @param gettext The horizon gettext function for translation.
* @returns Load balancers table batch actions service object.
*/
function tableBatchActions(createModal, basePath, gettext) {
var service = {
actions: actions
};
return service;
///////////////
function actions() {
return [{
service: createModal,
template: {
url: basePath + 'loadbalancers/actions/create/action.template.html',
text: gettext('Create Load Balancer')
}
}];
}
}
})();

View File

@ -0,0 +1,45 @@
/*
* 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 Balancers Table Batch Actions Service', function() {
var batchActionsService, scope;
beforeEach(module('horizon.framework.util'));
beforeEach(module('horizon.framework.conf'));
beforeEach(module('horizon.framework.widgets.toast'));
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) {
scope = $injector.get('$rootScope').$new();
batchActionsService = $injector.get(
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions');
}));
it('should define correct table batch actions', function() {
var actions = batchActionsService.actions();
expect(actions.length).toBe(1);
expect(actions[0].template.text).toBe('Create Load Balancer');
});
});
})();

View File

@ -0,0 +1,3 @@
<action action-classes="'btn btn-default btn-sm pull-right'">
<span class="fa fa-plus">$text$</span>
</action>

View File

@ -0,0 +1,47 @@
/*
* 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('CreateLoadBalancerDetailsController', CreateLoadBalancerDetailsController);
CreateLoadBalancerDetailsController.$inject = [
'horizon.dashboard.project.lbaasv2.patterns'
];
/**
* @ngdoc controller
* @name CreateLoadBalancerDetailsController
* @description
* The `CreateLoadBalancerDetailsController` controller provides functions for
* configuring the details step of the Create Load Balancer Wizard.
* @param patterns The LBaaS v2 patterns constant.
* @returns undefined
*/
function CreateLoadBalancerDetailsController(patterns) {
var ctrl = this;
// Error text for invalid fields
ctrl.loadbalancerIPError = gettext('The IP address is not valid.');
// IP address validation pattern
ctrl.ipPattern = [patterns.ipv4, patterns.ipv6].join('|');
}
})();

View File

@ -0,0 +1,40 @@
/*
* 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('Create Load Balancer Details Step', function() {
beforeEach(module('horizon.dashboard.project.lbaasv2'));
describe('CreateLoadBalancerDetailsController', function() {
var ctrl;
beforeEach(inject(function($controller) {
ctrl = $controller('CreateLoadBalancerDetailsController');
}));
it('should define error messages for invalid fields', function() {
expect(ctrl.loadbalancerIPError).toBeDefined();
});
it('should define patterns for field validation', function() {
expect(ctrl.ipPattern).toBeDefined();
});
});
});
})();

View File

@ -0,0 +1,3 @@
<h1 translate>Load Balancer Details Help</h1>
<p translate>Provide the details for the new load balancer. A subnet is required. The subnet is the network on which to allocate the load balancer's IP address. If an IP address is provided it must be a well-formed IPv4 or IPv6 address. The system will attempt to assign the provided IP address to the load balancer.</p>

View File

@ -0,0 +1,61 @@
<div ng-controller="CreateLoadBalancerDetailsController as ctrl">
<h1 translate>Load Balancer Details</h1>
<!--content-->
<div class="content">
<div translate class="subtitle">Provide the details for the new load balancer.</div>
<div class="row form-group">
<div class="col-sm-12 col-md-6">
<div class="form-field loadbalancer-name"
ng-class="{ 'has-error': createLoadBalancerDetailsForm['loadbalancer-name'].$invalid && createLoadBalancerDetailsForm['loadbalancer-name'].$dirty }">
<label translate class="on-top" for="loadbalancer-name">Name</label>
<input name="loadbalancer-name" id="loadbalancer-name"
type="text" class="form-control input-sm"
ng-model="model.spec.loadbalancer.name">
</div>
</div>
<div class="col-sm-12 col-md-6">
<div class="form-field loadbalancer-description">
<label translate class="on-top" for="loadbalancer-description">Description</label>
<input name="loadbalancer-description" id="loadbalancer-description"
type="text" class="form-control input-sm"
ng-model="model.spec.loadbalancer.description">
</div>
</div>
</div>
<div class="row form-group">
<div class="col-sm-6 col-md-3">
<div class="form-field loadbalancer-ip"
ng-class="{ 'has-error': createLoadBalancerDetailsForm['loadbalancer-ip'].$invalid && createLoadBalancerDetailsForm['loadbalancer-ip'].$dirty }">
<label translate class="on-top" for="loadbalancer-ip">IP Address</label>
<span class="fa fa-exclamation-triangle invalid"
ng-show="createLoadBalancerDetailsForm['loadbalancer-ip'].$invalid && createLoadBalancerDetailsForm.$dirty"
popover="{$ ::ctrl.loadbalancerIPError $}"
popover-placement="top" popover-append-to-body="true"
popover-trigger="hover"></span>
<input name="loadbalancer-ip" id="loadbalancer-ip"
type="text" class="form-control input-sm"
ng-model="model.spec.loadbalancer.ip" ng-pattern="::ctrl.ipPattern">
</div>
</div>
<div class="col-sm-6 col-md-3">
<div class="form-field required loadbalancer-subnet">
<label translate class="on-top" for="loadbalancer-subnet">Subnet</label>
<select class="form-control input-sm" name="loadbalancer-subnet"
id="loadbalancer-subnet"
ng-options="subnet.name for subnet in model.subnets"
ng-model="model.spec.loadbalancer.subnet" ng-required="true">
</select>
</div>
</div>
</div>
</div>
<!-- end content -->
</div>

View File

@ -0,0 +1,85 @@
/*
* 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')
.factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.modal',
modalService);
modalService.$inject = [
'$modal',
'$location',
'$window',
'horizon.framework.widgets.toast.service',
'horizon.framework.util.i18n.gettext',
'horizon.app.core.openstack-service-api.policy'
];
/**
* @ngdoc service
* @ngname horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.modal
*
* @description
* Provides the service for opening the create load balancer modal.
*
* @param $modal The angular bootstrap $modal service.
* @param $location The angular $location service.
* @param $window The angular reference to the browser window object.
* @param toastService The horizon toast service.
* @param gettext The horizon gettext function for translation.
* @param policy The horizon policy service.
* @returns The modal service for the create load balancer workflow.
*/
function modalService($modal, $location, $window, toastService, gettext, policy) {
var service = {
allowed: allowed,
perform: perform
};
return service;
//////////////
function allowed() {
// This rule is made up and should therefore always pass. I assume at some point there
// will be a valid rule similar to this that we will want to use.
return policy.ifAllowed({ rules: [['neutron', 'create_loadbalancer']] });
}
function perform() {
var spec = {
backdrop: 'static',
controller: 'ModalContainerController',
template: '<wizard ng-controller="CreateLoadBalancerWizardController"></wizard>',
windowClass: 'modal-dialog-wizard',
// ModalContainerController requires a launchContext parameter...
resolve: {
launchContext: null
}
};
var modal = $modal.open(spec);
modal.result.then(function(response) {
toastService.add('success', gettext('A new load balancer is being created.'));
$location.path($window.WEBROOT + 'project/ngloadbalancersv2/detail/' + response.data.id);
});
}
}
})();

View File

@ -0,0 +1,107 @@
/*
* 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 Balancers Table Create Action Modal Service', function() {
var modalService, modal;
beforeEach(module('horizon.framework.util'));
beforeEach(module('horizon.framework.conf'));
beforeEach(module('horizon.framework.widgets.toast'));
beforeEach(module('horizon.app.core.openstack-service-api'));
beforeEach(module('horizon.dashboard.project.lbaasv2'));
beforeEach(module(function($provide) {
var response = {
data: {
id: '1'
}
};
modal = {
open: function() {
return {
result: {
then: function(func) {
func(response);
}
}
};
}
};
var policyAPI = {
ifAllowed: function() {
return true;
}
};
$provide.value('$modal', modal);
$provide.value('horizon.app.core.openstack-service-api.policy', policyAPI);
}));
beforeEach(inject(function ($injector) {
modalService = $injector.get(
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.modal');
}));
it('should define function for opening a modal', function() {
expect(modalService.perform).toBeDefined();
});
it('should be allowed based on policy', function() {
expect(modalService.allowed()).toBe(true);
});
describe('modalService "perform" function tests', function() {
var toastService, $location;
beforeEach(inject(function ($injector) {
toastService = $injector.get('horizon.framework.widgets.toast.service');
$location = $injector.get('$location');
}));
it('calls modal.open', function() {
spyOn(modal, 'open').and.callThrough();
modalService.perform();
expect(modal.open).toHaveBeenCalled();
});
it('calls modal.open with expected values', function() {
spyOn(modal, 'open').and.callThrough();
modalService.perform();
var args = modal.open.calls.argsFor(0)[0];
expect(args.backdrop).toBe('static');
expect(args.controller).toBeDefined();
expect(args.resolve).toBeDefined();
expect(args.resolve.launchContext).toBeNull();
});
it('redirects upon success', function() {
spyOn(toastService, 'add').and.callThrough();
spyOn($location, 'path').and.callThrough();
modalService.perform();
expect(toastService.add).toHaveBeenCalledWith('success', jasmine.any(String));
expect($location.path).toHaveBeenCalledWith('/project/ngloadbalancersv2/detail/1');
});
});
});
})();

View File

@ -0,0 +1,181 @@
/*
* 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')
.factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.model',
createLoadBalancerModel);
createLoadBalancerModel.$inject = [
'$q',
'horizon.app.core.openstack-service-api.neutron',
'horizon.app.core.openstack-service-api.lbaasv2'
];
/**
* @ngdoc service
* @name horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.model
*
* @description
* This is the M part of the MVC design pattern for the create load balancer wizard workflow. It
* is responsible for providing data to the view of each step in the workflow and collecting the
* user's input from the view for creation of the new load balancer. It is also the center point
* of communication between the UI and services API.
*
* @param $q The angular service for promises.
* @param neutronAPI The neutron service API.
* @param lbaasv2API The LBaaS V2 service API.
* @returns The model service for the create load balancer workflow.
*/
function createLoadBalancerModel($q, neutronAPI, lbaasv2API) {
var initPromise;
/**
* @ngdoc model api object
*/
var model = {
initializing: false,
initialized: false,
/**
* @name spec
*
* @description
* A dictionary like object containing specification collected from user
* input. Required properties include:
*
* @property {String} name: The new load balancer name.
* @property {String} subnet: The subnet for the load balancer.
*/
spec: null,
subnets: [],
/**
* api methods for UI controllers
*/
initialize: initialize,
createLoadBalancer: createLoadBalancer
};
/**
* @ngdoc method
* @name createLoadBalancerModel.initialize
* @returns {promise}
*
* @description
* Send request to get all data to initialize the model.
*/
function initialize() {
var promise;
model.spec = {
loadbalancer: {
name: null,
description: null,
ip: null,
subnet: null
}
};
if (model.initializing) {
promise = initPromise;
} else {
model.initializing = true;
promise = $q.all([
lbaasv2API.getLoadBalancers().then(onGetLoadBalancers),
neutronAPI.getSubnets().then(onGetSubnets)
]);
promise.then(onInitSuccess, onInitFail);
}
return promise;
}
function onInitSuccess() {
model.initializing = false;
model.initialized = true;
}
function onInitFail() {
model.initializing = false;
model.initialized = false;
}
/**
* @ngdoc method
* @name createLoadBalancerModel.createLoadBalancer
* @returns {promise}
*
* @description
* Send request for creating the load balancer.
*
* @returns Response from the LBaaS V2 API for creating a load balancer.
*/
function createLoadBalancer() {
var finalSpec = angular.copy(model.spec);
// Delete null properties
angular.forEach(finalSpec, function(group, groupName) {
angular.forEach(group, function(value, key) {
if (value === null) {
delete finalSpec[groupName][key];
}
});
});
finalSpec.loadbalancer.subnet = finalSpec.loadbalancer.subnet.id;
return lbaasv2API.createLoadBalancer(finalSpec);
}
function onGetLoadBalancers(response) {
var existingNames = {};
angular.forEach(response.data.items, function(lb) {
existingNames[lb.name] = 1;
});
var name;
var index = 0;
var prefix = 'Load Balancer ';
do {
index += 1;
name = prefix + index;
} while (name in existingNames);
model.spec.loadbalancer.name = name;
}
function onGetSubnets(response) {
model.subnets = [];
angular.forEach(response.data.items, function(subnet) {
model.subnets.push(subnet);
});
}
return model;
}
})();

View File

@ -0,0 +1,184 @@
/*
* 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 Create Load Balancer Workflow Model Service', function() {
var model, $q, scope;
beforeEach(module('horizon.dashboard.project.lbaasv2'));
beforeEach(module(function($provide) {
$provide.value('horizon.app.core.openstack-service-api.lbaasv2', {
getLoadBalancers: function() {
var loadbalancers = [ { name: 'Load Balancer 1' }, { name: 'Load Balancer 2' } ];
var deferred = $q.defer();
deferred.resolve({ data: { items: loadbalancers } });
return deferred.promise;
},
createLoadBalancer: function(spec) {
return spec;
}
});
$provide.value('horizon.app.core.openstack-service-api.neutron', {
getSubnets: function() {
var subnets = [ { id: 'subnet-1', name: 'subnet-1' },
{ id: 'subnet-2', name: 'subnet-2' } ];
var deferred = $q.defer();
deferred.resolve({ data: { items: subnets } });
return deferred.promise;
}
});
}));
beforeEach(inject(function ($injector) {
model = $injector.get(
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.model'
);
$q = $injector.get('$q');
scope = $injector.get('$rootScope').$new();
}));
describe('Initial object (pre-initialize)', function() {
it('is defined', function() {
expect(model).toBeDefined();
});
it('has initialization status parameters', function() {
expect(model.initializing).toBeDefined();
expect(model.initialized).toBeDefined();
});
it('does not yet have a spec', function() {
expect(model.spec).toBeNull();
});
it('has empty subnets array', function() {
expect(model.subnets).toEqual([]);
});
it('has an "initialize" function', function() {
expect(model.initialize).toBeDefined();
});
it('has a "createLoadBalancer" function', function() {
expect(model.createLoadBalancer).toBeDefined();
});
});
describe('Post initialize model', function() {
beforeEach(function() {
model.initialize();
scope.$apply();
});
it('should initialize model properties', function() {
expect(model.initializing).toBe(false);
expect(model.initialized).toBe(true);
expect(model.subnets.length).toBe(2);
expect(model.spec).toBeDefined();
expect(model.spec.loadbalancer).toBeDefined();
});
it('should initialize names', function() {
expect(model.spec.loadbalancer.name).toBe('Load Balancer 3');
});
});
describe('Initialization failure', function() {
beforeEach(inject(function ($injector) {
var neutronAPI = $injector.get('horizon.app.core.openstack-service-api.neutron');
neutronAPI.getSubnets = function() {
var deferred = $q.defer();
deferred.reject('Error');
return deferred.promise;
};
}));
beforeEach(function() {
model.initialize();
scope.$apply();
});
it('should fail to be initialized on subnets error', function() {
expect(model.initializing).toBe(false);
expect(model.initialized).toBe(false);
expect(model.spec.loadbalancer.name).toBe('Load Balancer 3');
expect(model.subnets).toEqual([]);
});
});
describe('Post initialize model - Initializing', function() {
beforeEach(function() {
model.initializing = true;
model.initialize();
scope.$apply();
});
// This is here to ensure that as people add/change spec properties, they don't forget
// to implement tests for them.
it('has the right number of properties', function() {
expect(Object.keys(model.spec).length).toBe(1);
expect(Object.keys(model.spec.loadbalancer).length).toBe(4);
});
it('sets load balancer name to null', function() {
expect(model.spec.loadbalancer.name).toBeNull();
});
it('sets load balancer description to null', function() {
expect(model.spec.loadbalancer.description).toBeNull();
});
it('sets load balancer ip address to null', function() {
expect(model.spec.loadbalancer.ip).toBeNull();
});
it('sets load balancer subnet to null', function() {
expect(model.spec.loadbalancer.subnet).toBeNull();
});
});
describe('Create Load Balancer', function() {
beforeEach(function() {
model.initialize();
scope.$apply();
});
it('should set final spec properties', function() {
model.spec.loadbalancer.ip = '1.2.3.4';
model.spec.loadbalancer.subnet = model.subnets[0];
var finalSpec = model.createLoadBalancer();
expect(finalSpec.loadbalancer.name).toBe('Load Balancer 3');
expect(finalSpec.loadbalancer.description).toBeUndefined();
expect(finalSpec.loadbalancer.ip).toBe('1.2.3.4');
expect(finalSpec.loadbalancer.subnet).toBe(model.subnets[0].id);
});
});
});
})();

View File

@ -0,0 +1,42 @@
/*
* 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('CreateLoadBalancerWizardController', CreateLoadBalancerWizardController);
CreateLoadBalancerWizardController.$inject = [
'$scope',
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.model',
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.workflow'
];
function CreateLoadBalancerWizardController(
$scope,
createLoadBalancerModel,
createLoadBalancerWorkflow
) {
// Note: we set these attributes on the $scope so that the scope inheritance used all
// through the wizard continues to work.
$scope.workflow = createLoadBalancerWorkflow; // eslint-disable-line angular/ng_controller_as
$scope.model = createLoadBalancerModel; // eslint-disable-line angular/ng_controller_as
$scope.model.initialize();
$scope.submit = $scope.model.createLoadBalancer; // eslint-disable-line angular/ng_controller_as
}
})();

View File

@ -0,0 +1,63 @@
/*
* 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 Create Load Balancer Wizard Controller', function() {
var ctrl;
var model = {
createLoadBalancer: function() {
return 'created';
},
initialize: angular.noop
};
var scope = {};
beforeEach(module('horizon.app.core'));
beforeEach(module('horizon.framework.util'));
beforeEach(module('horizon.framework.conf'));
beforeEach(module('horizon.framework.widgets.toast'));
beforeEach(module('horizon.dashboard.project.lbaasv2'));
beforeEach(module(function ($provide) {
$provide.value('horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.model',
model);
$provide.value('horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.workflow',
{ thing: true });
}));
beforeEach(inject(function ($controller) {
spyOn(model, 'initialize');
ctrl = $controller('CreateLoadBalancerWizardController', { $scope: scope });
}));
it('defines the controller', function() {
expect(ctrl).toBeDefined();
});
it('calls initialize on the given model', function() {
expect(model.initialize).toHaveBeenCalled();
});
it('sets scope.workflow to the given workflow', function() {
expect(scope.workflow).toEqual({ thing: true });
});
it('defines scope.submit', function() {
expect(scope.submit).toBeDefined();
expect(scope.submit()).toBe('created');
});
});
})();

View File

@ -0,0 +1,53 @@
/*
* 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')
.factory('horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.workflow',
createLoadBalancerWorkflow);
createLoadBalancerWorkflow.$inject = [
'horizon.dashboard.project.lbaasv2.basePath',
'horizon.app.core.workflow.factory'
];
function createLoadBalancerWorkflow(basePath, dashboardWorkflow) {
return dashboardWorkflow({
title: gettext('Create Load Balancer'),
steps: [
{
id: 'loadbalancer',
title: gettext('Load Balancer Details'),
templateUrl: basePath + 'loadbalancers/actions/create/details/details.html',
helpUrl: basePath + 'loadbalancers/actions/create/details/details.help.html',
formName: 'createLoadBalancerDetailsForm'
}
],
btnText: {
finish: gettext('Create Load Balancer')
},
btnIcon: {
finish: 'fa fa-cloud-download'
}
});
}
})();

View File

@ -0,0 +1,65 @@
/*
* 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 Create Load Balancer Workflow Service', function() {
var createLoadBalancerWorkflow;
beforeEach(module('horizon.app.core'));
beforeEach(module('horizon.framework.util'));
beforeEach(module('horizon.framework.conf'));
beforeEach(module('horizon.framework.widgets.toast'));
beforeEach(module('horizon.dashboard.project.lbaasv2'));
beforeEach(inject(function ($injector) {
createLoadBalancerWorkflow = $injector.get(
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.create.workflow'
);
}));
it('should be defined', function () {
expect(createLoadBalancerWorkflow).toBeDefined();
});
it('should have a title property', function () {
expect(createLoadBalancerWorkflow.title).toBeDefined();
});
it('should have steps defined', function () {
expect(createLoadBalancerWorkflow.steps).toBeDefined();
expect(createLoadBalancerWorkflow.steps.length).toBe(1);
var forms = [
'createLoadBalancerDetailsForm'
];
forms.forEach(function(expectedForm, idx) {
expect(createLoadBalancerWorkflow.steps[idx].formName).toBe(expectedForm);
});
});
it('can be extended', function () {
expect(createLoadBalancerWorkflow.append).toBeDefined();
expect(createLoadBalancerWorkflow.prepend).toBeDefined();
expect(createLoadBalancerWorkflow.after).toBeDefined();
expect(createLoadBalancerWorkflow.replace).toBeDefined();
expect(createLoadBalancerWorkflow.remove).toBeDefined();
expect(createLoadBalancerWorkflow.addController).toBeDefined();
});
});
})();

View File

@ -21,9 +21,9 @@
.controller('LoadBalancerDetailController', LoadBalancerDetailController);
LoadBalancerDetailController.$inject = [
'horizon.dashboard.project.lbaasv2.loadbalancers.basePath',
'horizon.app.core.openstack-service-api.lbaasv2',
'$routeParams'
'$routeParams',
'$window'
];
/**
@ -32,12 +32,18 @@
*
* @description
* Controller for the LBaaS v2 load balancers detail page.
*
* @param api The LBaaS v2 API service.
* @param $routeParams The angular $routeParams service.
* @param $window The angular reference to the browser window object.
* @returns undefined
*/
function LoadBalancerDetailController(basepath, api, $routeParams) {
function LoadBalancerDetailController(api, $routeParams, $window) {
var ctrl = this;
ctrl.loadbalancer = {};
ctrl.path = basepath;
ctrl.webroot = $window.webroot;
var loadbalancerId = $routeParams.loadbalancerId;

View File

@ -31,8 +31,9 @@
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.loadbalancers'));
beforeEach(module('horizon.dashboard.project.lbaasv2'));
beforeEach(inject(function($injector) {
loadbalancer = { id: '1234' };
@ -49,11 +50,6 @@
});
}
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');

View File

@ -36,22 +36,19 @@
<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>
<a target="_self" ng-href="{$ ::ctrl.webroot $}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>
<a target="_self" ng-href="{$ ::ctrl.webroot $}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 heading="{$ 'Listeners' | translate $}">
<!-- TODO(jpomero) ng-include the table of listeners for this load balancer -->
</tab>
</tabset>
</div>

View File

@ -34,7 +34,10 @@
* @name operatingStatusFilter
* @description
* Takes raw load balancer operating status from the API and returns the user friendly status.
* @param gettext The horizon gettext function for translation.
* @returns The function for filtering the load balancer operating status.
*/
function operatingStatusFilter(gettext) {
var statuses = {
'ONLINE': gettext('Online'),
@ -54,7 +57,10 @@
* @name provisioningStatusFilter
* @description
* Takes raw load balancer provisioning status from the API and returns the user friendly status.
* @param gettext The horizon gettext function for translation.
* @returns The function for filtering the load balancer provisioning status.
*/
function provisioningStatusFilter(gettext) {
var statuses = {
'ACTIVE': gettext('Active'),

View File

@ -24,18 +24,8 @@
* Provides the services and widgets required to support and display the project load
* balancers v2 panel.
*/
angular
.module('horizon.dashboard.project.lbaasv2.loadbalancers', [])
.config(config);
config.$inject = [
'$provide',
'$windowProvider'
];
function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'dashboard/project/lbaasv2/loadbalancers/';
$provide.constant('horizon.dashboard.project.lbaasv2.loadbalancers.basePath', path);
}
.module('horizon.dashboard.project.lbaasv2.loadbalancers', []);
})();

View File

@ -22,22 +22,4 @@
});
});
describe('LBaaS v2 Load Balancers Module Base Path', function () {
var basePath, staticUrl;
beforeEach(module('horizon.dashboard.project.lbaasv2.loadbalancers'));
beforeEach(inject(function ($injector) {
basePath = $injector.get('horizon.dashboard.project.lbaasv2.loadbalancers.basePath');
staticUrl = $injector.get('$window').STATIC_URL;
}));
it('should be defined', function () {
expect(basePath).toBeDefined();
});
it('should be correct', function () {
expect(basePath).toEqual(staticUrl + 'dashboard/project/lbaasv2/loadbalancers/');
});
});
})();

View File

@ -18,11 +18,13 @@
angular
.module('horizon.dashboard.project.lbaasv2.loadbalancers')
.controller('loadBalancersTableController', LoadBalancersTableController);
.controller('LoadBalancersTableController', LoadBalancersTableController);
LoadBalancersTableController.$inject = [
'horizon.dashboard.project.lbaasv2.loadbalancers.basePath',
'horizon.app.core.openstack-service-api.lbaasv2'
'$scope',
'$window',
'horizon.app.core.openstack-service-api.lbaasv2',
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions'
];
/**
@ -30,16 +32,23 @@
* @name LoadBalancersTableController
*
* @description
* Controller for the LBaaS v2 load balancers table.
* Serves as the focal point for table actions.
* Controller for the LBaaS v2 load balancers table. Serves as the focal point for table actions.
*
* @param $scope The angular $scope object.
* @param $window The angular reference to the browser window object.
* @param api The LBaaS V2 service API.
* @param batchActions The load balancer batch actions service.
* @returns undefined
*/
function LoadBalancersTableController(basepath, api) {
function LoadBalancersTableController($scope, $window, api, batchActions) {
var ctrl = this;
ctrl.items = [];
ctrl.src = [];
ctrl.checked = {};
ctrl.path = basepath;
ctrl.webroot = $window.webroot;
ctrl.batchActions = batchActions;
init();

View File

@ -17,7 +17,8 @@
'use strict';
describe('LBaaS v2 Load Balancers Table Controller', function() {
var controller, lbaasv2API, staticUrl, items = [];
var controller, lbaasv2API, staticUrl, scope, batchActionsService;
var items = [];
function fakeAPI() {
return {
@ -29,22 +30,29 @@
///////////////////////
beforeEach(module('horizon.framework.util.http'));
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.loadbalancers'));
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');
batchActionsService = $injector.get(
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions');
controller = $injector.get('$controller');
staticUrl = $injector.get('$window').STATIC_URL;
scope = $injector.get('$rootScope').$new();
scope.lbaasv2API = lbaasv2API;
spyOn(lbaasv2API, 'getLoadBalancers').and.callFake(fakeAPI);
}));
function createController() {
return controller('loadBalancersTableController', {
lbaasv2API: lbaasv2API
});
return controller('LoadBalancersTableController', { $scope: scope });
}
it('should initialize correctly', function() {
@ -52,11 +60,7 @@
expect(ctrl.items).toEqual([]);
expect(ctrl.src).toEqual(items);
expect(ctrl.checked).toEqual({});
});
it('should set path properly', function() {
var path = staticUrl + 'dashboard/project/lbaasv2/loadbalancers/';
expect(createController().path).toEqual(path);
expect(ctrl.batchActions).toBeDefined();
});
it('should invoke lbaasv2 apis', function() {

View File

@ -1,6 +1,6 @@
<hz-page-header header="{$ 'Load Balancers' | translate $}"></hz-page-header>
<table ng-controller="loadBalancersTableController as table"
<table ng-controller="LoadBalancersTableController as table"
hz-table ng-cloak
st-table="table.items"
st-safe-src="table.src"
@ -20,6 +20,7 @@
-->
<th colspan="100" class="search-header">
<hz-search-bar group-classes="input-group-sm" icon-classes="fa-search">
<actions allowed="table.batchActions.actions" type="batch"></actions>
</hz-search-bar>
</th>
</tr>
@ -70,7 +71,7 @@
duration="200">
</span>
</td>
<td class="rsp-p1"><a ng-href="/project/ngloadbalancersv2/detail/{$ item.id $}">{$ item.name $}</a></td>
<td class="rsp-p1"><a ng-href="{$ ::ctrl.webroot $}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>