Merge "Add update action for cluster"
This commit is contained in:
commit
1a0364d21e
@ -149,17 +149,15 @@ def cluster_template_show(request, id):
|
|||||||
|
|
||||||
|
|
||||||
def cluster_create(request, **kwargs):
|
def cluster_create(request, **kwargs):
|
||||||
args = {}
|
args = _cleanup_params(CLUSTER_CREATE_ATTRS, True, **kwargs)
|
||||||
for (key, value) in kwargs.items():
|
|
||||||
if key in CLUSTER_CREATE_ATTRS:
|
|
||||||
args[key] = value
|
|
||||||
else:
|
|
||||||
raise exceptions.BadRequest(
|
|
||||||
"Key must be in %s" % ",".join(CLUSTER_CREATE_ATTRS))
|
|
||||||
return magnumclient(request).clusters.create(**args)
|
return magnumclient(request).clusters.create(**args)
|
||||||
|
|
||||||
|
|
||||||
def cluster_update(request, id, patch):
|
def cluster_update(request, id, **kwargs):
|
||||||
|
new = _cleanup_params(CLUSTER_CREATE_ATTRS, True, **kwargs)
|
||||||
|
old = magnumclient(request).clusters.get(id).to_dict()
|
||||||
|
old = _cleanup_params(CLUSTER_CREATE_ATTRS, False, **old)
|
||||||
|
patch = _create_patches(old, new)
|
||||||
return magnumclient(request).clusters.update(id, patch)
|
return magnumclient(request).clusters.update(id, patch)
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,6 +101,19 @@ class Cluster(generic.View):
|
|||||||
"""Get a specific cluster"""
|
"""Get a specific cluster"""
|
||||||
return change_to_id(magnum.cluster_show(request, cluster_id).to_dict())
|
return change_to_id(magnum.cluster_show(request, cluster_id).to_dict())
|
||||||
|
|
||||||
|
@rest_utils.ajax(data_required=True)
|
||||||
|
def patch(self, request, cluster_id):
|
||||||
|
"""Update a Cluster.
|
||||||
|
|
||||||
|
Returns the Cluster object on success.
|
||||||
|
"""
|
||||||
|
params = request.DATA
|
||||||
|
updated_cluster = magnum.cluster_update(
|
||||||
|
request, cluster_id, **params)
|
||||||
|
return rest_utils.CreatedResponse(
|
||||||
|
'/api/container_infra/cluster/%s' % cluster_id,
|
||||||
|
updated_cluster.to_dict())
|
||||||
|
|
||||||
|
|
||||||
@urls.register
|
@urls.register
|
||||||
class Clusters(generic.View):
|
class Clusters(generic.View):
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
'horizon.framework.util.i18n.gettext',
|
'horizon.framework.util.i18n.gettext',
|
||||||
'horizon.dashboard.container-infra.clusters.create.service',
|
'horizon.dashboard.container-infra.clusters.create.service',
|
||||||
'horizon.dashboard.container-infra.clusters.delete.service',
|
'horizon.dashboard.container-infra.clusters.delete.service',
|
||||||
|
'horizon.dashboard.container-infra.clusters.update.service',
|
||||||
'horizon.dashboard.container-infra.clusters.show-certificate.service',
|
'horizon.dashboard.container-infra.clusters.show-certificate.service',
|
||||||
'horizon.dashboard.container-infra.clusters.sign-certificate.service',
|
'horizon.dashboard.container-infra.clusters.sign-certificate.service',
|
||||||
'horizon.dashboard.container-infra.clusters.resourceType'
|
'horizon.dashboard.container-infra.clusters.resourceType'
|
||||||
@ -44,6 +45,7 @@
|
|||||||
gettext,
|
gettext,
|
||||||
createClusterService,
|
createClusterService,
|
||||||
deleteClusterService,
|
deleteClusterService,
|
||||||
|
updateClusterService,
|
||||||
showCertificateService,
|
showCertificateService,
|
||||||
signCertificateService,
|
signCertificateService,
|
||||||
resourceType) {
|
resourceType) {
|
||||||
@ -84,6 +86,13 @@
|
|||||||
text: gettext('Sign Certificate')
|
text: gettext('Sign Certificate')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.append({
|
||||||
|
id: 'updateClusterAction',
|
||||||
|
service: updateClusterService,
|
||||||
|
template: {
|
||||||
|
text: gettext('Update Cluster')
|
||||||
|
}
|
||||||
|
})
|
||||||
.append({
|
.append({
|
||||||
id: 'deleteClusterAction',
|
id: 'deleteClusterAction',
|
||||||
service: deleteClusterService,
|
service: deleteClusterService,
|
||||||
|
@ -47,7 +47,8 @@
|
|||||||
return {data: {items: response.data.items.map(addTrackBy)}};
|
return {data: {items: response.data.items.map(addTrackBy)}};
|
||||||
|
|
||||||
function addTrackBy(cluster) {
|
function addTrackBy(cluster) {
|
||||||
cluster.trackBy = cluster.id;
|
var timestamp = cluster.updated_at ? cluster.updated_at : cluster.created_at;
|
||||||
|
cluster.trackBy = cluster.id + timestamp;
|
||||||
return cluster;
|
return cluster;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 NEC Corporation
|
||||||
|
*
|
||||||
|
* 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.clusters.update.service
|
||||||
|
* @description Service for the container-infra cluster update modal
|
||||||
|
*/
|
||||||
|
angular
|
||||||
|
.module('horizon.dashboard.container-infra.clusters')
|
||||||
|
.factory('horizon.dashboard.container-infra.clusters.update.service', updateService);
|
||||||
|
|
||||||
|
updateService.$inject = [
|
||||||
|
'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.clusters.resourceType',
|
||||||
|
'horizon.dashboard.container-infra.clusters.workflow'
|
||||||
|
];
|
||||||
|
|
||||||
|
function updateService(
|
||||||
|
magnum, policy, actionResult, gettext, $qExtensions, modal, toast, resourceType, workflow
|
||||||
|
) {
|
||||||
|
|
||||||
|
var config;
|
||||||
|
var message = {
|
||||||
|
success: gettext('Cluster %s was successfully updated.')
|
||||||
|
};
|
||||||
|
|
||||||
|
var service = {
|
||||||
|
perform: perform,
|
||||||
|
allowed: allowed
|
||||||
|
};
|
||||||
|
|
||||||
|
return service;
|
||||||
|
|
||||||
|
//////////////
|
||||||
|
|
||||||
|
function perform(selected, $scope) {
|
||||||
|
config = workflow.init('update', gettext('Update Cluster'), $scope);
|
||||||
|
config.model.id = selected.id;
|
||||||
|
|
||||||
|
// load current data
|
||||||
|
magnum.getCluster(selected.id).then(onLoad);
|
||||||
|
function onLoad(response) {
|
||||||
|
config.model.name = response.data.name
|
||||||
|
? response.data.name : "";
|
||||||
|
config.model.cluster_template_id = response.data.cluster_template_id
|
||||||
|
? response.data.cluster_template_id : "";
|
||||||
|
config.model.master_count = response.data.master_count
|
||||||
|
? response.data.master_count : null;
|
||||||
|
config.model.node_count = response.data.node_count
|
||||||
|
? response.data.node_count : null;
|
||||||
|
config.model.discovery_url = response.data.discovery_url
|
||||||
|
? response.data.discovery_url : "";
|
||||||
|
config.model.create_timeout = response.data.create_timeout
|
||||||
|
? response.data.create_timeout : null;
|
||||||
|
config.model.keypair = response.data.keypair
|
||||||
|
? response.data.keypair : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return modal.open(config).then(submit);
|
||||||
|
}
|
||||||
|
|
||||||
|
function allowed() {
|
||||||
|
return $qExtensions.booleanAsPromise(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit(context) {
|
||||||
|
var id = context.model.id;
|
||||||
|
context.model = cleanNullProperties(context.model);
|
||||||
|
return magnum.updateCluster(id, context.model, true)
|
||||||
|
.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] === "" ||
|
||||||
|
key === "tabs" || key === "id") {
|
||||||
|
delete model[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
function success(response) {
|
||||||
|
response.data.id = response.data.uuid;
|
||||||
|
toast.add('success', interpolate(message.success, [response.data.id]));
|
||||||
|
return actionResult.getActionResult()
|
||||||
|
.updated(resourceType, response.data.id)
|
||||||
|
.result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 NEC Corporation
|
||||||
|
*
|
||||||
|
* 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.clusters.update.service', function() {
|
||||||
|
|
||||||
|
var service, $scope, $q, deferred, magnum;
|
||||||
|
var selected = {
|
||||||
|
id: 1
|
||||||
|
};
|
||||||
|
var model = {
|
||||||
|
id: 1,
|
||||||
|
tabs: "",
|
||||||
|
keypair_id: "",
|
||||||
|
coe: null
|
||||||
|
};
|
||||||
|
var modal = {
|
||||||
|
open: function(config) {
|
||||||
|
config.model = model;
|
||||||
|
deferred = $q.defer();
|
||||||
|
deferred.resolve(config);
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var workflow = {
|
||||||
|
init: function (action, title) {
|
||||||
|
action = title;
|
||||||
|
return {model: model};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
beforeEach(module('horizon.app.core'));
|
||||||
|
beforeEach(module('horizon.framework'));
|
||||||
|
beforeEach(module('horizon.dashboard.container-infra.clusters'));
|
||||||
|
|
||||||
|
beforeEach(module(function($provide) {
|
||||||
|
$provide.value('horizon.dashboard.container-infra.clusters.workflow', workflow);
|
||||||
|
$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.clusters.update.service');
|
||||||
|
magnum = $injector.get('horizon.app.core.openstack-service-api.magnum');
|
||||||
|
deferred = $q.defer();
|
||||||
|
deferred.resolve({data: {uuid: 1, labels: "key1:val1,key2:val2"}});
|
||||||
|
spyOn(magnum, 'getCluster').and.returnValue(deferred.promise);
|
||||||
|
spyOn(magnum, 'updateCluster').and.returnValue(deferred.promise);
|
||||||
|
spyOn(workflow, 'init').and.returnValue({model: model});
|
||||||
|
spyOn(modal, 'open').and.callThrough();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should check the policy if the user is allowed to update cluster', function() {
|
||||||
|
var allowed = service.allowed();
|
||||||
|
expect(allowed).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('open the modal', inject(function($timeout) {
|
||||||
|
service.perform(selected, $scope);
|
||||||
|
|
||||||
|
expect(workflow.init).toHaveBeenCalled();
|
||||||
|
|
||||||
|
expect(modal.open).toHaveBeenCalledWith({model: model});
|
||||||
|
|
||||||
|
$timeout.flush();
|
||||||
|
$scope.$apply();
|
||||||
|
|
||||||
|
expect(magnum.updateCluster).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
})();
|
@ -66,9 +66,7 @@
|
|||||||
|
|
||||||
function onGetClusterTemplate(response) {
|
function onGetClusterTemplate(response) {
|
||||||
ctrl.clusterTemplate = response.data;
|
ctrl.clusterTemplate = response.data;
|
||||||
if (response.data.keypair_id === null) {
|
if ($scope.model.keypair === "") {
|
||||||
$scope.model.keypair = "";
|
|
||||||
} else {
|
|
||||||
$scope.model.keypair = response.data.keypair_id;
|
$scope.model.keypair = response.data.keypair_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
$q = _$q_;
|
$q = _$q_;
|
||||||
$scope = _$rootScope_.$new();
|
$scope = _$rootScope_.$new();
|
||||||
$scope.model = {
|
$scope.model = {
|
||||||
cluster_template_id: '1'
|
cluster_template_id: '1',
|
||||||
|
keypair: ''
|
||||||
};
|
};
|
||||||
magnum = $injector.get('horizon.app.core.openstack-service-api.magnum');
|
magnum = $injector.get('horizon.app.core.openstack-service-api.magnum');
|
||||||
controller = $injector.get('$controller');
|
controller = $injector.get('$controller');
|
||||||
@ -49,11 +50,12 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should keypair is changed by cluster template\'s keypair', function() {
|
it('should keypair is changed by cluster template\'s keypair', function() {
|
||||||
|
$scope.model.cluster_template_id = '1';
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
expect($scope.model.keypair).toBe('1');
|
expect($scope.model.keypair).toBe('1');
|
||||||
|
|
||||||
$scope.model.cluster_template_id = '';
|
$scope.model.cluster_template_id = '';
|
||||||
$scope.$digest();
|
$scope.$apply();
|
||||||
expect($scope.model.keypair).toBe('');
|
expect($scope.model.keypair).toBe('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -96,13 +96,15 @@
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: 'name',
|
key: 'name',
|
||||||
placeholder: gettext('Name of the cluster.')
|
placeholder: gettext('Name of the cluster.'),
|
||||||
|
readonly: action === 'update'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'cluster_template_id',
|
key: 'cluster_template_id',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
titleMap: clusterTemplates,
|
titleMap: clusterTemplates,
|
||||||
required: true
|
required: true,
|
||||||
|
readonly: action === 'update'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'template',
|
type: 'template',
|
||||||
@ -110,7 +112,8 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: gettext('Size'),
|
title: gettext('Size'),
|
||||||
@ -124,7 +127,8 @@
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: 'master_count',
|
key: 'master_count',
|
||||||
placeholder: gettext('The number of master nodes for the cluster.')
|
placeholder: gettext('The number of master nodes for the cluster.'),
|
||||||
|
readonly: action === 'update'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'node_count',
|
key: 'node_count',
|
||||||
@ -146,23 +150,27 @@
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
key: 'discovery_url',
|
key: 'discovery_url',
|
||||||
placeholder: gettext('Specifies custom discovery url for node discovery.')
|
placeholder: gettext('Specifies custom discovery url for node discovery.'),
|
||||||
|
readonly: action === 'update'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'create_timeout',
|
key: 'create_timeout',
|
||||||
/* eslint-disable max-len */
|
/* eslint-disable max-len */
|
||||||
placeholder: gettext('The timeout for cluster creation in minutes.'),
|
placeholder: gettext('The timeout for cluster creation in minutes.'),
|
||||||
description: gettext('Set to 0 for no timeout. The default is no timeout.')
|
description: gettext('Set to 0 for no timeout. The default is no timeout.'),
|
||||||
|
readonly: action === 'update'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'keypair',
|
key: 'keypair',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
titleMap: keypairs,
|
titleMap: keypairs,
|
||||||
required: true
|
required: true,
|
||||||
|
readonly: action === 'update'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
required: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
function MagnumAPI($timeout, apiService, toastService, gettext) {
|
function MagnumAPI($timeout, apiService, toastService, gettext) {
|
||||||
var service = {
|
var service = {
|
||||||
createCluster: createCluster,
|
createCluster: createCluster,
|
||||||
|
updateCluster: updateCluster,
|
||||||
getCluster: getCluster,
|
getCluster: getCluster,
|
||||||
getClusters: getClusters,
|
getClusters: getClusters,
|
||||||
deleteCluster: deleteCluster,
|
deleteCluster: deleteCluster,
|
||||||
@ -58,6 +59,13 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateCluster(id, params) {
|
||||||
|
return apiService.patch('/api/container_infra/clusters/' + id, params)
|
||||||
|
.error(function() {
|
||||||
|
toastService.add('error', gettext('Unable to update cluster.'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getCluster(id) {
|
function getCluster(id) {
|
||||||
return apiService.get('/api/container_infra/clusters/' + id)
|
return apiService.get('/api/container_infra/clusters/' + id)
|
||||||
.error(function() {
|
.error(function() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user