Add exec/kill action

This patch adds execute and kill action as item action
in table view and details view.

Change-Id: I782c893c3ad4847ac80b6e5282c2811f70e28929
Partial-Implements: blueprint add-container-operations
This commit is contained in:
Shu Muto 2016-12-21 20:07:44 +09:00
parent 299cc43c98
commit 7e1829e4b6
7 changed files with 333 additions and 30 deletions

View File

@ -22,7 +22,7 @@ LOG = logging.getLogger(__name__)
CONTAINER_CREATE_ATTRS = ['name', 'image', 'command', 'cpu', 'memory',
'environment', 'workdir', 'ports', 'hostname',
'labels']
'labels', 'image_pull_policy']
@memoized
@ -109,3 +109,11 @@ def container_pause(request, id):
def container_unpause(request, id):
return zunclient(request).containers.unpause(id)
def container_execute(request, id, command):
return zunclient(request).containers.execute(id, command)
def container_kill(request, id, signal=None):
return zunclient(request).containers.kill(id, signal)

View File

@ -63,6 +63,12 @@ class ContainerActions(generic.View):
return client.container_pause(request, id)
elif action == 'unpause':
return client.container_unpause(request, id)
elif action == 'execute':
command = request.DATA.get("command")
return client.container_execute(request, id, command)
elif action == 'kill':
signal = request.DATA.get("signal")
return client.container_kill(request, id, signal)
@urls.register

View File

@ -35,6 +35,8 @@
'horizon.dashboard.container.containers.reboot.service',
'horizon.dashboard.container.containers.pause.service',
'horizon.dashboard.container.containers.unpause.service',
'horizon.dashboard.container.containers.execute.service',
'horizon.dashboard.container.containers.kill.service',
'horizon.dashboard.container.containers.resourceType',
];
@ -48,9 +50,32 @@
rebootContainerService,
pauseContainerService,
unpauseContainerService,
executeContainerService,
killContainerService,
resourceType)
{
var containersResourceType = registry.getResourceType(resourceType);
containersResourceType.globalActions
.append({
id: 'createContainerAction',
service: createContainerService,
template: {
type: 'create',
text: gettext('Create Container')
}
});
containersResourceType.batchActions
.append({
id: 'batchDeleteContainerAction',
service: deleteContainerService,
template: {
type: 'delete-selected',
text: gettext('Delete Containers')
}
});
containersResourceType.itemActions
.append({
id: 'startContainerAction',
@ -87,6 +112,20 @@
text: gettext('Unpause Container')
}
})
.append({
id: 'executeContainerAction',
service: executeContainerService,
template: {
text: gettext('Execute Command')
}
})
.append({
id: 'killContainerAction',
service: killContainerService,
template: {
text: gettext('Send Kill Signal')
}
})
.append({
id: 'deleteContainerAction',
service: deleteContainerService,
@ -95,24 +134,6 @@
text: gettext('Delete Container')
}
});
containersResourceType.batchActions
.append({
id: 'createContainerAction',
service: createContainerService,
template: {
type: 'create',
text: gettext('Create Container')
}
})
.append({
id: 'batchDeleteContainerAction',
service: deleteContainerService,
template: {
type: 'delete-selected',
text: gettext('Delete Containers')
}
});
}
})();

View File

@ -0,0 +1,118 @@
/**
* 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';
angular
.module('horizon.dashboard.container.containers')
.factory(
'horizon.dashboard.container.containers.execute.service',
executeContainerService);
executeContainerService.$inject = [
'horizon.app.core.openstack-service-api.zun',
'horizon.framework.util.i18n.gettext',
'horizon.framework.util.q.extensions',
'horizon.framework.widgets.form.ModalFormService',
'horizon.framework.widgets.toast.service'
];
/**
* @ngdoc factory
* @name horizon.dashboard.container.containers.execute.service
* @description
* Service for the command execution in the container
*/
function executeContainerService(
zun, gettext, $qExtensions, modal, toast
) {
// schema
var schema = {
type: "object",
properties: {
command: {
title: gettext("Command"),
type: "string"
}
}
};
// form
var form = [
{
type: "section",
htmlClass: "col-sm-12",
items: [
{
key: "command",
placeholder: gettext("The command to execute."),
required: true
}
]
}
];
// model
var model;
var message = {
success: gettext("Command was successfully executed at container %s.")
};
var service = {
initAction: initAction,
perform: perform,
allowed: allowed
};
return service;
//////////////
function initAction() {
}
function allowed() {
return $qExtensions.booleanAsPromise(true);
}
function perform(selected) {
model = {
id: selected.id,
name: selected.name,
command: ''
};
// modal config
var config = {
title: gettext("Execute Command"),
submitText: gettext("Execute"),
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 zun.executeContainer(id, context.model).then(function(response) {
toast.add('success', interpolate(message.success, [name]));
});
}
}
})();

View File

@ -0,0 +1,6 @@
<p translate>
Signal to send to the container: integer or string like SIGINT.
When not set, SIGKILL is set as default value and the container will exit.
The supported signals varies between platform.
Besides, you can omit 'SIG' prefix.
</p>

View File

@ -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';
angular
.module('horizon.dashboard.container.containers')
.factory(
'horizon.dashboard.container.containers.kill.service',
killContainerService);
killContainerService.$inject = [
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.containers.basePath',
'horizon.framework.util.i18n.gettext',
'horizon.framework.util.q.extensions',
'horizon.framework.widgets.form.ModalFormService',
'horizon.framework.widgets.toast.service'
];
/**
* @ngdoc factory
* @name horizon.dashboard.container.containers.kill.service
* @description
* Service to send kill signals to the container
*/
function killContainerService(
zun, basePath, gettext, $qExtensions, modal, toast
) {
// schema
var schema = {
type: "object",
properties: {
signal: {
title: gettext("Kill Signal"),
type: "string"
}
}
};
// form
var form = [
{
type: 'section',
htmlClass: 'row',
items: [
{
type: 'section',
htmlClass: 'col-sm-6',
items: [
{
"key": "signal",
"placeholder": gettext("The kill signal to send.")
}
]
},
{
type: 'template',
templateUrl: basePath + 'operations/kill.help.html'
}
]
}
];
// model
var model;
var message = {
success: gettext('Kill signal was successfully sent to container %s.')
};
var service = {
initAction: initAction,
perform: perform,
allowed: allowed
};
return service;
//////////////
function initAction() {
}
function allowed() {
return $qExtensions.booleanAsPromise(true);
}
function perform(selected) {
model = {
id: selected.id,
name: selected.name,
signal: ''
};
// modal config
var config = {
"title": gettext('Send Kill Signal'),
"submitText": gettext('Send'),
"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 zun.killContainer(id, context.model).then(function(response) {
toast.add('success', interpolate(message.success, [name]));
});
}
}
})();

View File

@ -36,7 +36,9 @@
logsContainer: logsContainer,
rebootContainer: rebootContainer,
pauseContainer: pauseContainer,
unpauseContainer: unpauseContainer
unpauseContainer: unpauseContainer,
executeContainer: executeContainer,
killContainer: killContainer
};
return service;
@ -140,17 +142,31 @@
}
function pauseContainer(id) {
return apiService.post('/api/zun/containers/' + id + '/pause')
.error(function() {
toastService.add('error', gettext('Unable to pause Container'));
});
}
return apiService.post('/api/zun/containers/' + id + '/pause')
.error(function() {
toastService.add('error', gettext('Unable to pause Container'));
});
}
function unpauseContainer(id) {
return apiService.post('/api/zun/containers/' + id + '/unpause')
.error(function() {
toastService.add('error', gettext('Unable to unpause of Container'));
});
}
return apiService.post('/api/zun/containers/' + id + '/unpause')
.error(function() {
toastService.add('error', gettext('Unable to unpause of Container'));
});
}
function executeContainer(id, params) {
return apiService.post('/api/zun/containers/' + id + '/execute', params)
.error(function() {
toastService.add('error', gettext('Unable to execute the command'));
});
}
function killContainer(id, params) {
return apiService.post('/api/zun/containers/' + id + '/kill', params)
.error(function() {
toastService.add('error', gettext('Unable to send kill signal'));
});
}
}
}());