Add cluster scale-in/out actions
This patch adds scale-in and scale-out actions for cluster into Angularized clusters panel. Change-Id: I1964a26f94e9afc358fbf85e3b98a534fed2dc28
This commit is contained in:
parent
b8c45e2039
commit
6a370f178b
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
features:
|
||||
- >
|
||||
Scale-in and Scale-out actions for cluster added.
|
||||
These actions are added as row action for each cluster
|
||||
in Clusters table view. Although, this action is only
|
||||
for Angularized clusters panel.
|
|
@ -397,6 +397,78 @@ class Cluster(generic.View):
|
|||
updated_cluster.to_dict())
|
||||
|
||||
|
||||
@urls.register
|
||||
class ClusterActions(generic.View):
|
||||
"""API for Senlin cluster."""
|
||||
|
||||
url_regex = r'senlin/clusters/(?P<cluster_id>[^/]+)/(?P<action>[^/]+)$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, cluster_id, action):
|
||||
if action == "policy":
|
||||
"""Get policies of a single cluster with the cluster id.
|
||||
|
||||
The following get parameters may be passed in the GET
|
||||
|
||||
:param cluster_id: the id of the cluster
|
||||
|
||||
The result is a cluster object.
|
||||
"""
|
||||
policies = senlin.cluster_policy_list(request, cluster_id, {})
|
||||
|
||||
return {
|
||||
'items': [p.to_dict() for p in policies],
|
||||
}
|
||||
elif action == "":
|
||||
return None
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, cluster_id, action):
|
||||
if action == "policy":
|
||||
"""Update policies for the cluster."""
|
||||
params = request.DATA
|
||||
|
||||
new_attach_ids = params["ids"]
|
||||
old_attached = senlin.cluster_policy_list(request, cluster_id, {})
|
||||
|
||||
# Extract policies should be detached and execute
|
||||
for policy in old_attached:
|
||||
should_detach = True
|
||||
for new_id in new_attach_ids:
|
||||
if new_id == policy.policy_id:
|
||||
# This policy is already attached.
|
||||
should_detach = False
|
||||
break
|
||||
if should_detach:
|
||||
# If policy is not exist in new policies,
|
||||
# it should be removed
|
||||
senlin.cluster_detach_policy(
|
||||
request, cluster_id, policy.policy_id)
|
||||
|
||||
# Extract policies should be attached and execute
|
||||
for new_id in new_attach_ids:
|
||||
should_attach = True
|
||||
for policy in old_attached:
|
||||
if new_id == policy.policy_id:
|
||||
# This policy is already attached.
|
||||
should_attach = False
|
||||
break
|
||||
if should_attach:
|
||||
# If policy is not exist in old policies,
|
||||
# it should be added
|
||||
senlin.cluster_attach_policy(request, cluster_id,
|
||||
new_id, {})
|
||||
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/clusters/%s/policy' % cluster_id)
|
||||
elif action == "scale-in":
|
||||
count = request.DATA.get("count") or None
|
||||
return senlin.cluster_scale_in(request, cluster_id, count)
|
||||
elif action == "scale-out":
|
||||
count = request.DATA.get("count") or None
|
||||
return senlin.cluster_scale_out(request, cluster_id, count)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Policies(generic.View):
|
||||
"""API for Senlin policies."""
|
||||
|
@ -482,62 +554,3 @@ class Policy(generic.View):
|
|||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/policies/%s' % updated_policy.id,
|
||||
updated_policy.to_dict())
|
||||
|
||||
|
||||
@urls.register
|
||||
class ClusterPolicies(generic.View):
|
||||
"""API for Senlin cluster."""
|
||||
|
||||
url_regex = r'senlin/clusters/(?P<cluster_id>[^/]+)/policy$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, cluster_id):
|
||||
"""Get policies of a single cluster with the cluster id.
|
||||
|
||||
The following get parameters may be passed in the GET
|
||||
|
||||
:param cluster_id: the id of the cluster
|
||||
|
||||
The result is a cluster object.
|
||||
"""
|
||||
policies = senlin.cluster_policy_list(request, cluster_id, {})
|
||||
|
||||
return {
|
||||
'items': [p.to_dict() for p in policies],
|
||||
}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, cluster_id):
|
||||
"""Update policies for the cluster."""
|
||||
params = request.DATA
|
||||
|
||||
new_attach_ids = params["ids"]
|
||||
old_attached = senlin.cluster_policy_list(request, cluster_id, {})
|
||||
|
||||
# Extract policies should be detached and execute
|
||||
for policy in old_attached:
|
||||
should_detach = True
|
||||
for new_id in new_attach_ids:
|
||||
if new_id == policy.policy_id:
|
||||
# This policy is already attached.
|
||||
should_detach = False
|
||||
break
|
||||
if should_detach:
|
||||
# If policy is not exist in new policies, it should be removed
|
||||
senlin.cluster_detach_policy(
|
||||
request, cluster_id, policy.policy_id)
|
||||
|
||||
# Extract policies should be attached and execute
|
||||
for new_id in new_attach_ids:
|
||||
should_attach = True
|
||||
for policy in old_attached:
|
||||
if new_id == policy.policy_id:
|
||||
# This policy is already attached.
|
||||
should_attach = False
|
||||
break
|
||||
if should_attach:
|
||||
# If policy is not exist in old policies, it should be added
|
||||
senlin.cluster_attach_policy(request, cluster_id, new_id, {})
|
||||
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/clusters/%s/policy' % cluster_id)
|
||||
|
|
|
@ -165,6 +165,16 @@ def cluster_recover(request, cluster, params=None):
|
|||
senlinclient(request).recover_cluster(cluster, **params)
|
||||
|
||||
|
||||
def cluster_scale_in(request, cluster, count=None):
|
||||
"""Scale in a Cluster"""
|
||||
senlinclient(request).cluster_scale_in(cluster, count)
|
||||
|
||||
|
||||
def cluster_scale_out(request, cluster, count=None):
|
||||
"""Scale out a Cluster"""
|
||||
senlinclient(request).cluster_scale_out(cluster, count)
|
||||
|
||||
|
||||
def cluster_delete(request, cluster):
|
||||
"""Delete cluster."""
|
||||
senlinclient(request).delete_cluster(cluster)
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
'horizon.cluster.clusters.actions.manage-policy.service',
|
||||
'horizon.cluster.clusters.actions.delete.service',
|
||||
'horizon.cluster.clusters.actions.update.service',
|
||||
'horizon.cluster.clusters.actions.scale-in.service',
|
||||
'horizon.cluster.clusters.actions.scale-out.service',
|
||||
'horizon.app.core.clusters.resourceType'
|
||||
];
|
||||
|
||||
|
@ -43,6 +45,8 @@
|
|||
managePolicyService,
|
||||
deleteClusterService,
|
||||
updateClusterService,
|
||||
scaleInClusterService,
|
||||
scaleOutClusterService,
|
||||
clusterResourceType
|
||||
) {
|
||||
var clusterResource = registry.getResourceType(clusterResourceType);
|
||||
|
@ -84,6 +88,22 @@
|
|||
type: 'row'
|
||||
}
|
||||
})
|
||||
.append({
|
||||
id: 'scaleInClusterAction',
|
||||
service: scaleInClusterService,
|
||||
template: {
|
||||
text: gettext('Scale-in Cluster'),
|
||||
type: 'row'
|
||||
}
|
||||
})
|
||||
.append({
|
||||
id: 'scaleOutClusterAction',
|
||||
service: scaleOutClusterService,
|
||||
template: {
|
||||
text: gettext('Scale-out Cluster'),
|
||||
type: 'row'
|
||||
}
|
||||
})
|
||||
.append({
|
||||
id: 'deleteClusterAction',
|
||||
service: deleteClusterService,
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
createCluster: createCluster,
|
||||
updateCluster: updateCluster,
|
||||
deleteCluster: deleteCluster,
|
||||
scaleCluster: scaleCluster,
|
||||
createProfile: createProfile,
|
||||
updateProfile: updateProfile,
|
||||
deleteProfile: deleteProfile,
|
||||
|
@ -476,7 +477,39 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Policies
|
||||
/**
|
||||
* @name scaleCluster
|
||||
* @description
|
||||
* Scale a Cluster.
|
||||
*
|
||||
* @param {Object} id
|
||||
* Cluster ID to scale.
|
||||
* @param {Object} name
|
||||
* Cluster name to scale.
|
||||
* @param {Object} scale
|
||||
* Direction of scale. 'in' or 'out'.
|
||||
* @param {Object} count
|
||||
* Count to scale-in a cluster.
|
||||
* @param {boolean} suppressError
|
||||
* If passed in, this will not show the default error handling
|
||||
* @returns {Object} The result of the API call
|
||||
*/
|
||||
function scaleCluster(id, name, scale, count, suppressError) {
|
||||
var promise = apiService.put(
|
||||
'/api/senlin/clusters/' + id + '/scale-' + scale,
|
||||
{count: count});
|
||||
|
||||
return suppressError ? promise : promise.error(function() {
|
||||
var msg = gettext('Unable to scale-%(scale)s the cluster with name: %(name)s');
|
||||
var scaleMsg;
|
||||
if (scale === 'in') {
|
||||
scaleMsg = gettext('in');
|
||||
} else {
|
||||
scaleMsg = gettext('out');
|
||||
}
|
||||
toastService.add('error', interpolate(msg, { scale: scaleMsg, name: name }, true));
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* @name getClusterPolicies
|
||||
|
@ -511,6 +544,8 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Policies
|
||||
|
||||
/*
|
||||
* @name getPolicies
|
||||
* @description
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use self 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.cluster.clusters.actions.scale-in.service
|
||||
* @Description
|
||||
* restart container.
|
||||
*/
|
||||
angular
|
||||
.module('horizon.cluster.clusters.actions')
|
||||
.factory('horizon.cluster.clusters.actions.scale-in.service', scaleService);
|
||||
|
||||
scaleService.$inject = [
|
||||
'horizon.app.core.openstack-service-api.senlin',
|
||||
'horizon.app.core.clusters.resourceType',
|
||||
'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'
|
||||
];
|
||||
|
||||
function scaleService(
|
||||
senlin, resourceType, actionResult, gettext, $qExtensions, modal, toast
|
||||
) {
|
||||
// schema
|
||||
var schema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
count: {
|
||||
title: gettext("Node Count"),
|
||||
type: "number",
|
||||
minimum: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// form
|
||||
var form = [
|
||||
{
|
||||
type: 'section',
|
||||
htmlClass: 'row',
|
||||
items: [
|
||||
{
|
||||
type: 'section',
|
||||
htmlClass: 'col-sm-12',
|
||||
items: [
|
||||
{
|
||||
key: "count",
|
||||
placeholder: gettext("Specify node count for scale-in.")
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// model
|
||||
var model;
|
||||
|
||||
var message = {
|
||||
success: gettext('Cluster scale-in %s was successfully accepted.')
|
||||
};
|
||||
|
||||
var service = {
|
||||
initAction: initAction,
|
||||
allowed: allowed,
|
||||
perform: perform
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
//////////////
|
||||
|
||||
// include this function in your service
|
||||
// if you plan to emit events to the parent controller
|
||||
function initAction() {
|
||||
}
|
||||
|
||||
function allowed() {
|
||||
return $qExtensions.booleanAsPromise(true);
|
||||
}
|
||||
|
||||
function perform(selected) {
|
||||
model = {
|
||||
id: selected.id,
|
||||
name: selected.name,
|
||||
count: null
|
||||
};
|
||||
// modal config
|
||||
var config = {
|
||||
title: gettext('Scale-In'),
|
||||
submitText: gettext('Scale-In'),
|
||||
schema: schema,
|
||||
form: form,
|
||||
model: model
|
||||
};
|
||||
return modal.open(config).then(submit);
|
||||
|
||||
function submit(context) {
|
||||
var id = context.model.id;
|
||||
var name = context.model.name;
|
||||
delete context.model.id;
|
||||
delete context.model.name;
|
||||
return senlin.scaleCluster(id, name, 'in', context.model.count).then(function() {
|
||||
toast.add('success', interpolate(message.success, [name]));
|
||||
var result = actionResult.getActionResult().updated(resourceType, id);
|
||||
return result.result;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use self 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.cluster.clusters.actions.scale-in.service
|
||||
* @Description
|
||||
* restart container.
|
||||
*/
|
||||
angular
|
||||
.module('horizon.cluster.clusters.actions')
|
||||
.factory('horizon.cluster.clusters.actions.scale-out.service', scaleService);
|
||||
|
||||
scaleService.$inject = [
|
||||
'horizon.app.core.openstack-service-api.senlin',
|
||||
'horizon.app.core.clusters.resourceType',
|
||||
'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'
|
||||
];
|
||||
|
||||
function scaleService(
|
||||
senlin, resourceType, actionResult, gettext, $qExtensions, modal, toast
|
||||
) {
|
||||
// schema
|
||||
var schema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
count: {
|
||||
title: gettext("Node Count"),
|
||||
type: "number",
|
||||
minimum: 1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// form
|
||||
var form = [
|
||||
{
|
||||
type: 'section',
|
||||
htmlClass: 'row',
|
||||
items: [
|
||||
{
|
||||
type: 'section',
|
||||
htmlClass: 'col-sm-12',
|
||||
items: [
|
||||
{
|
||||
key: "count",
|
||||
placeholder: gettext("Specify node count for scale-out.")
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// model
|
||||
var model;
|
||||
|
||||
var message = {
|
||||
success: gettext('Cluster scale-out %s was successfully accepted.')
|
||||
};
|
||||
|
||||
var service = {
|
||||
initAction: initAction,
|
||||
allowed: allowed,
|
||||
perform: perform
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
//////////////
|
||||
|
||||
// include this function in your service
|
||||
// if you plan to emit events to the parent controller
|
||||
function initAction() {
|
||||
}
|
||||
|
||||
function allowed() {
|
||||
return $qExtensions.booleanAsPromise(true);
|
||||
}
|
||||
|
||||
function perform(selected) {
|
||||
model = {
|
||||
id: selected.id,
|
||||
name: selected.name,
|
||||
count: null
|
||||
};
|
||||
// modal config
|
||||
var config = {
|
||||
title: gettext('Scale-Out'),
|
||||
submitText: gettext('Scale-Out'),
|
||||
schema: schema,
|
||||
form: form,
|
||||
model: model
|
||||
};
|
||||
return modal.open(config).then(submit);
|
||||
|
||||
function submit(context) {
|
||||
var id = context.model.id;
|
||||
var name = context.model.name;
|
||||
delete context.model.id;
|
||||
delete context.model.name;
|
||||
return senlin.scaleCluster(id, name, 'out', context.model.count).then(function() {
|
||||
toast.add('success', interpolate(message.success, [name]));
|
||||
var result = actionResult.getActionResult().updated(resourceType, id);
|
||||
return result.result;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
Loading…
Reference in New Issue