Add create action for quotas

This patch adds create action for quotas panel
in Admin dashboard.

Change-Id: I71e042ddcd9fd68ed03bf189b61d8b43aac16933
Partial-Implements: blueprint add-quotas-panel
This commit is contained in:
Shu Muto 2018-04-03 16:55:59 +09:00
parent dd70cba6dd
commit cb7518790f
10 changed files with 446 additions and 10 deletions

View File

@ -264,8 +264,8 @@ class Quotas(generic.View):
"""
created = magnum.quotas_create(request, **request.DATA)
return rest_utils.CreatedResponse(
('/api/container_infra/quotas/%s/%s' % created.id,
created.resource),
('/api/container_infra/quotas/%s/%s' % (
created.project_id, created.resource)),
created.to_dict())

View File

@ -205,8 +205,8 @@
});
}
function createQuota(projectId, resource, params) {
return apiService.post('/api/container_infra/quotas/' + projectId + '/' + resource, params)
function createQuota(params) {
return apiService.post('/api/container_infra/quotas/', params)
.error(function() {
toastService.add('error', gettext('Unable to create quota.'));
});

View File

@ -176,20 +176,18 @@
{
"func": "createQuota",
"method": "post",
"path": "/api/container_infra/quotas/123/Cluster",
"path": "/api/container_infra/quotas/",
"data": {
"project_id": "123",
"resource": "Cluster",
"hard_limit": "1"
"hard_limit": 1
},
"error": "Unable to create quota.",
"testInput": [
"123",
"Cluster",
{
"project_id": "123",
"resource": "Cluster",
"hard_limit": "1"
"hard_limit": 1
}
]
},

View File

@ -0,0 +1,57 @@
/**
* 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.dashboard.container-infra.quotas.actions
*
* @description
* Provides all of the actions for quotas.
*/
angular.module('horizon.dashboard.container-infra.quotas.actions',
[
'horizon.framework',
'horizon.dashboard.container-infra'
])
.run(registerQuotaActions);
registerQuotaActions.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.framework.util.i18n.gettext',
'horizon.dashboard.container-infra.quotas.create.service',
'horizon.dashboard.container-infra.quotas.resourceType'
];
function registerQuotaActions (
registry,
gettext,
createQuotaService,
resourceType) {
var quotaResourceType = registry.getResourceType(resourceType);
quotaResourceType.globalActions
.append({
id: 'createQuotaAction',
service: createQuotaService,
template: {
type: 'create',
text: gettext('Create Quota')
}
});
}
})();

View File

@ -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('register quota actions module', function() {
var registry;
beforeEach(module('horizon.dashboard.container-infra.quotas.actions'));
beforeEach(inject(function($injector) {
registry = $injector.get('horizon.framework.conf.resource-type-registry.service');
}));
it('registers Create Quota as a batch action', function() {
var actions = registry.getResourceType('OS::Magnum::Quota').globalActions;
expect(actionHasId(actions, 'createQuotaAction')).toBe(true);
});
function actionHasId(list, value) {
return list.filter(matchesId).length === 1;
function matchesId(action) {
if (action.id === value) {
return true;
}
}
}
});
})();

View File

@ -0,0 +1,95 @@
/**
* 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
* @name horizon.dashboard.container-infra.quotas.create.service
* @description Service for the container-infra quota create modal
*/
angular
.module('horizon.dashboard.container-infra.quotas')
.factory('horizon.dashboard.container-infra.quotas.create.service', createService);
createService.$inject = [
'$location',
'horizon.app.core.openstack-service-api.magnum',
'horizon.app.core.openstack-service-api.policy',
'horizon.framework.util.actions.action-result.service',
'horizon.framework.util.i18n.gettext',
'horizon.framework.util.q.extensions',
'horizon.framework.widgets.form.ModalFormService',
'horizon.framework.widgets.toast.service',
'horizon.dashboard.container-infra.quotas.resourceType',
'horizon.dashboard.container-infra.quotas.workflow'
];
function createService(
$location, magnum, policy, actionResult, gettext, $qExtensions, modal, toast,
resourceType, workflow
) {
var config;
var message = {
success: gettext('Quota %s was successfully created.')
};
var service = {
perform: perform,
allowed: allowed
};
return service;
//////////////
function perform(selected, $scope) {
config = workflow.init('create', gettext('Create'), $scope);
if (typeof selected !== 'undefined') {
config.model.id = selected.id;
}
return modal.open(config).then(submit);
}
function allowed() {
return $qExtensions.booleanAsPromise(true);
}
function submit(context) {
context.model = cleanNullProperties(context.model);
return magnum.createQuota(context.model).then(success, true);
}
function cleanNullProperties(model) {
// Initially clean fields that don't have any value.
// Not only "null", blank too.
for (var key in model) {
if (model.hasOwnProperty(key) && model[key] === null || model[key] === "") {
delete model[key];
}
}
return model;
}
function success(response) {
response.data.id = response.data.uuid;
toast.add('success', interpolate(message.success, [response.data.id]));
var result = actionResult.getActionResult()
.created(resourceType, response.data.id);
return result.result;
}
}
})();

View File

@ -0,0 +1,72 @@
/**
* 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.dashboard.container-infra.quotas.create.service', function() {
var service, $scope, $q, deferred, magnum, workflow;
var model = {
id: 1
};
var modal = {
open: function(config) {
config.model = model;
deferred = $q.defer();
deferred.resolve(config);
return deferred.promise;
}
};
///////////////////
beforeEach(module('horizon.app.core'));
beforeEach(module('horizon.framework'));
beforeEach(module('horizon.dashboard.container-infra.quotas'));
beforeEach(module(function($provide) {
$provide.value('horizon.framework.widgets.form.ModalFormService', modal);
}));
beforeEach(inject(function($injector, _$rootScope_, _$q_) {
$q = _$q_;
$scope = _$rootScope_.$new();
service = $injector.get('horizon.dashboard.container-infra.quotas.create.service');
magnum = $injector.get('horizon.app.core.openstack-service-api.magnum');
workflow = $injector.get('horizon.dashboard.container-infra.quotas.workflow');
deferred = $q.defer();
deferred.resolve({data: {id: 1}});
spyOn(magnum, 'createQuota').and.returnValue(deferred.promise);
spyOn(modal, 'open').and.callThrough();
spyOn(workflow, 'init').and.returnValue({model: model});
}));
it('should check the policy if the user is allowed to create quota', function() {
var allowed = service.allowed();
expect(allowed).toBeTruthy();
});
it('open the modal', inject(function($timeout) {
service.perform(model, $scope);
expect(modal.open).toHaveBeenCalled();
$timeout.flush();
$scope.$apply();
expect(magnum.createQuota).toHaveBeenCalled();
}));
});
})();

View File

@ -25,7 +25,8 @@
angular
.module('horizon.dashboard.container-infra.quotas',
[
'ngRoute'
'ngRoute',
'horizon.dashboard.container-infra.quotas.actions'
])
.constant('horizon.dashboard.container-infra.quotas.events', events())
.constant(

View File

@ -0,0 +1,127 @@
/**
* 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.container-infra.quotas')
.factory(
'horizon.dashboard.container-infra.quotas.workflow',
QuotaWorkflow);
QuotaWorkflow.$inject = [
'horizon.dashboard.container-infra.basePath',
'horizon.app.core.workflow.factory',
'horizon.framework.util.i18n.gettext',
'horizon.app.core.openstack-service-api.keystone'
];
function QuotaWorkflow(basePath, workflowService, gettext, keystone) {
var workflow = {
init: init
};
function init(action, title, $scope) {
var schema, form, model;
var projects = [{value:"", name: gettext("Choose a Project")}];
var resources = [{value:"Cluster", name: gettext("Cluster")}];
// schema
schema = {
type: 'object',
properties: {
'project_id': {
title: gettext('Project'),
type: 'string'
},
'resource': {
title: gettext('Resource'),
type: 'string'
},
'hard_limit': {
title: gettext('Hard Limit'),
type: 'number',
minimum: 1
}
}
};
// form
form = [
{
type: 'section',
htmlClass: 'row',
items: [
{
type: 'section',
htmlClass: 'col-xs-12',
items: [
{
key: 'project_id',
type: 'select',
titleMap: projects,
required: true,
readonly: action === 'update'
},
{
key: 'resource',
type: 'select',
titleMap: resources,
required: true,
readonly: action === 'update'
},
{
key: 'hard_limit',
placeholder: gettext('Limit for this resource.'),
required: true
}
]
}
]
}
];
keystone.getProjects().then(onGetProjects);
function onGetProjects(response) {
angular.forEach(response.data.items, function(item) {
projects.push({value: item.id, name: item.name});
});
form[0].items[0].items[0].titleMap = projects;
}
model = {
id: "",
project_id: "",
resource: "Cluster",
hard_limit: null
};
var config = {
title: title,
schema: schema,
form: form,
model: model
};
$scope.model = model;
return config;
}
return workflow;
}
})();

View File

@ -0,0 +1,45 @@
/**
* 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.dashboard.container-infra.quotas.workflow', function() {
var workflow, $scope, $q, keystone, deferred;
beforeEach(module('horizon.app.core'));
beforeEach(module('horizon.framework'));
beforeEach(module('horizon.dashboard.container-infra.quotas'));
beforeEach(inject(function($injector, _$rootScope_, _$q_) {
$q = _$q_;
$scope = _$rootScope_.$new();
workflow = $injector.get('horizon.dashboard.container-infra.quotas.workflow');
keystone = $injector.get('horizon.app.core.openstack-service-api.keystone');
deferred = $q.defer();
deferred.resolve({data:{items:{1:{name:1},2:{name:2}}}});
spyOn(keystone, 'getProjects').and.returnValue(deferred.promise);
}));
it('should be init', inject(function($timeout) {
var config = workflow.init('create', 'Create Quota', $scope);
$timeout.flush();
expect(config.title).toBeDefined();
expect(config.schema).toBeDefined();
expect(config.form).toBeDefined();
expect(config.model).toBeDefined();
}));
});
})();