Remove 'host' parameter from deleting image

'host' parameter for deleting image has removed from client.
Zun UI should remove this parameter.

Now, due to only 'id' is necessary for deleting image,
we can use 'delete-selected' type action and common deletion
confirmation dialog. Using the common deletion action,
we can also implement deletion action as batch action.

Change-Id: I2adae5cb466e620177c3788ac340f6464711e21d
Closes-Bug: #1799125
This commit is contained in:
Shu Muto 2018-10-22 14:44:44 +09:00
parent 0022f4a0ba
commit 3f8cb502b6
4 changed files with 122 additions and 96 deletions

View File

@ -226,6 +226,15 @@ class Images(generic.View):
result = client.image_list(request)
return {'items': [change_to_id(i.to_dict()) for i in result]}
@rest_utils.ajax(data_required=True)
def delete(self, request):
"""Delete one or more Images by id.
Returns HTTP 204 (no content) on successful deletion.
"""
for id in request.DATA:
client.image_delete(request, id)
@rest_utils.ajax(data_required=True)
def post(self, request):
"""Create a new Image.
@ -238,17 +247,6 @@ class Images(generic.View):
new_image.to_dict())
@urls.register
class Image(generic.View):
"""API for operate a single image"""
url_regex = r'zun/images/(?P<id>[^/]+)$'
@rest_utils.ajax(data_required=True)
def delete(self, request, id):
"""Delete a specific image"""
client.image_delete(request, id, **request.DATA)
@urls.register
class Hosts(generic.View):
"""API for Zun Hosts"""

View File

@ -56,6 +56,16 @@
}
});
imagesResourceType.batchActions
.append({
id: 'deleteImageAction',
service: deleteImageService,
template: {
type: 'delete-selected',
text: gettext('Delete Images')
}
});
imagesResourceType.itemActions
.append({
id: 'deleteImageAction',

View File

@ -19,123 +19,138 @@
* @ngDoc factory
* @name horizon.dashboard.container.images.actions.delete.service
* @Description
* restart container.
* Brings up the delete images confirmation modal dialog.
* On submit, delete selected resources.
* On cancel, do nothing.
*/
angular
.module('horizon.dashboard.container.images.actions')
.module('horizon.dashboard.container.images')
.factory('horizon.dashboard.container.images.actions.delete.service', deleteService);
deleteService.$inject = [
'$location',
'$q',
'$rootScope',
'horizon.app.core.openstack-service-api.zun',
'horizon.dashboard.container.images.basePath',
'horizon.dashboard.container.images.resourceType',
'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.framework.widgets.modal.deleteModalService',
'horizon.framework.widgets.table.events',
'horizon.framework.widgets.toast.service',
'horizon.dashboard.container.images.resourceType',
'horizon.dashboard.container.images.events'
];
function deleteService(
zun, basePath, resourceType, actionResult, gettext, $qExtensions, modal, toast
$location, $q, $rootScope, zun, policy, actionResult, gettext, $qExtensions, deleteModal,
tableEvents, toast, resourceType, events
) {
var push = Array.prototype.push;
var hosts = [{value: "", name: gettext("Select host to remove the image from.")}];
// schema
var schema = {
type: "object",
properties: {
host: {
title: gettext("Host"),
type: "string"
}
}
var scope;
var context = {
labels: null,
deleteEntity: deleteEntity,
successEvent: events.DELETE_SUCCESS
};
// form
var form = [
{
type: 'section',
htmlClass: 'row',
items: [
{
type: 'section',
htmlClass: 'col-sm-12',
items: [
{
key: 'host',
type: "select",
titleMap: hosts,
required: true
}
]
}
]
}
];
// model
var model;
var message = {
success: gettext('Container %s was successfully restarted.')
};
var service = {
initAction: initAction,
allowed: allowed,
perform: perform
};
var notAllowedMessage = gettext("You are not allowed to delete images: %s");
return service;
//////////////
// include this function in your service
// if you plan to emit events to the parent controller
function initAction() {
// get hosts for zun
zun.getHosts().then(onGetZunHosts);
function onGetZunHosts(response) {
var hs = [];
response.data.items.forEach(function (host) {
hs.push({value: host.id, name: host.hostname});
});
push.apply(hosts, hs);
}
}
function allowed() {
return $qExtensions.booleanAsPromise(true);
}
function perform(selected) {
model = {
id: selected.id,
repo: selected.repo,
host: ""
};
// modal config
var config = {
"title": interpolate(gettext('Delete Image %s'), [model.repo]),
"submitText": gettext('Delete'),
"schema": schema,
"form": form,
"model": model
};
return modal.open(config).then(submit);
function submit(context) {
var id = context.model.id;
var repo = context.model.repo;
var host = context.model.host;
return zun.deleteImage(id, host).then(function() {
toast.add('success', interpolate(message.success, [repo]));
var result = actionResult.getActionResult().updated(resourceType, id);
return result.result;
});
// delete selected resource objects
function perform(selected, newScope) {
scope = newScope;
selected = angular.isArray(selected) ? selected : [selected];
context.labels = labelize(selected.length);
return $qExtensions.allSettled(selected.map(checkPermission)).then(afterCheck);
}
function labelize(count) {
return {
title: ngettext('Confirm Delete Image',
'Confirm Delete Images', count),
/* eslint-disable max-len */
message: ngettext('You have selected "%s". Please confirm your selection. Deleted image is not recoverable.',
'You have selected "%s". Please confirm your selection. Deleted images are not recoverable.', count),
/* eslint-enable max-len */
submit: ngettext('Delete Image',
'Delete Images', count),
success: ngettext('Deleted Image: %s.',
'Deleted Images: %s.', count),
error: ngettext('Unable to delete Image: %s.',
'Unable to delete Images: %s.', count)
};
}
// for batch delete
function checkPermission(selected) {
return {promise: allowed(selected), context: selected};
}
// for batch delete
function afterCheck(result) {
var outcome = $q.reject(); // Reject the promise by default
if (result.fail.length > 0) {
toast.add('error', getMessage(notAllowedMessage, result.fail));
outcome = $q.reject(result.fail);
}
if (result.pass.length > 0) {
outcome = deleteModal.open(scope, result.pass.map(getEntity), context).then(createResult);
}
return outcome;
}
function createResult(deleteModalResult) {
// To make the result of this action generically useful, reformat the return
// from the deleteModal into a standard form
var result = actionResult.getActionResult();
deleteModalResult.pass.forEach(function markDeleted(item) {
result.updated(resourceType, getEntity(item).id);
});
deleteModalResult.fail.forEach(function markFailed(item) {
result.failed(resourceType, getEntity(item).id);
});
var indexPath = '/admin/container/images';
var currentPath = $location.path();
if (result.result.failed.length === 0 && result.result.updated.length > 0 &&
currentPath !== indexPath) {
$location.path(indexPath);
} else {
$rootScope.$broadcast(tableEvents.CLEAR_SELECTIONS);
return result.result;
}
}
function getMessage(message, entities) {
return interpolate(message, [entities.map(getName).join(", ")]);
}
function getName(result) {
return getEntity(result).name;
}
// for batch delete
function getEntity(result) {
return result.context;
}
// call delete REST API
function deleteEntity(id) {
return zun.deleteImage(id);
}
}
})();

View File

@ -243,9 +243,12 @@
return apiService.get(imagesPath).error(error(msg));
}
function deleteImage(id, host) {
var msg = gettext('Unable to delete the Image.');
return apiService.delete(imagesPath + id, {host: host}).error(error(msg));
function deleteImage(id, suppressError) {
var promise = apiService.delete(imagesPath, [id]);
return suppressError ? promise : promise.error(function() {
var msg = gettext('Unable to delete the Image with id: %(id)s');
toastService.add('error', interpolate(msg, { id: id }, true));
});
}
///////////