Add angular create server group action
This patch adds create server group action for angular server groups panel. Change-Id: Ia4354448dcb42bc5e53e2415084f0569a75a68a3 Partial-Implements: blueprint ng-server-groups
This commit is contained in:
parent
ce283b4a31
commit
df857f00b5
@ -931,6 +931,13 @@ def server_group_list(request):
|
||||
return novaclient(request).server_groups.list()
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def server_group_create(request, **kwargs):
|
||||
microversion = get_microversion(request, "servergroup_soft_policies")
|
||||
return novaclient(request, version=microversion).server_groups.create(
|
||||
**kwargs)
|
||||
|
||||
|
||||
@profiler.trace
|
||||
def service_list(request, binary=None):
|
||||
return novaclient(request).services.list(binary=binary)
|
||||
|
@ -431,6 +431,22 @@ class ServerGroups(generic.View):
|
||||
result = api.nova.server_group_list(request)
|
||||
return {'items': [u.to_dict() for u in result]}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create a server group.
|
||||
|
||||
Create a server group using parameters supplied in the POST
|
||||
application/json object. The "name" (string) parameter is required
|
||||
and the "policies" (array) parameter is required.
|
||||
|
||||
This method returns the new server group object on success.
|
||||
"""
|
||||
new_servergroup = api.nova.server_group_create(request, **request.DATA)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/nova/servergroups/%s' % utils_http.urlquote(
|
||||
new_servergroup.id), new_servergroup.to_dict()
|
||||
)
|
||||
|
||||
|
||||
@urls.register
|
||||
class ServerMetadata(generic.View):
|
||||
|
@ -53,6 +53,7 @@
|
||||
getServer: getServer,
|
||||
getServers: getServers,
|
||||
getServerGroups: getServerGroups,
|
||||
createServerGroup: createServerGroup,
|
||||
deleteServer: deleteServer,
|
||||
pauseServer: pauseServer,
|
||||
unpauseServer: unpauseServer,
|
||||
@ -332,6 +333,28 @@
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name createServerGroup
|
||||
* @description
|
||||
* Create a new server group. This returns the new server group object on success.
|
||||
*
|
||||
* @param {Object} newServerGroup
|
||||
* The server group to create.
|
||||
*
|
||||
* @param {string} newServerGroup.name
|
||||
* The name of the new server group. Required.
|
||||
*
|
||||
* @param {array} newServerGroup.policies
|
||||
* The policies of the new server group. Required.
|
||||
* @returns {Object} The result of the API call
|
||||
*/
|
||||
function createServerGroup(newServerGroup) {
|
||||
return apiService.post('/api/nova/servergroups/', newServerGroup)
|
||||
.error(function () {
|
||||
toastService.add('error', gettext('Unable to create the server group.'));
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* @name deleteServer
|
||||
* @description
|
||||
|
@ -302,6 +302,16 @@
|
||||
"path": '/api/nova/servergroups/',
|
||||
"error": 'Unable to retrieve server groups.'
|
||||
},
|
||||
{
|
||||
"func": "createServerGroup",
|
||||
"method": "post",
|
||||
"path": "/api/nova/servergroups/",
|
||||
"data": "new server group",
|
||||
"error": "Unable to create the server group.",
|
||||
"testInput": [
|
||||
"new server group"
|
||||
]
|
||||
},
|
||||
{
|
||||
"func": "getExtensions",
|
||||
"method": "get",
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.server_groups.actions
|
||||
*
|
||||
* @description
|
||||
* Provides all of the actions for server groups.
|
||||
*/
|
||||
angular
|
||||
.module('horizon.app.core.server_groups.actions', [
|
||||
'horizon.framework.conf',
|
||||
'horizon.app.core.server_groups'
|
||||
])
|
||||
.run(registerServerGroupActions);
|
||||
|
||||
registerServerGroupActions.$inject = [
|
||||
'horizon.app.core.server_groups.actions.create.service',
|
||||
'horizon.app.core.server_groups.resourceType',
|
||||
'horizon.framework.conf.resource-type-registry.service'
|
||||
];
|
||||
|
||||
function registerServerGroupActions(
|
||||
createService,
|
||||
serverGroupResourceTypeCode,
|
||||
registry
|
||||
) {
|
||||
var serverGroupResourceType = registry.getResourceType(serverGroupResourceTypeCode);
|
||||
|
||||
serverGroupResourceType.globalActions
|
||||
.append({
|
||||
id: 'createServerGroupAction',
|
||||
service: createService,
|
||||
template: {
|
||||
type: 'create',
|
||||
text: gettext('Create Server Group')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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('server groups actions module', function() {
|
||||
var registry;
|
||||
beforeEach(module('horizon.app.core.server_groups.actions'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
registry = $injector.get('horizon.framework.conf.resource-type-registry.service');
|
||||
}));
|
||||
|
||||
it('registers Create Server Group as a global action', function() {
|
||||
var actions = registry.getResourceType('OS::Nova::ServerGroup').globalActions;
|
||||
expect(actionHasId(actions, 'createServerGroupAction')).toBe(true);
|
||||
});
|
||||
|
||||
function actionHasId(list, value) {
|
||||
return list.filter(matchesId).length === 1;
|
||||
|
||||
function matchesId(action) {
|
||||
return action.id === value;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.create.service', createService);
|
||||
|
||||
createService.$inject = [
|
||||
'horizon.app.core.openstack-service-api.nova',
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
'horizon.app.core.server_groups.actions.workflow.service',
|
||||
'horizon.app.core.server_groups.resourceType',
|
||||
'horizon.framework.widgets.form.ModalFormService',
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.framework.util.actions.action-result.service',
|
||||
'horizon.framework.util.i18n.gettext'
|
||||
];
|
||||
|
||||
/**
|
||||
* @ngDoc factory
|
||||
* @name horizon.app.core.server_groups.actions.create.service
|
||||
* @Description A service to handle the Create Server Group modal.
|
||||
*/
|
||||
function createService(
|
||||
novaAPI,
|
||||
policy,
|
||||
workflow,
|
||||
resourceType,
|
||||
modalFormService,
|
||||
toast,
|
||||
actionResultService,
|
||||
gettext
|
||||
) {
|
||||
|
||||
var service = {
|
||||
allowed: allowed,
|
||||
perform: perform,
|
||||
submit: submit
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
//////////////
|
||||
|
||||
function allowed() {
|
||||
return policy.ifAllowed(
|
||||
{rules: [['compute', 'os_compute_api:os-server-groups:create']]});
|
||||
}
|
||||
|
||||
function perform() {
|
||||
var config = workflow.init();
|
||||
config.title = gettext("Create Server Group");
|
||||
return modalFormService.open(config).then(submit);
|
||||
}
|
||||
|
||||
function submit(context) {
|
||||
var data = {name: context.model.name};
|
||||
// Nova limits only one policy associated with a server group.
|
||||
data.policies = [context.model.policy];
|
||||
return novaAPI.createServerGroup(data).then(onSuccess);
|
||||
}
|
||||
|
||||
function onSuccess(response) {
|
||||
var servergroup = response.data;
|
||||
toast.add('success', interpolate(
|
||||
gettext('Server Group %s was successfully created.'), [servergroup.name]));
|
||||
|
||||
return actionResultService.getActionResult()
|
||||
.created(resourceType, servergroup.id)
|
||||
.result;
|
||||
}
|
||||
|
||||
}
|
||||
})();
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.create.service', function() {
|
||||
|
||||
var $q, $scope, novaAPI, service, modalFormService, policyAPI, resType, toast;
|
||||
|
||||
///////////////////////
|
||||
|
||||
beforeEach(module('horizon.framework'));
|
||||
beforeEach(module('horizon.app.core'));
|
||||
beforeEach(module('horizon.app.core.server_groups'));
|
||||
|
||||
beforeEach(inject(function($injector, _$rootScope_, _$q_) {
|
||||
$scope = _$rootScope_.$new();
|
||||
$q = _$q_;
|
||||
service = $injector.get('horizon.app.core.server_groups.actions.create.service');
|
||||
toast = $injector.get('horizon.framework.widgets.toast.service');
|
||||
modalFormService = $injector.get('horizon.framework.widgets.form.ModalFormService');
|
||||
novaAPI = $injector.get('horizon.app.core.openstack-service-api.nova');
|
||||
policyAPI = $injector.get('horizon.app.core.openstack-service-api.policy');
|
||||
resType = $injector.get('horizon.app.core.server_groups.resourceType');
|
||||
}));
|
||||
|
||||
it('should check the policy if the user is allowed to create server group', function() {
|
||||
spyOn(policyAPI, 'ifAllowed').and.callThrough();
|
||||
var allowed = service.allowed();
|
||||
expect(allowed).toBeTruthy();
|
||||
expect(policyAPI.ifAllowed).toHaveBeenCalledWith(
|
||||
{ rules: [['compute', 'os_compute_api:os-server-groups:create']] });
|
||||
});
|
||||
|
||||
it('should open the modal', function() {
|
||||
spyOn(modalFormService, 'open').and.returnValue($q.defer().promise);
|
||||
spyOn(novaAPI, 'isFeatureSupported').and.returnValue($q.defer().promise);
|
||||
|
||||
service.perform();
|
||||
$scope.$apply();
|
||||
|
||||
expect(modalFormService.open).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should submit create server group request to nova', function() {
|
||||
var deferred = $q.defer();
|
||||
spyOn(novaAPI, 'createServerGroup').and.returnValue(deferred.promise);
|
||||
spyOn(toast, 'add').and.callFake(angular.noop);
|
||||
var handler = jasmine.createSpyObj('handler', ['success']);
|
||||
|
||||
deferred.resolve({data: {name: 'name1', id: '1'}});
|
||||
service.submit({model: {name: 'karma', policy: 'affinity'}}).then(handler.success);
|
||||
|
||||
$scope.$apply();
|
||||
|
||||
expect(novaAPI.createServerGroup).toHaveBeenCalledWith(
|
||||
{name: 'karma', policies: ['affinity']});
|
||||
expect(toast.add).toHaveBeenCalledWith(
|
||||
'success', 'Server Group name1 was successfully created.');
|
||||
|
||||
expect(handler.success).toHaveBeenCalled();
|
||||
var result = handler.success.calls.first().args[0];
|
||||
expect(result.created).toEqual([{type: resType, id: '1'}]);
|
||||
});
|
||||
|
||||
});
|
||||
})();
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.server_groups.actions.workflow.service
|
||||
* @ngController
|
||||
*
|
||||
* @description
|
||||
* Workflow for creating server group
|
||||
*/
|
||||
angular
|
||||
.module('horizon.app.core.server_groups.actions')
|
||||
.factory('horizon.app.core.server_groups.actions.workflow.service', ServerGroupWorkflow);
|
||||
|
||||
ServerGroupWorkflow.$inject = [
|
||||
'horizon.app.core.server_groups.service'
|
||||
];
|
||||
|
||||
function ServerGroupWorkflow(serverGroupsService) {
|
||||
|
||||
var workflow = {
|
||||
init: init
|
||||
};
|
||||
|
||||
function init() {
|
||||
var schema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
title: gettext('Name'),
|
||||
type: 'string'
|
||||
},
|
||||
policy: {
|
||||
title: gettext('Policy'),
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
required: ['name', 'policy']
|
||||
};
|
||||
|
||||
var form = [
|
||||
"name",
|
||||
{
|
||||
key: "policy",
|
||||
type: "select",
|
||||
titleMap: []
|
||||
}
|
||||
];
|
||||
|
||||
var model = {};
|
||||
|
||||
var config = {
|
||||
schema: schema,
|
||||
form: form,
|
||||
model: model
|
||||
};
|
||||
|
||||
serverGroupsService.getServerGroupPolicies().then(modifyPolicies);
|
||||
function modifyPolicies(policies) {
|
||||
var policyField = config.form[1];
|
||||
angular.forEach(policies, function(k, v) {
|
||||
this.push({name: k, value: v});
|
||||
}, policyField.titleMap);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
return workflow;
|
||||
}
|
||||
})();
|
@ -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.server_groups.actions.workflow.service', function() {
|
||||
|
||||
var $q, $scope, workflow, service;
|
||||
|
||||
beforeEach(module('horizon.framework'));
|
||||
beforeEach(module('horizon.app.core'));
|
||||
beforeEach(module('horizon.app.core.server_groups'));
|
||||
|
||||
beforeEach(inject(function($injector, _$rootScope_, _$q_) {
|
||||
$scope = _$rootScope_.$new();
|
||||
$q = _$q_;
|
||||
workflow = $injector.get('horizon.app.core.server_groups.actions.workflow.service');
|
||||
service = $injector.get('horizon.app.core.server_groups.service');
|
||||
}));
|
||||
|
||||
function testInitWorkflow() {
|
||||
var deferred = $q.defer();
|
||||
spyOn(service, 'getServerGroupPolicies').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 be create workflow config for creation', function() {
|
||||
testInitWorkflow();
|
||||
});
|
||||
});
|
||||
})();
|
@ -26,7 +26,8 @@
|
||||
angular
|
||||
.module('horizon.app.core.server_groups', [
|
||||
'horizon.framework.conf',
|
||||
'horizon.app.core'
|
||||
'horizon.app.core',
|
||||
'horizon.app.core.server_groups.actions'
|
||||
])
|
||||
.constant('horizon.app.core.server_groups.resourceType', 'OS::Nova::ServerGroup')
|
||||
.run(run)
|
||||
|
@ -34,7 +34,8 @@
|
||||
*/
|
||||
function serverGroupsService(nova) {
|
||||
return {
|
||||
getServerGroupsPromise: getServerGroupsPromise
|
||||
getServerGroupsPromise: getServerGroupsPromise,
|
||||
getServerGroupPolicies: getServerGroupPolicies
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -11,6 +11,8 @@
|
||||
# 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.
|
||||
|
||||
import json
|
||||
from json import loads as to_json
|
||||
|
||||
from django.conf import settings
|
||||
@ -363,6 +365,29 @@ class NovaRestTestCase(test.TestCase):
|
||||
response.json)
|
||||
nc.server_group_list.assert_called_once_with(request)
|
||||
|
||||
@test.create_mocks({api.nova: ['server_group_create']})
|
||||
def test_server_group_create(self):
|
||||
req_data = json.dumps({
|
||||
'name': 'server_group', 'policies': ['affinity']})
|
||||
|
||||
self.mock_server_group_create.return_value = mock.Mock(**{
|
||||
'id': '123',
|
||||
'to_dict.return_value': {'id': '123',
|
||||
'name': 'server_group',
|
||||
'policies': ['affinity']}
|
||||
})
|
||||
|
||||
server_group_data = {'name': 'server_group',
|
||||
'policies': ['affinity']}
|
||||
request = self.mock_rest_request(body=req_data)
|
||||
response = nova.ServerGroups().post(request)
|
||||
|
||||
self.assertStatusCode(response, 201)
|
||||
self.assertEqual('/api/nova/servergroups/123', response['location'])
|
||||
|
||||
self.mock_server_group_create.assert_called_once_with(
|
||||
request, **server_group_data)
|
||||
|
||||
#
|
||||
# Server Metadata
|
||||
#
|
||||
|
@ -643,3 +643,17 @@ class ComputeApiTests(test.APIMockTestCase):
|
||||
self.assertIsInstance(ret_val, list)
|
||||
self.assertEqual(len(ret_val), len(server_groups))
|
||||
novaclient.server_groups.list.assert_called_once_with()
|
||||
|
||||
def test_server_group_create(self):
|
||||
servergroup = self.server_groups.first()
|
||||
kwargs = {'name': servergroup.name, 'policies': servergroup.policies}
|
||||
novaclient = self.stub_novaclient()
|
||||
self._mock_current_version(novaclient, '2.45')
|
||||
novaclient.server_groups.create.return_value = servergroup
|
||||
|
||||
ret_val = api.nova.server_group_create(self.request, **kwargs)
|
||||
|
||||
self.assertEqual(servergroup.name, ret_val.name)
|
||||
self.assertEqual(servergroup.policies, ret_val.policies)
|
||||
novaclient.versions.get_current.assert_called_once_with()
|
||||
novaclient.server_groups.create.assert_called_once_with(**kwargs)
|
||||
|
@ -6,3 +6,4 @@ features:
|
||||
Project->Compute panel group. The panel turns on if Nova API
|
||||
extension 'ServerGroups' is available. It displays information about
|
||||
server groups.
|
||||
Supported actions: create.
|
||||
|
Loading…
Reference in New Issue
Block a user