Add update action for container

This patch adds update action for container as item action.

Change-Id: Ie80a1f447e218213adaff2253a1ec1afd7fe5672
This commit is contained in:
Shu Muto 2017-06-14 16:20:45 +09:00
parent 2fe0d61529
commit 6ab8f75e59
6 changed files with 201 additions and 37 deletions

View File

@ -43,39 +43,37 @@ def zunclient(request):
return c
def container_create(request, **kwargs):
def _cleanup_params(attrs, check, **params):
args = {}
run = False
for (key, value) in kwargs.items():
for (key, value) in params.items():
if key == "run":
run = value
continue
elif key == "interactive":
args["interactive"] = value
continue
elif key == "restart_policy":
args[key] = utils.check_restart_policy(value)
continue
if key in CONTAINER_CREATE_ATTRS:
elif key == "environment" or key == "labels":
values = {}
vals = value.split(",")
for v in vals:
kv = v.split("=", 1)
values[kv[0]] = kv[1]
args[str(key)] = values
elif key in attrs:
if value is None:
value = ''
args[str(key)] = str(value)
else:
elif check:
raise exceptions.BadRequest(
"Key must be in %s" % ",".join(CONTAINER_CREATE_ATTRS))
if key == "environment":
envs = {}
vals = value.split(",")
for v in vals:
kv = v.split("=", 1)
envs[kv[0]] = kv[1]
args["environment"] = envs
elif key == "labels":
labels = {}
vals = value.split(",")
for v in vals:
kv = v.split("=", 1)
labels[kv[0]] = kv[1]
args["labels"] = labels
"Key must be in %s" % ",".join(attrs))
return args, run
def container_create(request, **kwargs):
args, run = _cleanup_params(CONTAINER_CREATE_ATTRS, True, **kwargs)
response = None
if run:
response = zunclient(request).containers.run(**args)
@ -84,6 +82,11 @@ def container_create(request, **kwargs):
return response
def container_update(request, id, **kwargs):
args, run = _cleanup_params(CONTAINER_CREATE_ATTRS, True, **kwargs)
return zunclient(request).containers.update(id, **args)
def container_delete(request, id, force=False):
# TODO(shu-mutou): force option should be provided by user.
return zunclient(request).containers.delete(id, force)
@ -147,13 +150,5 @@ def image_list(request, limit=None, marker=None, sort_key=None,
def image_create(request, **kwargs):
args = {}
for (key, value) in kwargs.items():
if key in IMAGE_PULL_ATTRS:
args[str(key)] = str(value)
else:
raise exceptions.BadRequest(
"Key must be in %s" % ",".join(IMAGE_PULL_ATTRS))
args = _cleanup_params(IMAGE_PULL_ATTRS, True, **kwargs)
return zunclient(request).images.create(**args)

View File

@ -46,6 +46,17 @@ class Container(generic.View):
"""
return client.container_delete(request, id, force=True)
@rest_utils.ajax(data_required=True)
def patch(self, request, id):
"""Update a Container.
Returns the Container object on success.
"""
container = client.container_update(request, id, **request.DATA)
return rest_utils.CreatedResponse(
'/api/zun/containers/%s' % id,
container.to_dict())
@urls.register
class ContainerActions(generic.View):

View File

@ -33,6 +33,7 @@
'horizon.framework.conf.resource-type-registry.service',
'horizon.framework.util.i18n.gettext',
'horizon.dashboard.container.containers.create.service',
'horizon.dashboard.container.containers.update.service',
'horizon.dashboard.container.containers.delete.service',
'horizon.dashboard.container.containers.delete-force.service',
'horizon.dashboard.container.containers.start.service',
@ -50,6 +51,7 @@
registry,
gettext,
createContainerService,
updateContainerService,
deleteContainerService,
deleteContainerForceService,
startContainerService,
@ -92,6 +94,13 @@
text: gettext('Refresh')
}
})
.append({
id: 'updateContainerAction',
service: updateContainerService,
template: {
text: gettext('Update Container')
}
})
.append({
id: 'startContainerAction',
service: startContainerService,

View File

@ -0,0 +1,144 @@
/**
* 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.containers.update.service
* @description Service for the container update modal
*/
angular
.module('horizon.dashboard.container.containers')
.factory('horizon.dashboard.container.containers.update.service', updateService);
updateService.$inject = [
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.resourceType',
'horizon.dashboard.container.containers.workflow',
'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 updateService(
policy, zun, resourceType, workflow,
actionResult, gettext, $qExtensions, modal, toast
) {
var message = {
success: gettext('Container %s was successfully updated.')
};
var service = {
initAction: initAction,
perform: perform,
allowed: allowed
};
return service;
//////////////
function initAction() {
}
function perform(selected) {
var title, submitText;
title = gettext('Update Container');
submitText = gettext('Update');
var config = workflow.init('update', title, submitText);
config.model.id = selected.id;
// load current data
zun.getContainer(selected.id).then(onLoad);
function onLoad(response) {
config.model.name = response.data.name
? response.data.name : "";
config.model.image = response.data.image
? response.data.image : "";
config.model.image_driver = response.data.image_driver
? response.data.image_driver : "docker";
config.model.image_pull_policy = response.data.image_pull_policy
? response.data.image_pull_policy : "";
config.model.command = response.data.command
? response.data.command : "";
config.model.cpu = response.data.cpu
? response.data.cpu : "";
config.model.memory = response.data.memory
? parseInt(response.data.memory, 10) : "";
config.model.restart_policy = response.data.restart_policy.Name
? response.data.restart_policy.Name : "";
config.model.restart_policy_max_retry = response.data.restart_policy.MaximumRetryCount
? parseInt(response.data.restart_policy.MaximumRetryCount, 10) : null;
config.model.workdir = response.data.workdir
? response.data.workdir : "";
config.model.environment = response.data.environment
? hashToString(response.data.environment) : "";
config.model.interactive = response.data.interactive
? response.data.interactive : false;
config.model.labels = response.data.labels
? hashToString(response.data.labels) : "";
}
return modal.open(config).then(submit);
}
function allowed() {
return policy.ifAllowed({ rules: [['container', 'edit_container']] });
}
function submit(context) {
var id = context.model.id;
context.model = cleanUpdateProperties(context.model);
return zun.updateContainer(id, context.model).then(success);
}
function success(response) {
response.data.id = response.data.uuid;
toast.add('success', interpolate(message.success, [response.data.name]));
var result = actionResult.getActionResult().updated(resourceType, response.data.name);
return result.result;
}
function cleanUpdateProperties(model) {
// Initially clean fields that don't have any value.
// Not only "null", blank too.
// only "cpu" and "memory" fields are editable.
for (var key in model) {
if (model.hasOwnProperty(key) && model[key] === null || model[key] === "" ||
(key !== "cpu" && key !== "memory")) {
delete model[key];
}
}
return model;
}
function hashToString(hash) {
var str = "";
for (var key in hash) {
if (hash.hasOwnProperty(key)) {
if (str.length > 0) {
str += ",";
}
str += key + "=" + hash[key];
}
}
return str;
}
}
})();

View File

@ -82,8 +82,7 @@
cpu: {
title: gettext("CPU"),
type: "number",
minimum: 0,
step: 0.1
minimum: 0
},
memory: {
title: gettext("Memory"),
@ -182,7 +181,8 @@
},
{
key: "run",
readonly: action === "update"
readonly: action === "update",
condition: action === "update"
}
]
}
@ -200,6 +200,7 @@
items: [
{
key: "cpu",
step: 0.1,
placeholder: gettext("The number of virtual cpu for this container.")
},
{
@ -292,8 +293,6 @@
];
// model
model = {
//id: "",
//uuid: "",
// info
name: "",
image: "",

View File

@ -29,6 +29,7 @@
var imagesPath = '/api/zun/images/';
var service = {
createContainer: createContainer,
updateContainer: updateContainer,
getContainer: getContainer,
getContainers: getContainers,
deleteContainer: deleteContainer,
@ -57,6 +58,11 @@
return apiService.post(containersPath, params).error(error(msg));
}
function updateContainer(id, params) {
var msg = gettext('Unable to update Container.');
return apiService.patch(containersPath + id, params).error(error(msg));
}
function getContainer(id) {
var msg = gettext('Unable to retrieve the Container.');
return apiService.get(containersPath + id).error(error(msg));