diff --git a/openstack_dashboard/api/rest/neutron.py b/openstack_dashboard/api/rest/neutron.py index 4da558ef90..ae1e7944d1 100644 --- a/openstack_dashboard/api/rest/neutron.py +++ b/openstack_dashboard/api/rest/neutron.py @@ -301,6 +301,20 @@ class QoSPolicies(generic.View): tenant_id=request.user.project_id) return {'items': [p.to_dict() for p in result]} + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a Network QoS policy. + + Create a qos policy using parameters supplied in the POST + application/json object. The "name" (string) parameter is required. + This method returns the new qos policy object on success. + """ + qospolicy = api.neutron.policy_create(request, **request.DATA) + return rest_utils.CreatedResponse( + '/api/neutron/qos_policies/%s' % qospolicy.id, + qospolicy.to_dict() + ) + @urls.register class QoSPolicy(generic.View): diff --git a/openstack_dashboard/enabled/_1510_project_network_qos_panel.py b/openstack_dashboard/enabled/_1510_project_network_qos_panel.py index 3a37588d6c..f9d9122c06 100644 --- a/openstack_dashboard/enabled/_1510_project_network_qos_panel.py +++ b/openstack_dashboard/enabled/_1510_project_network_qos_panel.py @@ -8,6 +8,3 @@ PANEL_GROUP = 'network' # Python panel class of the PANEL to be added. ADD_PANEL = ('openstack_dashboard.dashboards.project.network_qos' '.panel.NetworkQoS') - -# Will default to disabled until the feature is completed. -DISABLED = True diff --git a/openstack_dashboard/static/app/core/network_qos/actions/actions.module.js b/openstack_dashboard/static/app/core/network_qos/actions/actions.module.js index f9126c0b8a..31239d943b 100644 --- a/openstack_dashboard/static/app/core/network_qos/actions/actions.module.js +++ b/openstack_dashboard/static/app/core/network_qos/actions/actions.module.js @@ -31,17 +31,30 @@ registerQosActions.$inject = [ 'horizon.framework.conf.resource-type-registry.service', + 'horizon.app.core.network_qos.actions.create.service', 'horizon.app.core.network_qos.actions.delete.service', 'horizon.app.core.network_qos.resourceType' ]; function registerQosActions( registry, + createService, deleteService, qosResourceTypeCode ) { var qosResourceType = registry.getResourceType(qosResourceTypeCode); + qosResourceType.globalActions + .append({ + id: 'createPolicyAction', + service: createService, + template: { + text: gettext('Create Policy'), + type: 'create' + } + } + ); + qosResourceType.itemActions .append({ id: 'deletePolicyAction', diff --git a/openstack_dashboard/static/app/core/network_qos/actions/actions.module.spec.js b/openstack_dashboard/static/app/core/network_qos/actions/actions.module.spec.js index 59ebd78f16..34cb8c5183 100644 --- a/openstack_dashboard/static/app/core/network_qos/actions/actions.module.spec.js +++ b/openstack_dashboard/static/app/core/network_qos/actions/actions.module.spec.js @@ -23,6 +23,11 @@ registry = $injector.get('horizon.framework.conf.resource-type-registry.service'); })); + it('registers Create Policy as a global action', function() { + var actions = registry.getResourceType('OS::Neutron::QoSPolicy').globalActions; + expect(actionHasId(actions, 'createPolicyAction')).toBe(true); + }); + it('registers Delete Policy as an item action', function() { var actions = registry.getResourceType('OS::Neutron::QoSPolicy').itemActions; expect(actionHasId(actions, 'deletePolicyAction')).toBe(true); diff --git a/openstack_dashboard/static/app/core/network_qos/actions/create.action.service.js b/openstack_dashboard/static/app/core/network_qos/actions/create.action.service.js new file mode 100644 index 0000000000..a44b6051b5 --- /dev/null +++ b/openstack_dashboard/static/app/core/network_qos/actions/create.action.service.js @@ -0,0 +1,89 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc overview + * @ngname horizon.app.core.network_qos.actions.create.service + * + * @description + * Provides all of the actions for creating network qos policy. + */ + + angular + .module('horizon.app.core.network_qos') + .factory('horizon.app.core.network_qos.actions.create.service', createService); + + createService.$inject = [ + 'horizon.app.core.openstack-service-api.neutron', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.app.core.network_qos.actions.workflow.service', + 'horizon.app.core.network_qos.resourceType', + 'horizon.framework.widgets.form.ModalFormService', + 'horizon.framework.widgets.toast.service', + 'horizon.framework.util.actions.action-result.service' + ]; + + function createService( + neutronAPI, + policy, + workflow, + resourceType, + modalFormService, + toast, + actionResultService + ) { + + var service = { + allowed: allowed, + perform: perform, + submit: submit + }; + + return service; + + ////// + function allowed() { + return policy.ifAllowed( + {rules: [ + ['network', 'create_qos_policy'] + ]} + ); + } + + function perform() { + var createPolicy = workflow.init(); + createPolicy.title = gettext('Create QoS Policy'); + return modalFormService.open(createPolicy).then(submit); + } + + function submit(context) { + var data = {name: context.model.name, description: context.model.description, + shared: context.model.shared}; + return neutronAPI.createNetworkQoSPolicy(data).then(onCreateNetworkQoSPolicy); + } + + function onCreateNetworkQoSPolicy(response) { + var qospolicy = response.data; + toast.add('success', interpolate( + gettext('QoS Policy %s was successfully created.'), [qospolicy.name])); + + return actionResultService.getActionResult() + .created(resourceType, qospolicy.id) + .result; + } + } +})(); diff --git a/openstack_dashboard/static/app/core/network_qos/actions/create.action.service.spec.js b/openstack_dashboard/static/app/core/network_qos/actions/create.action.service.spec.js new file mode 100644 index 0000000000..c62b530531 --- /dev/null +++ b/openstack_dashboard/static/app/core/network_qos/actions/create.action.service.spec.js @@ -0,0 +1,80 @@ +/* + * 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('horizon.app.core.network_qos.actions.create.service', function() { + + var $q, $scope, neutronAPI, service, modalFormService, policyAPI, toast, resType; + + /////////////////////// + + beforeEach(module('horizon.framework')); + beforeEach(module('horizon.app.core')); + beforeEach(module('horizon.app.core.network_qos')); + + beforeEach(inject(function($injector, _$rootScope_, _$q_) { + $scope = _$rootScope_.$new(); + $q = _$q_; + service = $injector.get('horizon.app.core.network_qos.actions.create.service'); + toast = $injector.get('horizon.framework.widgets.toast.service'); + modalFormService = $injector.get('horizon.framework.widgets.form.ModalFormService'); + neutronAPI = $injector.get('horizon.app.core.openstack-service-api.neutron'); + policyAPI = $injector.get('horizon.app.core.openstack-service-api.policy'); + resType = $injector.get('horizon.app.core.network_qos.resourceType'); + })); + + it('should check if the user is allowed to create netwrok qos policy', function() { + spyOn(policyAPI, 'ifAllowed').and.callThrough(); + var allowed = service.allowed(); + expect(allowed).toBeTruthy(); + expect(policyAPI.ifAllowed).toHaveBeenCalledWith( + { rules: [['network', 'create_qos_policy']] }); + }); + + it('should open the modal', function() { + spyOn(modalFormService, 'open').and.returnValue($q.defer().promise); + spyOn(neutronAPI, 'createNetworkQoSPolicy').and.returnValue($q.defer().promise); + + service.perform(); + $scope.$apply(); + + expect(modalFormService.open).toHaveBeenCalled(); + }); + + it('should submit create neutron qos request to neutron', function() { + var deferred = $q.defer(); + spyOn(neutronAPI, 'createNetworkQoSPolicy').and.returnValue(deferred.promise); + spyOn(toast, 'add').and.callFake(angular.noop); + var handler = jasmine.createSpyObj('handler', ['success']); + + deferred.resolve({data: {name: 'qos1', id: '1'}}); + service.submit({model: {name: 'qos', description: undefined, shared: 'yes'}}) + .then(handler.success); + + $scope.$apply(); + + expect(neutronAPI.createNetworkQoSPolicy).toHaveBeenCalledWith( + {name: 'qos', description: undefined, shared: 'yes'}); + expect(toast.add).toHaveBeenCalledWith( + 'success', 'QoS Policy qos1 was successfully created.'); + + expect(handler.success).toHaveBeenCalled(); + var result = handler.success.calls.first().args[0]; + expect(result.created).toEqual([{type: resType, id: '1'}]); + }); + + }); +})(); diff --git a/openstack_dashboard/static/app/core/network_qos/actions/workflow/workflow.service.js b/openstack_dashboard/static/app/core/network_qos/actions/workflow/workflow.service.js new file mode 100644 index 0000000000..a65881f66f --- /dev/null +++ b/openstack_dashboard/static/app/core/network_qos/actions/workflow/workflow.service.js @@ -0,0 +1,84 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function() { + 'use strict'; + + /** + * @ngdoc factory + * @name horizon.app.core.network_qos.actions.workflow.service + * @ngController + * + * @description + * Workflow for creating network qos policy + */ + + angular + .module('horizon.app.core.network_qos.actions') + .factory('horizon.app.core.network_qos.actions.workflow.service', NetworkQosWorkflow); + + function NetworkQosWorkflow() { + + var workflow = { + init: init + }; + + function init() { + var schema = { + type: 'object', + properties: { + name: { + title: gettext('Name'), + type: 'string' + }, + description: { + title: gettext('Description'), + type: 'string', + maxLength: 255 + }, + shared: { + title: gettext('Shared'), + type: 'boolean', + default: false + } + }, + required: ['name'] + }; + + var form = [ + "name", + { + key: "description", + type: "textarea" + }, + { + key: "shared", + type: "checkbox" + } + ]; + + var model = {}; + + var config = { + schema: schema, + form: form, + model: model + }; + + return config; + } + + return workflow; + } +})(); diff --git a/openstack_dashboard/static/app/core/network_qos/actions/workflow/workflow.service.spec.js b/openstack_dashboard/static/app/core/network_qos/actions/workflow/workflow.service.spec.js new file mode 100644 index 0000000000..a8e7db2ac1 --- /dev/null +++ b/openstack_dashboard/static/app/core/network_qos/actions/workflow/workflow.service.spec.js @@ -0,0 +1,51 @@ +/* + * 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('horizon.app.core.network_qos.actions.workflow.service', function() { + + var $q, $scope, workflow, service; + + beforeEach(module('horizon.framework')); + beforeEach(module('horizon.app.core')); + beforeEach(module('horizon.app.core.network_qos')); + + beforeEach(inject(function($injector, _$rootScope_, _$q_) { + $scope = _$rootScope_.$new(); + $q = _$q_; + workflow = $injector.get('horizon.app.core.network_qos.actions.workflow.service'); + service = $injector.get('horizon.app.core.network_qos.actions.create.service'); + })); + + function testInitWorkflow() { + var deferred = $q.defer(); + spyOn(service, 'perform').and.returnValue(deferred.promise); + deferred.resolve({'a1': 'n1'}); + + var config = workflow.init(); + $scope.$apply(); + + expect(config.schema).toBeDefined(); + expect(config.form).toBeDefined(); + expect(config.model).toBeDefined(); + return config; + } + + it('should create workflow config for creation', function() { + testInitWorkflow(); + }); + }); +})(); diff --git a/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.js b/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.js index 3073d83793..504eaece12 100644 --- a/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.js +++ b/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.js @@ -38,6 +38,7 @@ createNetwork: createNetwork, createSubnet: createSubnet, createTrunk: createTrunk, + createNetworkQoSPolicy: createNetworkQoSPolicy, deletePolicy: deletePolicy, deleteTrunk: deleteTrunk, getAgents: getAgents, @@ -391,6 +392,42 @@ }); } + /** + * @name createNetworkQoSPolicy + * @description + * Create a new network qos policy. + * @returns {Object} The new network qos policy object on success. + * + * @param {Object} newQosPolicy + * The network qos policy to create. Required. + * + * Example new qos policy object + * { + * "name": "myNewNetworkQoSPolicy", + * "description": "new network qos policy", + * "shared": true, + * } + * + * Description of properties on the qos policy object + * + * @property {string} newQosPolicy.name + * The name of the new network qos policy. Required. + * + * @property {string} newQosPolicy.description + * The description of the qos policy. Optional. + * + * @property {boolean} newQosPolicy.shared + * Indicates whether this network qos policy is shared across all other projects. + * By default, it is unchecked (false). Optional. + * + */ + function createNetworkQoSPolicy(newQosPolicy) { + return apiService.post('/api/neutron/qos_policies/', newQosPolicy) + .error(function () { + toastService.add('error', gettext('Unable to create the QoS Policy.')); + }); + } + /** * @name deletePolicy * @description diff --git a/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.spec.js b/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.spec.js index 7996860813..918db157a3 100644 --- a/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.spec.js +++ b/openstack_dashboard/static/app/core/openstack-service-api/neutron.service.spec.js @@ -311,6 +311,16 @@ ], "error": "Unable to retrieve the qos policies." }, + { + "func": "createNetworkQoSPolicy", + "method": "post", + "path": "/api/neutron/qos_policies/", + "data": "new network qos policy", + "error": "Unable to create the QoS Policy.", + "testInput": [ + "new network qos policy" + ] + }, { "func": "deletePolicy", "method": "delete", diff --git a/releasenotes/notes/network_qos-ee068d073e86de76.yaml b/releasenotes/notes/network_qos-ee068d073e86de76.yaml new file mode 100644 index 0000000000..b7364f0bdf --- /dev/null +++ b/releasenotes/notes/network_qos-ee068d073e86de76.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add "Create Network QoS Policy" button to QoS Policy Panel. + From Horizon users can now create network qos policy.