diff --git a/neutron_lbaas_dashboard/api/rest/lbaasv2.py b/neutron_lbaas_dashboard/api/rest/lbaasv2.py index e61a526..f617e4a 100644 --- a/neutron_lbaas_dashboard/api/rest/lbaasv2.py +++ b/neutron_lbaas_dashboard/api/rest/lbaasv2.py @@ -230,3 +230,17 @@ class LoadBalancer(generic.View): """ lb = neutronclient(request).show_loadbalancer(loadbalancer_id) return lb.get('loadbalancer') + + @rest_utils.ajax() + def put(self, request, loadbalancer_id): + """Edit a load balancer. + + """ + data = request.DATA + spec = {} + if data['loadbalancer'].get('name'): + spec['name'] = data['loadbalancer']['name'] + if data['loadbalancer'].get('description'): + spec['description'] = data['loadbalancer']['description'] + return neutronclient(request).update_loadbalancer( + loadbalancer_id, {'loadbalancer': spec}).get('loadbalancer') diff --git a/neutron_lbaas_dashboard/enabled/_1481_project_ng_loadbalancersv2_panel.py b/neutron_lbaas_dashboard/enabled/_1481_project_ng_loadbalancersv2_panel.py index 8d59498..7e2f8d4 100644 --- a/neutron_lbaas_dashboard/enabled/_1481_project_ng_loadbalancersv2_panel.py +++ b/neutron_lbaas_dashboard/enabled/_1481_project_ng_loadbalancersv2_panel.py @@ -38,8 +38,11 @@ ADD_JS_FILES = [ '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/row-actions.service.js', ('dashboard/project/lbaasv2/loadbalancers/actions/create/' 'wizard.controller.js'), + ('dashboard/project/lbaasv2/loadbalancers/actions/edit/' + 'wizard.controller.js'), 'dashboard/project/lbaasv2/workflow/modal.service.js', 'dashboard/project/lbaasv2/workflow/model.service.js', 'dashboard/project/lbaasv2/workflow/workflow.service.js', @@ -60,8 +63,12 @@ ADD_JS_SPEC_FILES = [ 'dashboard/project/lbaasv2/loadbalancers/filters.spec.js', ('dashboard/project/lbaasv2/loadbalancers/actions/' 'batch-actions.service.spec.js'), + ('dashboard/project/lbaasv2/loadbalancers/actions/' + 'row-actions.service.spec.js'), ('dashboard/project/lbaasv2/loadbalancers/actions/create/' 'wizard.controller.spec.js'), + ('dashboard/project/lbaasv2/loadbalancers/actions/edit/' + 'wizard.controller.spec.js'), 'dashboard/project/lbaasv2/workflow/modal.service.spec.js', 'dashboard/project/lbaasv2/workflow/model.service.spec.js', 'dashboard/project/lbaasv2/workflow/workflow.service.spec.js', diff --git a/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js b/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js index 59dad1e..7b062ce 100644 --- a/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js +++ b/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.js @@ -38,7 +38,8 @@ var service = { getLoadBalancers: getLoadBalancers, getLoadBalancer: getLoadBalancer, - createLoadBalancer: createLoadBalancer + createLoadBalancer: createLoadBalancer, + editLoadBalancer: editLoadBalancer }; return service; @@ -93,5 +94,21 @@ }); } + /** + * @name horizon.app.core.openstack-service-api.lbaasv2.editLoadBalancer + * @description + * Edit a load balancer + * @param {string} id + * @param {object} spec + * Specifies the data used to update the load balancer. + */ + + function editLoadBalancer(id, spec) { + return apiService.put('/api/lbaas/loadbalancers/' + id + '/', spec) + .error(function () { + toastService.add('error', gettext('Unable to update load balancer.')); + }); + } + } }()); diff --git a/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js b/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js index b917ed2..720d289 100644 --- a/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js +++ b/neutron_lbaas_dashboard/static/app/core/openstack-service-api/lbaasv2.service.spec.js @@ -60,6 +60,16 @@ "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' } + ] } ]; diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/wizard.controller.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/wizard.controller.js new file mode 100644 index 0000000..114df56 --- /dev/null +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/wizard.controller.js @@ -0,0 +1,42 @@ +/* + * Copyright 2016 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +(function () { + 'use strict'; + + angular + .module('horizon.dashboard.project.lbaasv2.loadbalancers') + .controller('EditLoadBalancerWizardController', EditLoadBalancerWizardController); + + EditLoadBalancerWizardController.$inject = [ + '$scope', + 'horizon.dashboard.project.lbaasv2.workflow.model', + 'horizon.dashboard.project.lbaasv2.workflow.workflow', + 'horizon.framework.util.i18n.gettext' + ]; + + function EditLoadBalancerWizardController($scope, model, workflowService, gettext) { + var scope = $scope; + // Note: We set these attributes on the $scope so that the scope inheritance used all through + // the wizard continues to work. Using local var to appease eslint angular/ng_controller_as. + scope.model = model; + scope.submit = scope.model.submit; + scope.workflow = workflowService(gettext('Update Load Balancer'), + 'fa fa-pencil', + ['loadbalancer']); + scope.model.initialize('loadbalancer', scope.launchContext.id); + } + +})(); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/wizard.controller.spec.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/wizard.controller.spec.js new file mode 100644 index 0000000..231dcf4 --- /dev/null +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/edit/wizard.controller.spec.js @@ -0,0 +1,67 @@ +/* + * Copyright 2016 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +(function () { + 'use strict'; + + describe('LBaaS v2 Edit Load Balancer Wizard Controller', function() { + var ctrl, workflowSpy; + var model = { + submit: function() { + return 'updated'; + }, + initialize: angular.noop + }; + var workflow = 'foo'; + var scope = { + launchContext: { id: '1' } + }; + + beforeEach(module('horizon.framework.util')); + beforeEach(module('horizon.dashboard.project.lbaasv2')); + beforeEach(module(function ($provide) { + workflowSpy = jasmine.createSpy('workflow').and.returnValue(workflow); + $provide.value('horizon.dashboard.project.lbaasv2.workflow.model', model); + $provide.value('horizon.dashboard.project.lbaasv2.workflow.workflow', workflowSpy); + })); + beforeEach(inject(function ($controller) { + spyOn(model, 'initialize'); + ctrl = $controller('EditLoadBalancerWizardController', { $scope: scope }); + })); + + it('defines the controller', function() { + expect(ctrl).toBeDefined(); + }); + + it('calls initialize on the given model', function() { + expect(model.initialize).toHaveBeenCalledWith('loadbalancer', '1'); + }); + + it('sets scope.workflow to the given workflow', function() { + expect(scope.workflow).toBe(workflow); + }); + + it('initializes workflow with correct properties', function() { + expect(workflowSpy).toHaveBeenCalledWith('Update Load Balancer', + 'fa fa-pencil', ['loadbalancer']); + }); + + it('defines scope.submit', function() { + expect(scope.submit).toBe(model.submit); + expect(scope.submit()).toBe('updated'); + }); + }); + +})(); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.js new file mode 100644 index 0000000..4af4e8e --- /dev/null +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.js @@ -0,0 +1,89 @@ +/* + * 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.rowActions', + tableRowActions); + + tableRowActions.$inject = [ + '$q', + '$route', + 'horizon.dashboard.project.lbaasv2.workflow.modal', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.framework.util.q.extensions', + 'horizon.framework.util.i18n.gettext' + ]; + + /** + * @ngdoc service + * @ngname horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions + * + * @description + * Provides the service for the Load Balancers table row actions. + * + * @param $q The angular service for promises. + * @param $route The angular $route service. + * @param workflowModal The LBaaS workflow modal service. + * @param policy The horizon policy 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. + */ + + function tableRowActions($q, $route, workflowModal, policy, qExtensions, gettext) { + + var edit = workflowModal.init({ + controller: 'EditLoadBalancerWizardController', + message: gettext('The load balancer has been updated.'), + handle: onEdit, + allowed: canEdit + }); + + var service = { + actions: actions + }; + + return service; + + /////////////// + + function actions() { + return [{ + service: edit, + template: { + text: gettext('Edit') + } + }]; + } + + function canEdit(item) { + return $q.all([ + qExtensions.booleanAsPromise(item.provisioning_status === 'ACTIVE'), + // 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', 'update_loadbalancer']] }) + ]); + } + + function onEdit(/*response*/) { + $route.reload(); + } + } + +})(); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.spec.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.spec.js new file mode 100644 index 0000000..9e62333 --- /dev/null +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/actions/row-actions.service.spec.js @@ -0,0 +1,98 @@ +/* + * 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 Row Actions Service', function() { + var rowActionsService, scope, $route, actions, policy; + + function canEdit(item) { + spyOn(policy, 'ifAllowed').and.returnValue(true); + var promise = actions[0].service.allowed(item); + var allowed; + promise.then(function() { + allowed = true; + }, function() { + allowed = false; + }); + scope.$apply(); + expect(policy.ifAllowed).toHaveBeenCalledWith({rules: [['neutron', 'update_loadbalancer']]}); + return allowed; + } + + 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' + } + }; + var modal = { + open: function() { + return { + result: { + then: function(func) { + func(response); + } + } + }; + } + }; + $provide.value('$modal', modal); + })); + + beforeEach(inject(function ($injector) { + scope = $injector.get('$rootScope').$new(); + $route = $injector.get('$route'); + policy = $injector.get('horizon.app.core.openstack-service-api.policy'); + rowActionsService = $injector.get( + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions'); + actions = rowActionsService.actions(); + })); + + it('should define correct table row actions', function() { + expect(actions.length).toBe(1); + expect(actions[0].template.text).toBe('Edit'); + }); + + it('should allow editing an ACTIVE load balancer', function() { + expect(canEdit({provisioning_status: 'ACTIVE'})).toBe(true); + }); + + it('should not allow editing a non-ACTIVE load balancer', function() { + expect(canEdit({provisioning_status: 'PENDING_UPDATE'})).toBe(false); + }); + + it('should have the "allowed" and "perform" functions', function() { + actions.forEach(function(action) { + expect(action.service.allowed).toBeDefined(); + expect(action.service.perform).toBeDefined(); + }); + }); + + it('should reload table after edit', function() { + spyOn($route, 'reload').and.callThrough(); + actions[0].service.perform(); + expect($route.reload).toHaveBeenCalled(); + }); + + }); +})(); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.js index e495048..7cca2d6 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.js @@ -22,6 +22,7 @@ LoadBalancerDetailController.$inject = [ 'horizon.app.core.openstack-service-api.lbaasv2', + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions', '$routeParams' ]; @@ -33,14 +34,16 @@ * Controller for the LBaaS v2 load balancers detail page. * * @param api The LBaaS v2 API service. + * @param rowActions The load balancer row actions service. * @param $routeParams The angular $routeParams service. * @returns undefined */ - function LoadBalancerDetailController(api, $routeParams) { + function LoadBalancerDetailController(api, rowActions, $routeParams) { var ctrl = this; - ctrl.loadbalancer = {}; + ctrl.loadbalancer = null; + ctrl.actions = rowActions.actions; var loadbalancerId = $routeParams.loadbalancerId; diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.spec.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.spec.js index aeed16f..d789a90 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.spec.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.controller.spec.js @@ -29,12 +29,16 @@ /////////////////////// - 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')); + beforeEach(module(function($provide) { + $provide.value('$modal', {}); + })); + beforeEach(inject(function($injector) { loadbalancer = { id: '1234' }; lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.html b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.html index 866e1af..ad0898f 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.html +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/detail.html @@ -7,6 +7,8 @@

{$ ::ctrl.loadbalancer.description $}

+
IP Address diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.js index e6dd406..50cc88e 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.js @@ -22,7 +22,8 @@ LoadBalancersTableController.$inject = [ 'horizon.app.core.openstack-service-api.lbaasv2', - 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions' + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.batchActions', + 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions' ]; /** @@ -34,16 +35,18 @@ * * @param api The LBaaS V2 service API. * @param batchActions The load balancer batch actions service. + * @param rowActions The load balancer row actions service. * @returns undefined */ - function LoadBalancersTableController(api, batchActions) { + function LoadBalancersTableController(api, batchActions, rowActions) { var ctrl = this; ctrl.items = []; ctrl.src = []; ctrl.checked = {}; ctrl.batchActions = batchActions; + ctrl.rowActions = rowActions; init(); diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js index bc5e3da..4ef145c 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.controller.spec.js @@ -44,7 +44,6 @@ lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); controller = $injector.get('$controller'); scope = $injector.get('$rootScope').$new(); - scope.lbaasv2API = lbaasv2API; spyOn(lbaasv2API, 'getLoadBalancers').and.callFake(fakeAPI); })); @@ -58,6 +57,7 @@ expect(ctrl.src).toEqual(items); expect(ctrl.checked).toEqual({}); expect(ctrl.batchActions).toBeDefined(); + expect(ctrl.rowActions).toBeDefined(); }); it('should invoke lbaasv2 apis', function() { diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.html b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.html index 9b9c412..ebd6428 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.html +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/loadbalancers/table.html @@ -77,6 +77,13 @@ {$ item.provisioning_status | provisioningStatus $} {$ item.vip_address $} {$ item.listeners.length $} + + + + diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.html b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.html index cf33dd6..6329331 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.html +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/listener/listener.html @@ -35,7 +35,7 @@
@@ -52,7 +52,7 @@ + ng-required="model.context.resource === 'listener'">
diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/loadbalancer/loadbalancer.html b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/loadbalancer/loadbalancer.html index c4a358d..83ca0a6 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/loadbalancer/loadbalancer.html +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/loadbalancer/loadbalancer.html @@ -39,7 +39,8 @@ popover-trigger="hover"> + ng-model="model.spec.loadbalancer.ip" ng-pattern="::ctrl.ipPattern" + ng-disabled="model.context.resource === 'loadbalancer' && model.context.id"> @@ -49,7 +50,8 @@ diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js index def1717..1e80abb 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.js @@ -144,56 +144,27 @@ } else { model.initializing = true; - var promises = []; + switch ((id ? 'edit' : 'create') + resource) { + case 'createloadbalancer': + promise = $q.all([ + lbaasv2API.getLoadBalancers().then(onGetLoadBalancers), + neutronAPI.getSubnets().then(onGetSubnets), + neutronAPI.getPorts().then(onGetPorts), + novaAPI.getServers().then(onGetServers) + ]).then(initMemberAddresses); + model.context.submit = createLoadBalancer; + break; + case 'editloadbalancer': + promise = $q.all([ + lbaasv2API.getLoadBalancer(model.context.id).then(onGetLoadBalancer), + neutronAPI.getSubnets().then(onGetSubnets) + ]).then(initSubnet); + model.context.submit = editLoadBalancer; + break; + default: + throw Error('Invalid resource context: ' + (id ? 'edit' : 'create') + resource); + } - promises.push(lbaasv2API.getLoadBalancers().then(onGetLoadBalancers)); - promises.push(neutronAPI.getSubnets().then(onGetSubnets)); - promises.push(neutronAPI.getPorts().then(onGetPorts)); - promises.push(novaAPI.getServers().then(onGetServers)); - model.context.submit = createLoadBalancer; - -// // Create load balancer (default) -// if (resource === 'loadbalancer' && !id) { -// promises.push(lbaasv2API.getLoadBalancers().then(onGetLoadBalancers)); -// promises.push(neutronAPI.getSubnets().then(onGetSubnets)); -// promises.push(neutronAPI.getPorts().then(onGetPorts)); -// promises.push(novaAPI.getServers().then(onGetServers)); -// model.context.submit = createLoadBalancer; -// } -// // Edit load balancer -// else if (resource === 'loadbalancer' && id) { -// // Get load balancer -// model.context.submit = editLoadBalancer; -// } -// // Create listener -// else if (resource === 'listener' && !id) { -// promises.push(neutronAPI.getSubnets().then(onGetSubnets)); -// promises.push(neutronAPI.getPorts().then(onGetPorts)); -// promises.push(novaAPI.getServers().then(onGetServers)); -// model.context.submit = createListener; -// } -// // Edit listener -// else if (resource === 'listener' && id) { -// // Get listener, pool, members, monitor -// model.context.submit = editListener; -// } -// // Edit pool -// else if (resource === 'pool' && id) { -// // Get pool, members, monitor -// model.context.submit = editPool; -// } -// // Edit monitor -// else if (resource === 'monitor' && id) { -// // Get monitor -// model.context.submit = editMonitor; -// } -// // Edit members -// else if (resource === 'members' && id) { -// // Get pool members by pool id -// model.context.submit = editMembers; -// } - - promise = $q.all(promises); promise.then(onInitSuccess, onInitFail); } @@ -201,7 +172,6 @@ } function onInitSuccess() { - initMemberAddresses(); model.initializing = false; model.initialized = true; } @@ -224,7 +194,25 @@ function submit() { var finalSpec = angular.copy(model.spec); - var resource = model.context.resource; + cleanFinalSpecLoadBalancer(finalSpec); + cleanFinalSpecListener(finalSpec); + cleanFinalSpecPool(finalSpec); + cleanFinalSpecMembers(finalSpec); + cleanFinalSpecMonitor(finalSpec); + removeNulls(finalSpec); + return model.context.submit(finalSpec); + } + + function createLoadBalancer(spec) { + return lbaasv2API.createLoadBalancer(spec); + } + + function editLoadBalancer(spec) { + return lbaasv2API.editLoadBalancer(model.context.id, spec); + } + + function cleanFinalSpecLoadBalancer(finalSpec) { + var context = model.context; // Load balancer requires subnet if (!finalSpec.loadbalancer.subnet) { @@ -233,10 +221,23 @@ finalSpec.loadbalancer.subnet = finalSpec.loadbalancer.subnet.id; } + // Cannot edit the subnet + if (context.resource === 'loadbalancer' && context.id) { + delete finalSpec.subnet; + delete finalSpec.ip; + } + } + + function cleanFinalSpecListener(finalSpec) { + // Listener requires protocol and port if (!finalSpec.listener.protocol || !finalSpec.listener.port) { delete finalSpec.listener; } + } + + function cleanFinalSpecPool(finalSpec) { + var resource = model.context.resource; // Pool requires protocol and method, and also the listener if (resource !== 'pool' && !finalSpec.listener || @@ -244,16 +245,6 @@ !finalSpec.pool.method) { delete finalSpec.pool; } - - cleanFinalSpecMembers(finalSpec); - cleanFinalSpecMonitor(finalSpec); - removeNulls(finalSpec); - - return model.context.submit(finalSpec); - } - - function createLoadBalancer(spec) { - return lbaasv2API.createLoadBalancer(spec); } function cleanFinalSpecMembers(finalSpec) { @@ -304,7 +295,7 @@ function onGetLoadBalancers(response) { var existingNames = {}; - angular.forEach(response.data.items, function(lb) { + angular.forEach(response.data.items, function nameExists(lb) { existingNames[lb.name] = 1; }); var name; @@ -324,7 +315,7 @@ function onGetServers(response) { model.members.length = 0; var members = []; - angular.forEach(response.data.items, function(server) { + angular.forEach(response.data.items, function addMember(server) { members.push({ id: server.id, name: server.name, @@ -340,13 +331,13 @@ } function initMemberAddresses() { - angular.forEach(model.members, function(member) { - var memberPorts = ports.filter(function(port) { + angular.forEach(model.members, function initAddresses(member) { + var memberPorts = ports.filter(function filterPortByDevice(port) { return port.device_id === member.id; }); member.addresses = []; - angular.forEach(memberPorts, function(port) { - angular.forEach(port.fixed_ips, function(ip) { + angular.forEach(memberPorts, function addAddressesForPort(port) { + angular.forEach(port.fixed_ips, function addAddress(ip) { member.addresses.push({ ip: ip.ip_address, subnet: ip.subnet_id @@ -357,6 +348,22 @@ }); } + function onGetLoadBalancer(response) { + var loadbalancer = response.data; + var spec = model.spec.loadbalancer; + spec.name = loadbalancer.name || ''; + spec.description = loadbalancer.description || ''; + spec.ip = loadbalancer.vip_address || ''; + spec.subnet = loadbalancer.vip_subnet_id; + } + + function initSubnet() { + var subnet = model.subnets.filter(function filterSubnetsByLoadBalancer(s) { + return s.id === model.spec.loadbalancer.subnet; + })[0]; + model.spec.loadbalancer.subnet = subnet; + } + return model; } diff --git a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js index cf3129f..31a54f6 100644 --- a/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js +++ b/neutron_lbaas_dashboard/static/dashboard/project/lbaasv2/workflow/model.service.spec.js @@ -32,8 +32,19 @@ return deferred.promise; }, + getLoadBalancer: function() { + var loadbalancer = { vip_subnet_id: 'subnet-1' }; + + var deferred = $q.defer(); + deferred.resolve({ data: loadbalancer }); + + return deferred.promise; + }, createLoadBalancer: function(spec) { return spec; + }, + editLoadBalancer: function(id, spec) { + return spec; } }); @@ -190,7 +201,7 @@ })); beforeEach(function() { - model.initialize(); + model.initialize('loadbalancer'); scope.$apply(); }); @@ -202,11 +213,29 @@ }); }); + describe('Resource not provided', function() { + var initModelNoContext = function() { + model.initialize(); + }; + + var initModelNoResource = function() { + model.initialize('', 'foo'); + }; + + it('should fail to be initialized - create', function() { + expect(initModelNoContext).toThrow(Error('Invalid resource context: createundefined')); + }); + + it('should fail to be initialized - edit', function() { + expect(initModelNoResource).toThrow(Error('Invalid resource context: edit')); + }); + }); + describe('Post initialize model - Initializing', function() { beforeEach(function() { model.initializing = true; - model.initialize(); + model.initialize('loadbalancer'); scope.$apply(); }); @@ -297,10 +326,38 @@ }); }); - describe('Model submit function', function() { + describe('context (create loadbalancer)', function() { beforeEach(function() { - model.initialize(); + model.initialize('loadbalancer'); + scope.$apply(); + }); + + it('should initialize context', function() { + expect(model.context.resource).toBe('loadbalancer'); + expect(model.context.id).toBeUndefined(); + expect(model.context.submit.name).toBe('createLoadBalancer'); + }); + }); + + describe('context (edit loadbalancer)', function() { + + beforeEach(function() { + model.initialize('loadbalancer', '1'); + scope.$apply(); + }); + + it('should initialize context', function() { + expect(model.context.resource).toBe('loadbalancer'); + expect(model.context.id).toBe('1'); + expect(model.context.submit.name).toBe('editLoadBalancer'); + }); + }); + + describe('Model submit function (create loadbalancer)', function() { + + beforeEach(function() { + model.initialize('loadbalancer'); scope.$apply(); }); @@ -427,5 +484,24 @@ }); }); + describe('Model submit function (edit loadbalancer)', function() { + + beforeEach(function() { + model.initialize('loadbalancer', '1'); + scope.$apply(); + }); + + it('should set final spec properties', function() { + model.spec.loadbalancer.description = 'new description'; + + var finalSpec = model.submit(); + + expect(finalSpec.loadbalancer.name).toBe(''); + expect(finalSpec.loadbalancer.description).toBe('new description'); + expect(finalSpec.loadbalancer.ip).toBe(''); + expect(finalSpec.loadbalancer.subnet).toBe('subnet-1'); + }); + }); + }); })();