Add angular delete server groups action

This patch adds delete actions for angular server groups panel.

Change-Id: I9cd887fb315a5523c44c48d284cdea3a6673b3b2
Partial-Implements: blueprint ng-server-groups
This commit is contained in:
wei.ying 2017-11-04 20:14:07 +08:00
parent df857f00b5
commit 37647dd318
11 changed files with 341 additions and 1 deletions

View File

@ -938,6 +938,11 @@ def server_group_create(request, **kwargs):
**kwargs) **kwargs)
@profiler.trace
def server_group_delete(request, servergroup_id):
novaclient(request).server_groups.delete(servergroup_id)
@profiler.trace @profiler.trace
def service_list(request, binary=None): def service_list(request, binary=None):
return novaclient(request).services.list(binary=binary) return novaclient(request).services.list(binary=binary)

View File

@ -448,6 +448,19 @@ class ServerGroups(generic.View):
) )
@urls.register
class ServerGroup(generic.View):
url_regex = r'nova/servergroups/(?P<servergroup_id>[^/]+)/$'
@rest_utils.ajax()
def delete(self, request, servergroup_id):
"""Delete a specific server group
DELETE http://localhost/api/nova/servergroups/<servergroup_id>
"""
api.nova.server_group_delete(request, servergroup_id)
@urls.register @urls.register
class ServerMetadata(generic.View): class ServerMetadata(generic.View):
"""API for server metadata.""" """API for server metadata."""

View File

@ -54,6 +54,7 @@
getServers: getServers, getServers: getServers,
getServerGroups: getServerGroups, getServerGroups: getServerGroups,
createServerGroup: createServerGroup, createServerGroup: createServerGroup,
deleteServerGroup: deleteServerGroup,
deleteServer: deleteServer, deleteServer: deleteServer,
pauseServer: pauseServer, pauseServer: pauseServer,
unpauseServer: unpauseServer, unpauseServer: unpauseServer,
@ -355,6 +356,28 @@
}); });
} }
/**
* @name deleteServerGroup
* @description
* Delete a single server group by ID.
*
* @param {String} serverGroupId
* Server Group to delete
*
* @param {boolean} suppressError
* If passed in, this will not show the default error handling
* (horizon alert).
*
* @returns {Object} The result of the API call
*/
function deleteServerGroup(serverGroupId, suppressError) {
var promise = apiService.delete('/api/nova/servergroups/' + serverGroupId + '/');
return suppressError ? promise : promise.error(function() {
var msg = gettext('Unable to delete the server group with id %(id)s');
toastService.add('error', interpolate(msg, { id: serverGroupId }, true));
});
}
/* /*
* @name deleteServer * @name deleteServer
* @description * @description

View File

@ -312,6 +312,13 @@
"new server group" "new server group"
] ]
}, },
{
"func": "deleteServerGroup",
"method": "delete",
"path": "/api/nova/servergroups/1/",
"error": "Unable to delete the server group with id 1",
"testInput": [1]
},
{ {
"func": "getExtensions", "func": "getExtensions",
"method": "get", "method": "get",

View File

@ -31,17 +31,39 @@
registerServerGroupActions.$inject = [ registerServerGroupActions.$inject = [
'horizon.app.core.server_groups.actions.create.service', 'horizon.app.core.server_groups.actions.create.service',
'horizon.app.core.server_groups.actions.delete.service',
'horizon.app.core.server_groups.resourceType', 'horizon.app.core.server_groups.resourceType',
'horizon.framework.conf.resource-type-registry.service' 'horizon.framework.conf.resource-type-registry.service'
]; ];
function registerServerGroupActions( function registerServerGroupActions(
createService, createService,
deleteService,
serverGroupResourceTypeCode, serverGroupResourceTypeCode,
registry registry
) { ) {
var serverGroupResourceType = registry.getResourceType(serverGroupResourceTypeCode); var serverGroupResourceType = registry.getResourceType(serverGroupResourceTypeCode);
serverGroupResourceType.itemActions
.append({
id: 'deleteServerGroupAction',
service: deleteService,
template: {
type: 'delete',
text: gettext('Delete Server Group')
}
});
serverGroupResourceType.batchActions
.append({
id: 'batchDeleteServerGroupAction',
service: deleteService,
template: {
type: 'delete-selected',
text: gettext('Delete Server Groups')
}
});
serverGroupResourceType.globalActions serverGroupResourceType.globalActions
.append({ .append({
id: 'createServerGroupAction', id: 'createServerGroupAction',

View File

@ -23,6 +23,16 @@
registry = $injector.get('horizon.framework.conf.resource-type-registry.service'); registry = $injector.get('horizon.framework.conf.resource-type-registry.service');
})); }));
it('registers Delete Server Group as an item action', function() {
var actions = registry.getResourceType('OS::Nova::ServerGroup').itemActions;
expect(actionHasId(actions, 'deleteServerGroupAction')).toBe(true);
});
it('registers Delete Server Group as a batch action', function() {
var actions = registry.getResourceType('OS::Nova::ServerGroup').batchActions;
expect(actionHasId(actions, 'batchDeleteServerGroupAction')).toBe(true);
});
it('registers Create Server Group as a global action', function() { it('registers Create Server Group as a global action', function() {
var actions = registry.getResourceType('OS::Nova::ServerGroup').globalActions; var actions = registry.getResourceType('OS::Nova::ServerGroup').globalActions;
expect(actionHasId(actions, 'createServerGroupAction')).toBe(true); expect(actionHasId(actions, 'createServerGroupAction')).toBe(true);

View File

@ -0,0 +1,108 @@
/*
* 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.server_groups')
.factory('horizon.app.core.server_groups.actions.delete.service', deleteServerGroupService);
deleteServerGroupService.$inject = [
'horizon.app.core.openstack-service-api.nova',
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.server_groups.resourceType',
'horizon.framework.util.actions.action-result.service',
'horizon.framework.util.i18n.ngettext',
'horizon.framework.widgets.modal.deleteModalService'
];
/*
* @ngdoc factory
* @name horizon.app.core.server_groups.actions.delete.service
*
* @Description
* Brings up the delete server groups confirmation modal dialog.
* On submit, delete given server groups.
* On cancel, do nothing.
*/
function deleteServerGroupService(
nova,
policy,
serverGroupResourceType,
actionResultService,
ngettext,
deleteModal
) {
return {
allowed: allowed,
perform: perform
};
//////////////
function allowed() {
return policy.ifAllowed(
{rules: [['compute', 'os_compute_api:os-server-groups:delete']]});
}
function perform(items, scope) {
var servergroups = angular.isArray(items) ? items : [items];
var context = {
labels: labelize(servergroups.length),
deleteEntity: deleteServerGroup
};
return deleteModal.open(scope, servergroups, context).then(deleteResult);
}
function deleteResult(deleteModalResult) {
// To make the result of this action generically useful, reformat the return
// from the deleteModal into a standard form
var actionResult = actionResultService.getActionResult();
deleteModalResult.pass.forEach(function markDeleted(item) {
actionResult.deleted(serverGroupResourceType, item.context.id);
});
deleteModalResult.fail.forEach(function markFailed(item) {
actionResult.failed(serverGroupResourceType, item.context.id);
});
return actionResult.result;
}
function labelize(count) {
return {
title: ngettext(
'Confirm Delete Server Group',
'Confirm Delete Server Groups', count),
message: ngettext(
'You have selected "%s". Deleted Server Group is not recoverable.',
'You have selected "%s". Deleted Server Groups are not recoverable.', count),
submit: ngettext(
'Delete Server Group',
'Delete Server Groups', count),
success: ngettext(
'Deleted Server Group: %s.',
'Deleted Server Groups: %s.', count),
error: ngettext(
'Unable to delete Server Group: %s.',
'Unable to delete Server Groups: %s.', count)
};
}
function deleteServerGroup(servergroup) {
return nova.deleteServerGroup(servergroup, true);
}
}
})();

View File

@ -0,0 +1,135 @@
/*
* 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.server_groups.actions.delete.service', function() {
var $scope, deferredModal, novaAPI, service;
var deleteModalService = {
open: function () {
deferredModal.resolve({
pass: [{context: {id: 'pass'}}],
fail: [{context: {id: 'fail'}}]
});
return deferredModal.promise;
}
};
///////////////////////
beforeEach(module('horizon.app.core'));
beforeEach(module('horizon.app.core.server_groups'));
beforeEach(module('horizon.framework'));
beforeEach(module('horizon.framework.widgets.modal', function($provide) {
$provide.value('horizon.framework.widgets.modal.deleteModalService', deleteModalService);
}));
beforeEach(inject(function($injector, _$rootScope_, $q) {
$scope = _$rootScope_.$new();
deferredModal = $q.defer();
novaAPI = $injector.get('horizon.app.core.openstack-service-api.nova');
service = $injector.get('horizon.app.core.server_groups.actions.delete.service');
}));
describe('perform method', function() {
beforeEach(function() {
spyOn(deleteModalService, 'open').and.callThrough();
});
////////////
it('should open the delete modal and show correct labels, single object', testSingleLabels);
it('should open the delete modal and show correct labels, plural objects', testPluralLabels);
it('should open the delete modal with correct entities', testEntities);
it('should only delete server groups that are valid', testValids);
it('should pass in a function that deletes a server group', testNova);
it('should check the policy if the user is allowed to delete server group', testAllowed);
////////////
function testSingleLabels() {
var servergroups = {name: 'sg'};
service.perform(servergroups);
$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('server group');
});
}
function testPluralLabels() {
var servergroups = [{name: 'sg1'}, {name: 'sg2'}];
service.perform(servergroups);
$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('server groups');
});
}
function testEntities() {
var servergroups = [{name: 'sg1'}, {name: 'sg2'}, {name: 'sg3'}];
service.perform(servergroups);
$scope.$apply();
var entities = deleteModalService.open.calls.argsFor(0)[1];
expect(deleteModalService.open).toHaveBeenCalled();
expect(entities.length).toEqual(servergroups.length);
}
function testValids() {
var servergroups = [{name: 'sg1'}, {name: 'sg2'}, {name: 'sg3'}];
service.perform(servergroups);
$scope.$apply();
var entities = deleteModalService.open.calls.argsFor(0)[1];
expect(deleteModalService.open).toHaveBeenCalled();
expect(entities.length).toBe(servergroups.length);
expect(entities[0].name).toEqual('sg1');
expect(entities[1].name).toEqual('sg2');
expect(entities[2].name).toEqual('sg3');
}
function testNova() {
spyOn(novaAPI, 'deleteServerGroup').and.callFake(angular.noop);
var servergroups = [{id: 1, name: 'sg1'}, {id: 2, name: 'sg2'}];
service.perform(servergroups);
$scope.$apply();
var contextArg = deleteModalService.open.calls.argsFor(0)[2];
var deleteFunction = contextArg.deleteEntity;
deleteFunction(servergroups[0].id);
expect(novaAPI.deleteServerGroup).toHaveBeenCalledWith(servergroups[0].id, true);
}
function testAllowed() {
var allowed = service.allowed();
expect(allowed).toBeTruthy();
}
}); // end of delete modal
}); // end of delete server group
})();

View File

@ -388,6 +388,13 @@ class NovaRestTestCase(test.TestCase):
self.mock_server_group_create.assert_called_once_with( self.mock_server_group_create.assert_called_once_with(
request, **server_group_data) request, **server_group_data)
@test.create_mocks({api.nova: ['server_group_delete']})
def test_server_group_delete(self):
request = self.mock_rest_request()
self.mock_server_group_delete.return_value = None
nova.ServerGroup().delete(request, "1")
self.mock_server_group_delete.assert_called_once_with(request, "1")
# #
# Server Metadata # Server Metadata
# #

View File

@ -657,3 +657,13 @@ class ComputeApiTests(test.APIMockTestCase):
self.assertEqual(servergroup.policies, ret_val.policies) self.assertEqual(servergroup.policies, ret_val.policies)
novaclient.versions.get_current.assert_called_once_with() novaclient.versions.get_current.assert_called_once_with()
novaclient.server_groups.create.assert_called_once_with(**kwargs) novaclient.server_groups.create.assert_called_once_with(**kwargs)
def test_server_group_delete(self):
servergroup_id = self.server_groups.first().id
novaclient = self.stub_novaclient()
novaclient.server_groups.delete.return_value = None
api_val = api.nova.server_group_delete(self.request, servergroup_id)
self.assertIsNone(api_val)
novaclient.server_groups.delete.assert_called_once_with(servergroup_id)

View File

@ -6,4 +6,4 @@ features:
Project->Compute panel group. The panel turns on if Nova API Project->Compute panel group. The panel turns on if Nova API
extension 'ServerGroups' is available. It displays information about extension 'ServerGroups' is available. It displays information about
server groups. server groups.
Supported actions: create. Supported actions: create, delete.