Merge "Add delete action to network_qos panel"
This commit is contained in:
commit
187e0051e4
|
@ -309,7 +309,11 @@ class QoSPolicies(generic.View):
|
|||
@urls.register
|
||||
class QoSPolicy(generic.View):
|
||||
"""API for a single QoS Policy."""
|
||||
url_regex = r'neutron/qos_policy/(?P<policy_id>[^/]+)/$'
|
||||
url_regex = r'neutron/qos_policies/(?P<policy_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def delete(self, request, policy_id):
|
||||
api.neutron.policy_delete(request, policy_id)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, policy_id):
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* 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
|
||||
*
|
||||
* @description
|
||||
* Provides all of the actions for network_qos.
|
||||
*/
|
||||
|
||||
angular.module('horizon.app.core.network_qos.actions', [
|
||||
'horizon.framework.conf',
|
||||
'horizon.app.core.network_qos'
|
||||
])
|
||||
.run(registerQosActions);
|
||||
|
||||
registerQosActions.$inject = [
|
||||
'horizon.framework.conf.resource-type-registry.service',
|
||||
'horizon.app.core.network_qos.actions.delete.service',
|
||||
'horizon.app.core.network_qos.resourceType'
|
||||
];
|
||||
|
||||
function registerQosActions(
|
||||
registry,
|
||||
deleteService,
|
||||
qosResourceTypeCode
|
||||
) {
|
||||
var qosResourceType = registry.getResourceType(qosResourceTypeCode);
|
||||
|
||||
qosResourceType.itemActions
|
||||
.append({
|
||||
id: 'deletePolicyAction',
|
||||
service: deleteService,
|
||||
template: {
|
||||
text: gettext('Delete Policy'),
|
||||
type: 'delete'
|
||||
}
|
||||
});
|
||||
|
||||
qosResourceType.batchActions
|
||||
.append({
|
||||
id: 'batchDeletePolicyAction',
|
||||
service: deleteService,
|
||||
template: {
|
||||
text: gettext('Delete Policies'),
|
||||
type: 'delete-selected'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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('network_qos actions module', function() {
|
||||
var registry;
|
||||
beforeEach(module('horizon.app.core.network_qos.actions'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
registry = $injector.get('horizon.framework.conf.resource-type-registry.service');
|
||||
}));
|
||||
|
||||
it('registers Delete Policy as an item action', function() {
|
||||
var actions = registry.getResourceType('OS::Neutron::QoSPolicy').itemActions;
|
||||
expect(actionHasId(actions, 'deletePolicyAction')).toBe(true);
|
||||
});
|
||||
|
||||
it('registers Delete Policies as a batch action', function() {
|
||||
var actions = registry.getResourceType('OS::Neutron::QoSPolicy').batchActions;
|
||||
expect(actionHasId(actions, 'batchDeletePolicyAction')).toBe(true);
|
||||
});
|
||||
|
||||
function actionHasId(list, value) {
|
||||
return list.filter(matchesId).length === 1;
|
||||
|
||||
function matchesId(action) {
|
||||
return action.id === value;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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.app.core.network_qos')
|
||||
.factory('horizon.app.core.network_qos.actions.delete.service', deleteService);
|
||||
|
||||
deleteService.$inject = [
|
||||
'$q',
|
||||
'horizon.app.core.openstack-service-api.neutron',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.app.core.network_qos.resourceType',
|
||||
'horizon.framework.util.actions.action-result.service',
|
||||
'horizon.framework.widgets.modal.deleteModalService'
|
||||
];
|
||||
|
||||
function deleteService(
|
||||
$q,
|
||||
neutron,
|
||||
policy,
|
||||
resourceType,
|
||||
actionResultService,
|
||||
deleteModal
|
||||
) {
|
||||
var service = {
|
||||
allowed: allowed,
|
||||
perform: perform
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
////////////
|
||||
|
||||
function allowed() {
|
||||
return policy.ifAllowed(
|
||||
{rules: [
|
||||
['network', 'delete_qos_policy']
|
||||
]}
|
||||
);
|
||||
}
|
||||
|
||||
function perform(items, scope) {
|
||||
var policies = angular.isArray(items) ? items : [items];
|
||||
|
||||
return openModal().then(onComplete);
|
||||
|
||||
function openModal() {
|
||||
return deleteModal.open(
|
||||
scope,
|
||||
policies,
|
||||
{
|
||||
labels: labelize(policies.length),
|
||||
deleteEntity: neutron.deletePolicy
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function onComplete(result) {
|
||||
var actionResult = actionResultService.getActionResult();
|
||||
|
||||
result.pass.forEach(function markDeleted(item) {
|
||||
actionResult.deleted(resourceType, item.context.id);
|
||||
});
|
||||
result.fail.forEach(function markFailed(item) {
|
||||
actionResult.failed(resourceType, item.context.id);
|
||||
});
|
||||
|
||||
return actionResult.result;
|
||||
}
|
||||
}
|
||||
|
||||
function labelize(count) {
|
||||
return {
|
||||
title: ngettext(
|
||||
'Confirm Delete Policy',
|
||||
'Confirm Delete Policies',
|
||||
count),
|
||||
|
||||
message: ngettext(
|
||||
'You have selected "%s". Deleted policy is not recoverable.',
|
||||
'You have selected "%s". Deleted policies are not recoverable.',
|
||||
count),
|
||||
|
||||
submit: ngettext(
|
||||
'Delete Policy',
|
||||
'Delete Policies',
|
||||
count),
|
||||
|
||||
success: ngettext(
|
||||
'Deleted policy: %s.',
|
||||
'Deleted policies: %s.',
|
||||
count),
|
||||
|
||||
error: ngettext(
|
||||
'Unable to delete policy: %s.',
|
||||
'Unable to delete policies: %s.',
|
||||
count)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* 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.delete.service', function() {
|
||||
|
||||
var deleteModalService = {
|
||||
open: function () {
|
||||
deferredModal.resolve({
|
||||
pass: [{context: {id: 'a'}}],
|
||||
fail: [{context: {id: 'b'}}]
|
||||
});
|
||||
return deferredModal.promise;
|
||||
}
|
||||
};
|
||||
|
||||
var neutronAPI = {
|
||||
deletePolicy: function() {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
var policyAPI = {
|
||||
ifAllowed: function() {
|
||||
return {
|
||||
success: function(callback) {
|
||||
callback({allowed: true});
|
||||
},
|
||||
failure: function(callback) {
|
||||
callback({allowed: false});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var deferred, service, $scope, deferredModal;
|
||||
|
||||
////////////
|
||||
|
||||
beforeEach(module('horizon.app.core'));
|
||||
beforeEach(module('horizon.app.core.network_qos'));
|
||||
beforeEach(module('horizon.framework'));
|
||||
|
||||
beforeEach(module('horizon.framework.widgets.modal', function($provide) {
|
||||
$provide.value('horizon.framework.widgets.modal.deleteModalService', deleteModalService);
|
||||
}));
|
||||
|
||||
beforeEach(module('horizon.app.core.openstack-service-api', function($provide) {
|
||||
$provide.value('horizon.app.core.openstack-service-api.neutron', neutronAPI);
|
||||
$provide.value('horizon.app.core.openstack-service-api.policy', policyAPI);
|
||||
spyOn(policyAPI, 'ifAllowed').and.callThrough();
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($injector, _$rootScope_, $q) {
|
||||
$scope = _$rootScope_.$new();
|
||||
service = $injector.get('horizon.app.core.network_qos.actions.delete.service');
|
||||
deferred = $q.defer();
|
||||
deferredModal = $q.defer();
|
||||
}));
|
||||
|
||||
function generatePolicy(policyCount) {
|
||||
|
||||
var policies = [];
|
||||
var data = {
|
||||
owner: 'project',
|
||||
name: '',
|
||||
id: ''
|
||||
};
|
||||
|
||||
for (var index = 0; index < policyCount; index++) {
|
||||
var policy = angular.copy(data);
|
||||
policy.id = String(index);
|
||||
policy.name = 'policy' + index;
|
||||
policies.push(policy);
|
||||
}
|
||||
return policies;
|
||||
}
|
||||
|
||||
describe('perform method', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
spyOn(deleteModalService, 'open').and.callThrough();
|
||||
});
|
||||
|
||||
////////////
|
||||
|
||||
it('should open the delete modal and show correct labels, one object',
|
||||
function testSingleObject() {
|
||||
var policies = generatePolicy(1);
|
||||
service.perform(policies[0]);
|
||||
$scope.$apply();
|
||||
|
||||
var labels = deleteModalService.open.calls.argsFor(0)[2].labels;
|
||||
expect(deleteModalService.open).toHaveBeenCalled();
|
||||
angular.forEach(labels, function eachLabel(label) {
|
||||
expect(label.toLowerCase()).toContain('policy');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('should open the delete modal and show correct singular labels',
|
||||
function testSingleLabels() {
|
||||
var policies = generatePolicy(1);
|
||||
service.perform(policies);
|
||||
$scope.$apply();
|
||||
|
||||
var labels = deleteModalService.open.calls.argsFor(0)[2].labels;
|
||||
expect(deleteModalService.open).toHaveBeenCalled();
|
||||
angular.forEach(labels, function eachLabel(label) {
|
||||
expect(label.toLowerCase()).toContain('policy');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('should open the delete modal and show correct plural labels',
|
||||
function testpluralLabels() {
|
||||
var policies = generatePolicy(2);
|
||||
service.perform(policies);
|
||||
$scope.$apply();
|
||||
|
||||
var labels = deleteModalService.open.calls.argsFor(0)[2].labels;
|
||||
expect(deleteModalService.open).toHaveBeenCalled();
|
||||
angular.forEach(labels, function eachLabel(label) {
|
||||
expect(label.toLowerCase()).toContain('policies');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('should open the delete modal with correct entities',
|
||||
function testEntities() {
|
||||
var count = 3;
|
||||
var policies = generatePolicy(count);
|
||||
service.perform(policies);
|
||||
$scope.$apply();
|
||||
|
||||
var entities = deleteModalService.open.calls.argsFor(0)[1];
|
||||
expect(deleteModalService.open).toHaveBeenCalled();
|
||||
expect(entities.length).toEqual(count);
|
||||
}
|
||||
);
|
||||
|
||||
it('should only delete policies that are valid',
|
||||
function testValids() {
|
||||
var count = 2;
|
||||
var policies = generatePolicy(count);
|
||||
service.perform(policies);
|
||||
$scope.$apply();
|
||||
|
||||
var entities = deleteModalService.open.calls.argsFor(0)[1];
|
||||
expect(deleteModalService.open).toHaveBeenCalled();
|
||||
expect(entities.length).toBe(count);
|
||||
expect(entities[0].name).toEqual('policy0');
|
||||
expect(entities[1].name).toEqual('policy1');
|
||||
}
|
||||
);
|
||||
|
||||
it('should pass in a function that deletes a policy',
|
||||
function testNeutron() {
|
||||
spyOn(neutronAPI, 'deletePolicy');
|
||||
var count = 1;
|
||||
var policies = generatePolicy(count);
|
||||
var policy = policies[0];
|
||||
service.perform(policies);
|
||||
$scope.$apply();
|
||||
|
||||
var contextArg = deleteModalService.open.calls.argsFor(0)[2];
|
||||
var deleteFunction = contextArg.deleteEntity;
|
||||
deleteFunction(policy.id);
|
||||
expect(neutronAPI.deletePolicy).toHaveBeenCalledWith(policy.id);
|
||||
}
|
||||
);
|
||||
|
||||
}); // end of delete modal
|
||||
|
||||
describe('allow method', function() {
|
||||
|
||||
var resolver = {
|
||||
success: function() {},
|
||||
error: function() {}
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
spyOn(resolver, 'success');
|
||||
spyOn(resolver, 'error');
|
||||
});
|
||||
|
||||
////////////
|
||||
|
||||
it('should use default policy if batch action',
|
||||
function testBatch() {
|
||||
service.allowed();
|
||||
$scope.$apply();
|
||||
expect(policyAPI.ifAllowed).toHaveBeenCalled();
|
||||
expect(resolver.success).not.toHaveBeenCalled();
|
||||
expect(resolver.error).not.toHaveBeenCalled();
|
||||
}
|
||||
);
|
||||
|
||||
it('allows delete if policy can be deleted',
|
||||
function testValid() {
|
||||
service.allowed().success(resolver.success);
|
||||
$scope.$apply();
|
||||
expect(resolver.success).toHaveBeenCalled();
|
||||
}
|
||||
);
|
||||
|
||||
it('disallows delete if policy is not owned by user',
|
||||
function testOwner() {
|
||||
deferred.reject();
|
||||
service.allowed().failure(resolver.error);
|
||||
$scope.$apply();
|
||||
expect(resolver.error).toHaveBeenCalled();
|
||||
}
|
||||
);
|
||||
|
||||
}); // end of allow method
|
||||
|
||||
}); // end of delete.service
|
||||
|
||||
})();
|
|
@ -26,7 +26,10 @@
|
|||
angular
|
||||
.module('horizon.app.core.network_qos', [
|
||||
'ngRoute',
|
||||
'horizon.app.core.network_qos.details'
|
||||
'horizon.framework.conf',
|
||||
'horizon.app.core.network_qos.actions',
|
||||
'horizon.app.core.network_qos.details',
|
||||
'horizon.app.core'
|
||||
])
|
||||
.constant('horizon.app.core.network_qos.resourceType', 'OS::Neutron::QoSPolicy')
|
||||
.run(run)
|
||||
|
@ -34,12 +37,14 @@
|
|||
|
||||
run.$inject = [
|
||||
'horizon.framework.conf.resource-type-registry.service',
|
||||
'horizon.framework.util.i18n.gettext',
|
||||
'horizon.app.core.network_qos.basePath',
|
||||
'horizon.app.core.network_qos.service',
|
||||
'horizon.app.core.network_qos.resourceType'
|
||||
];
|
||||
|
||||
function run(registry,
|
||||
gettext,
|
||||
basePath,
|
||||
qosService,
|
||||
qosResourceType) {
|
||||
|
@ -130,7 +135,7 @@
|
|||
.when('/project/network_qos', {
|
||||
templateUrl: path + 'panel.html'
|
||||
})
|
||||
.when('/project/network_qos/:policy_id', {
|
||||
.when('/project/network_qos/:id', {
|
||||
redirectTo: goToAngularDetails
|
||||
});
|
||||
|
||||
|
|
|
@ -21,4 +21,29 @@
|
|||
});
|
||||
});
|
||||
|
||||
describe('loading the qos module', function () {
|
||||
var registry;
|
||||
|
||||
beforeEach(module('horizon.app.core.network_qos'));
|
||||
beforeEach(inject(function($injector) {
|
||||
registry = $injector.get('horizon.framework.conf.resource-type-registry.service');
|
||||
}));
|
||||
|
||||
it('registers names', function() {
|
||||
expect(registry.getResourceType('OS::Neutron::QoSPolicy').getName()).toBe("QoS Policies");
|
||||
});
|
||||
|
||||
it('should set facets for search', function () {
|
||||
var names = registry.getResourceType('OS::Neutron::QoSPolicy').filterFacets
|
||||
.map(getName);
|
||||
expect(names).toContain('name');
|
||||
expect(names).toContain('description');
|
||||
expect(names).toContain('shared');
|
||||
|
||||
function getName(x) {
|
||||
return x.name;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
Loading…
Reference in New Issue