diff --git a/devstack/plugin.sh b/devstack/plugin.sh index 5128852..cb7a65a 100644 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -15,7 +15,7 @@ function install_zaqar_ui { } function configure_zaqar_ui { - cp -a ${ZAQAR_UI_DIR}/zaqar_ui/enabled/* ${DEST}/horizon/openstack_dashboard/local/enabled/ + #cp -a ${ZAQAR_UI_DIR}/zaqar_ui/enabled/* ${DEST}/horizon/openstack_dashboard/local/enabled/ # NOTE: If locale directory does not exist, compilemessages will fail, # so check for an existence of locale directory is required. if [ -d ${ZAQAR_UI_DIR}/zaqar_ui/locale ]; then diff --git a/zaqar_ui/api/rest/zaqar.py b/zaqar_ui/api/rest/zaqar.py index 816d523..1385e62 100644 --- a/zaqar_ui/api/rest/zaqar.py +++ b/zaqar_ui/api/rest/zaqar.py @@ -45,12 +45,19 @@ def _load_yaml(data): @urls.register class Queue(generic.View): """API for retrieving a single queue""" - url_regex = r'zaqar/queue/(?P[^/]+)$' + url_regex = r'zaqar/queues/(?P[^/]+)$' @rest_utils.ajax() def get(self, request, queue_name): """Get a specific queue""" - return zaqar.queue_get(request, queue_name).to_dict() + queue = zaqar.queue_get(request, queue_name) + stats = queue.stats['messages'] + queue_info = {'name': queue_name, + 'claimed': stats['claimed'], + 'free': stats['free'], + 'total': stats['total'], + 'metadata': queue.metadata()} + return queue_info @rest_utils.ajax(data_required=True) def post(self, request, queue_name): @@ -59,12 +66,28 @@ class Queue(generic.View): Returns the updated queue object on success. """ queue = zaqar.queue_update(request, queue_name, **request.DATA) - location = '/api/zaqar/queue/%s' % queue._name + location = '/api/zaqars/queue/%s' % queue._name response = {'name': queue._name, 'metadata': queue._metadata} return rest_utils.CreatedResponse(location, response) +@urls.register +class QueueActions(generic.View): + """API for actions on a single queue""" + url_regex = r'zaqar/queues/(?P[^/]+)/(?P[^/]+)$' + + @rest_utils.ajax(data_required=True) + def post(self, request, queue_name, action): + """Actions for a queue""" + if action == "purge": + resource_types = request.DATA.get("resource_types") + zaqar.queue_purge(request, queue_name, resource_types) + elif action == "share": + # FIXME(flwang): This is placeholder for pre-signed feature. + pass + + @urls.register class Queues(generic.View): """API for queues""" diff --git a/zaqar_ui/api/zaqar.py b/zaqar_ui/api/zaqar.py index 1bf7db5..ca3da35 100644 --- a/zaqar_ui/api/zaqar.py +++ b/zaqar_ui/api/zaqar.py @@ -89,6 +89,11 @@ def queue_get(request, queue_name): return zaqarclient(request).queue(queue_name, auto_create=False) +def queue_purge(request, queue_name, resource_types): + queue = zaqarclient(request).queue(queue_name, auto_create=False) + queue.purge(resource_types=resource_types) + + def subscription_list(request, queue_name): return [{'subscriber': s.subscriber, 'id': s.id, diff --git a/zaqar_ui/static/app/core/openstack-service-api/zaqar.service.js b/zaqar_ui/static/app/core/openstack-service-api/zaqar.service.js index 7ecdfe9..a922c70 100644 --- a/zaqar_ui/static/app/core/openstack-service-api/zaqar.service.js +++ b/zaqar_ui/static/app/core/openstack-service-api/zaqar.service.js @@ -35,9 +35,11 @@ var service = { getQueues: getQueues, + getQueue: getQueue, createQueue: createQueue, deleteQueue: deleteQueue, updateQueue: updateQueue, + purgeQueue: purgeQueue, getSubscriptions: getSubscriptions, addSubscription: addSubscription, deleteSubscription: deleteSubscription, @@ -62,6 +64,11 @@ return apiService.get(queuePath).error(error(msg)); } + function getQueue(queueName) { + var msg = gettext('Unable to retrieve the Queue.'); + return apiService.get(queuePath + queueName).error(error(msg)); + } + function createQueue(newQueue) { var msg = gettext('Unable to create the queue.'); return apiService.put(queuePath, newQueue).error(error(msg)); @@ -73,11 +80,18 @@ function updateQueue(queue) { var msg = gettext('Unable to update the queue.'); - var url = '/api/zaqar/queue/' + queue.queue_name; + var url = queuePath + queue.queue_name; var form = { metadata: queue.metadata }; return apiService.post(url, form).error(error(msg)); } + function purgeQueue(queueName, resourceTypes) { + var msg = gettext('Unable to purge the queue.'); + var url = queuePath + queueName + '/purge'; + var form = resourceTypes; + return apiService.post(url, form).error(error(msg)); + } + function getSubscriptions(queue) { var url = interpolate(subPath, [queue.name]); return apiService.get(url); diff --git a/zaqar_ui/static/dashboard/project/queues/actions/actions.module.js b/zaqar_ui/static/dashboard/project/queues/actions/actions.module.js index 82c58a3..ad6d071 100644 --- a/zaqar_ui/static/dashboard/project/queues/actions/actions.module.js +++ b/zaqar_ui/static/dashboard/project/queues/actions/actions.module.js @@ -32,6 +32,7 @@ 'horizon.dashboard.project.queues.actions.createQueueService', 'horizon.dashboard.project.queues.actions.deleteQueueService', 'horizon.dashboard.project.queues.actions.updateQueueService', + 'horizon.dashboard.project.queues.actions.purgeQueueService', 'horizon.dashboard.project.queues.actions.createSubscriptionService', 'horizon.dashboard.project.queues.resourceType' ]; @@ -41,6 +42,7 @@ createQueueService, deleteQueueService, updateQueueService, + purgeQueueService, createSubscriptionService, resourceType ) { @@ -54,6 +56,13 @@ text: gettext('Update') } }) + .append({ + id: 'queuesItemPurge', + service: purgeQueueService, + template: { + text: gettext('Purge') + } + }) .append({ id: 'subscriptionsCreate', service: createSubscriptionService, diff --git a/zaqar_ui/static/dashboard/project/queues/actions/purge-queue.service.js b/zaqar_ui/static/dashboard/project/queues/actions/purge-queue.service.js new file mode 100644 index 0000000..a9f0234 --- /dev/null +++ b/zaqar_ui/static/dashboard/project/queues/actions/purge-queue.service.js @@ -0,0 +1,147 @@ +/** + * 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.project.queues') + .factory( + 'horizon.dashboard.project.queues.actions.purgeQueueService', purgeQueueService); + + purgeQueueService.$inject = [ + '$q', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.app.core.openstack-service-api.zaqar', + 'horizon.dashboard.project.queues.events', + '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.project.queues.actions.purgeQueueService + * @param {Object} $q + * @param {Object} policy + * @param {Object} zaqar + * @param {Object} events + * @param {Object} gettext + * @param {Object} $qExtensions + * @param {Object} modal + * @param {Object} toast + * @returns {Object} purge queue service + * @description Brings up the purge queues choices modal dialog. + * On submit, purge given queues. + * On cancel, do nothing. + */ + function purgeQueueService( + $q, policy, zaqar, events, gettext, $qExtensions, modal, toast + ) { + // schema + var schema = { + type: "object", + properties: { + resource_types: { + title: gettext("Choose resource to purge"), + type: "string", + enum: ["messages", "subscriptions", "all"] + } + } + }; + + // form + var form = [ + { + type: 'section', + htmlClass: 'row', + items: [ + { + type: 'section', + htmlClass: 'col-sm-12', + items: [ + { + key: 'resource_types', + type: 'radiobuttons', + titleMap: [ + {value: 'messages', name: gettext('Messages')}, + {value: 'subscriptions', name: gettext('Subscriptions')}, + {value: "all", name: "All"} + ], + required:true + } + ] + } + ] + } + ]; + + var scope, model; + + var message = { + success: gettext('Queue %s has been purged successfully.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + function initAction() { + } + + function allowed() { + return $qExtensions.booleanAsPromise(true); + } + + function perform(selected, $scope) { + scope = $scope; + + model = { + id: selected.id, + name: selected.name, + resource_types: [] + }; + // modal config + var config = { + "title": gettext('Purge Queue'), + "submitText": gettext('Purge'), + "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; + context.model.resource_types = (context.model.resource_types === "all") + ? [] : [context.model.resource_types]; + + return zaqar.purgeQueue(id, context.model).then(function() { + toast.add('success', interpolate(message.success, [name])); + scope.$emit(events.PURGE_SUCCESS, name); + }); + } + } +})(); diff --git a/zaqar_ui/static/dashboard/project/queues/queues.module.js b/zaqar_ui/static/dashboard/project/queues/queues.module.js index fcf70a4..fee9c0b 100644 --- a/zaqar_ui/static/dashboard/project/queues/queues.module.js +++ b/zaqar_ui/static/dashboard/project/queues/queues.module.js @@ -48,6 +48,7 @@ METADATA_CHANGED: 'horizon.dashboard.project.queues.METADATA_CHANGED', DELETE_SUCCESS: 'horizon.dashboard.project.queues.DELETE_SUCCESS', UPDATE_SUCCESS: 'horizon.dashboard.project.queues.UPDATE_SUCCESS', + PURGE_SUCCESS: 'horizon.dashboard.project.queues.PURGE_SUCCESS', SUBSCRIPTION_CREATE_SUCCESS: 'horizon.dashboard.project.queues.SUBSCRIPTION_CREATE_SUCCESS' }; } diff --git a/zaqar_ui/static/dashboard/project/queues/table/queue.controller.js b/zaqar_ui/static/dashboard/project/queues/table/queue.controller.js index 670eac9..dab5c49 100644 --- a/zaqar_ui/static/dashboard/project/queues/table/queue.controller.js +++ b/zaqar_ui/static/dashboard/project/queues/table/queue.controller.js @@ -54,11 +54,13 @@ var createWatcher = $scope.$on(events.CREATE_SUCCESS, onCreateSuccess); var deleteWatcher = $scope.$on(events.DELETE_SUCCESS, onDeleteSuccess); var updateWatcher = $scope.$on(events.UPDATE_SUCCESS, onUpdateSuccess); + var purgeWatcher = $scope.$on(events.PURGE_SUCCESS, onPurgeSuccess); var subWatcher = $scope.$on(events.SUBSCRIPTION_CREATE_SUCCESS, broadcastEvents); $scope.$on('$destroy', function destroy() { createWatcher(); deleteWatcher(); updateWatcher(); + purgeWatcher(); subWatcher(); }); } @@ -85,6 +87,32 @@ }); } + function refreshQueue(queueName) { + zaqar.getQueue(queueName).then(function(response) { + response.data.id = queueName; + for (var i = 0; i < ctrl.queuesSrc.length; i++) { + var queue = ctrl.queuesSrc[i]; + if (queue.id === queueName) { + ctrl.queuesSrc[i] = response.data; + } + } + for (var i = 0; i < ctrl.queues.length; i++) { + var queue = ctrl.queues[i]; + if (queue.id === queueName) { + ctrl.queues[i] = response.data; + } + } + }); + } + + function refreshSubscriptions(queueName) { + var queue = new Object(); + queue.name = queueName; + zaqar.getSubscriptions(queue).then(function() { + $scope.tCtrl.broadcastExpansion(queue); + }); + } + function onCreateSuccess(e, newQueue) { e.stopPropagation(); newQueue.id = newQueue.name; @@ -110,6 +138,14 @@ // update queue ctrl.queuesSrc[queue.id] = queue; } + + function onPurgeSuccess(e, queueName) { + e.stopPropagation(); + // purge queue + refreshQueue(queueName); + refreshSubscriptions(queueName); + } + } })();