From ecb417864b5d0ecc8fdca0428ef73e31818f17c9 Mon Sep 17 00:00:00 2001 From: Shu Muto Date: Thu, 15 Dec 2016 13:11:30 +0900 Subject: [PATCH] Add CRUD actions for pool This patch adds CRUD actions for storage pool into pools panel. Change-Id: I900bc82b24fd30f98027c82820940f35cd4e6ad6 Implements: blueprint support-pool --- .../notes/pool-panel-020bf94bc34b4cd8.yaml | 8 + .../admin/pools/actions/actions.module.js | 86 +++++++++++ .../admin/pools/actions/create.service.js | 84 +++++++++++ .../admin/pools/actions/delete.service.js | 138 ++++++++++++++++++ .../admin/pools/actions/update.service.js | 95 ++++++++++++ .../admin/pools/actions/workflow.service.js | 138 ++++++++++++++++++ .../static/dashboard/admin/pools/drawer.html | 2 +- .../static/dashboard/admin/pools/panel.html | 2 +- .../dashboard/admin/pools/pools.module.js | 3 +- .../dashboard/admin/pools/pools.service.js | 14 +- 10 files changed, 566 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/pool-panel-020bf94bc34b4cd8.yaml create mode 100644 zaqar_ui/static/dashboard/admin/pools/actions/actions.module.js create mode 100644 zaqar_ui/static/dashboard/admin/pools/actions/create.service.js create mode 100644 zaqar_ui/static/dashboard/admin/pools/actions/delete.service.js create mode 100644 zaqar_ui/static/dashboard/admin/pools/actions/update.service.js create mode 100644 zaqar_ui/static/dashboard/admin/pools/actions/workflow.service.js diff --git a/releasenotes/notes/pool-panel-020bf94bc34b4cd8.yaml b/releasenotes/notes/pool-panel-020bf94bc34b4cd8.yaml new file mode 100644 index 0000000..1a56c5b --- /dev/null +++ b/releasenotes/notes/pool-panel-020bf94bc34b4cd8.yaml @@ -0,0 +1,8 @@ +--- +features: + - > + Storage pools management panel is added. This panel is + added into Admin dashboard. Also create, update and + delete actions are implemeted. Create action is + implemented as globalAction, so it is callable from + other panels. diff --git a/zaqar_ui/static/dashboard/admin/pools/actions/actions.module.js b/zaqar_ui/static/dashboard/admin/pools/actions/actions.module.js new file mode 100644 index 0000000..af12e76 --- /dev/null +++ b/zaqar_ui/static/dashboard/admin/pools/actions/actions.module.js @@ -0,0 +1,86 @@ +/* + * 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 + * @ngname horizon.dashboard.admin.pools.actions + * + * @description + * Provides all of the actions for pools. + */ + angular.module('horizon.dashboard.admin.pools.actions', [ + 'horizon.framework.conf', + 'horizon.dashboard.admin.pools' + ]) + .run(registerPoolActions); + + registerPoolActions.$inject = [ + 'horizon.framework.conf.resource-type-registry.service', + 'horizon.dashboard.admin.pools.actions.create.service', + 'horizon.dashboard.admin.pools.actions.delete.service', + 'horizon.dashboard.admin.pools.actions.update.service', + 'horizon.dashboard.admin.pools.resourceType' + ]; + + function registerPoolActions( + registry, + createPoolService, + deletePoolService, + updatePoolService, + poolResourceType + ) { + var resourceType = registry.getResourceType(poolResourceType); + + resourceType.globalActions + .append({ + id: 'createPoolAction', + service: createPoolService, + template: { + text: gettext('Create Pool'), + type: 'create' + } + }); + + resourceType.batchActions + .append({ + id: 'batchDeletePoolAction', + service: deletePoolService, + template: { + type: 'delete-selected', + text: gettext('Delete Pools') + } + }); + + resourceType.itemActions + .append({ + id: 'updatePoolAction', + service: updatePoolService, + template: { + text: gettext('Update Pool'), + type: 'row' + } + }) + .append({ + id: 'deletePoolAction', + service: deletePoolService, + template: { + text: gettext('Delete Pool'), + type: 'delete' + } + }); + } +})(); diff --git a/zaqar_ui/static/dashboard/admin/pools/actions/create.service.js b/zaqar_ui/static/dashboard/admin/pools/actions/create.service.js new file mode 100644 index 0000000..60c900c --- /dev/null +++ b/zaqar_ui/static/dashboard/admin/pools/actions/create.service.js @@ -0,0 +1,84 @@ +/** + * 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 factory + * @name horizon.dashboard.admin.pools.actions.create.service + * @description + * Service for the storage pool create modal + */ + angular + .module('horizon.dashboard.admin.pools.actions') + .factory('horizon.dashboard.admin.pools.actions.create.service', createPoolService); + + createPoolService.$inject = [ + 'horizon.app.core.openstack-service-api.policy', + 'horizon.app.core.openstack-service-api.zaqar', + 'horizon.dashboard.admin.pools.actions.workflow', + 'horizon.dashboard.admin.pools.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 createPoolService( + policy, zaqar, workflow, resourceType, + actionResult, gettext, $qExtensions, modal, toast + ) { + + var message = { + success: gettext('Pool %s was successfully created.') + }; + + var service = { + initAction: initAction, + perform: perform, + allowed: allowed + }; + + return service; + + ////////////// + + function initAction() { + } + + function perform() { + var title, submitText; + title = gettext('Create Pool'); + submitText = gettext('Create'); + var config = workflow.init('create', title, submitText); + return modal.open(config).then(submit); + } + + function allowed() { + return policy.ifAllowed({ rules: [['pool', 'add_pool']] }); + } + + function submit(context) { + return zaqar.createPool(context.model, true).then(success, true); + } + + function success(response) { + toast.add('success', interpolate(message.success, [response.data.id])); + var result = actionResult.getActionResult().created(resourceType, response.data.name); + return result.result; + } + } +})(); diff --git a/zaqar_ui/static/dashboard/admin/pools/actions/delete.service.js b/zaqar_ui/static/dashboard/admin/pools/actions/delete.service.js new file mode 100644 index 0000000..bb25b8d --- /dev/null +++ b/zaqar_ui/static/dashboard/admin/pools/actions/delete.service.js @@ -0,0 +1,138 @@ +/** + * 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.dashboard.admin.pools.actions.delete.service + * @Description + * Brings up the delete pools confirmation modal dialog. + * On submit, delete given pools. + * On cancel, do nothing. + */ + angular + .module('horizon.dashboard.admin.pools.actions') + .factory('horizon.dashboard.admin.pools.actions.delete.service', deleteService); + + deleteService.$inject = [ + '$q', + 'horizon.app.core.openstack-service-api.policy', + 'horizon.app.core.openstack-service-api.zaqar', + 'horizon.dashboard.admin.pools.resourceType', + 'horizon.framework.util.actions.action-result.service', + 'horizon.framework.util.i18n.gettext', + 'horizon.framework.util.q.extensions', + 'horizon.framework.widgets.modal.deleteModalService', + 'horizon.framework.widgets.toast.service' + ]; + + function deleteService( + $q, policy, zaqar, resourceType, actionResult, gettext, $qExtensions, + deleteModal, toast + ) { + var scope, context; + var notAllowedMessage = gettext("You are not allowed to delete pools: %s"); + + var service = { + initAction: initAction, + allowed: allowed, + perform: perform + }; + + return service; + + ////////////// + + function initAction() { + context = { }; + } + + function perform(items, newScope) { + scope = newScope; + var pools = angular.isArray(items) ? items : [items]; + context.labels = labelize(pools.length); + context.deleteEntity = deletePool; + return $qExtensions.allSettled(pools.map(checkPermission)).then(afterCheck); + } + + function allowed() { + return policy.ifAllowed({ rules: [['pool', 'delete_pool']] }); + } + + function checkPermission(pool) { + return {promise: allowed(), context: pool}; + } + + 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) { + var result = actionResult.getActionResult(); + deleteModalResult.pass.forEach(function markDeleted(item) { + result.deleted(resourceType, getEntity(item).name); + }); + deleteModalResult.fail.forEach(function markFailed(item) { + result.failed(resourceType, getEntity(item).name); + }); + return result.result; + } + + function labelize(count) { + return { + title: ngettext( + 'Confirm Delete Pool', + 'Confirm Delete Pools', count), + message: ngettext( + 'You have selected "%s". Deleted Pool is not recoverable.', + 'You have selected "%s". Deleted Pools are not recoverable.', count), + submit: ngettext( + 'Delete Pool', + 'Delete Pools', count), + success: ngettext( + 'Deleted Pool: %s.', + 'Deleted Pools: %s.', count), + error: ngettext( + 'Unable to delete Pool: %s.', + 'Unable to delete Pools: %s.', count) + }; + } + + function deletePool(pool) { + return zaqar.deletePool(pool, true); + } + + function getMessage(message, entities) { + return interpolate(message, [entities.map(getName).join(", ")]); + } + + function getName(result) { + return getEntity(result).name; + } + + function getEntity(result) { + return result.context; + } + } +})(); diff --git a/zaqar_ui/static/dashboard/admin/pools/actions/update.service.js b/zaqar_ui/static/dashboard/admin/pools/actions/update.service.js new file mode 100644 index 0000000..68bb264 --- /dev/null +++ b/zaqar_ui/static/dashboard/admin/pools/actions/update.service.js @@ -0,0 +1,95 @@ +/** + * 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 factory + * @name horizon.dashboard.admin.pools.actions.update.service + * @description + * Service for the storage pool update modal + */ + angular + .module('horizon.dashboard.admin.pools.actions') + .factory('horizon.dashboard.admin.pools.actions.update.service', updateService); + + updateService.$inject = [ + 'horizon.app.core.openstack-service-api.policy', + 'horizon.app.core.openstack-service-api.zaqar', + 'horizon.dashboard.admin.pools.actions.workflow', + 'horizon.dashboard.admin.pools.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 updateService( + policy, zaqar, workflow, resourceType, + actionResult, gettext, $qExtensions, modal, toast + ) { + + var message = { + success: gettext('Pool %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 Pool'); + submitText = gettext('Update'); + var config = workflow.init('update', title, submitText); + + // load current data + zaqar.getPool(selected.name).then(onLoad); + function onLoad(response) { + config.model.name = response.data.name; + config.model.group = response.data.group; + config.model.weight = response.data.weight; + config.model.uri = response.data.uri; + config.model.options = response.data.options; + } + + return modal.open(config).then(submit); + } + + function allowed() { + return policy.ifAllowed({ rules: [['pool', 'update_pool']] }); + } + + function submit(context) { + return zaqar.updatePool(context.model, true).then(success, true); + } + + function success(response) { + toast.add('success', interpolate(message.success, [response.data.name])); + var result = actionResult.getActionResult().updated(resourceType, response.data.name); + return result.result; + } + } +})(); diff --git a/zaqar_ui/static/dashboard/admin/pools/actions/workflow.service.js b/zaqar_ui/static/dashboard/admin/pools/actions/workflow.service.js new file mode 100644 index 0000000..daa8e86 --- /dev/null +++ b/zaqar_ui/static/dashboard/admin/pools/actions/workflow.service.js @@ -0,0 +1,138 @@ +/** + * 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 factory + * @name horizon.dashboard.admin.pools.actions.workflow + * @description + * Workflow for creating/updating storage pool + */ + angular + .module('horizon.dashboard.admin.pools.actions') + .factory('horizon.dashboard.admin.pools.actions.workflow', workflow); + + workflow.$inject = [ + 'horizon.framework.util.i18n.gettext' + ]; + + function workflow(gettext) { + var workflow = { + init: init + }; + + function init(actionType, title, submitText) { + var schema, form, model; + var optionsPlaceholder = gettext( + 'An optional request component related to storage-specific options in YAML format.'); + + // schema + schema = { + type: 'object', + properties: { + name: { + title: gettext('Name'), + type: 'string' + }, + group: { + title: gettext('Group'), + type: 'string' + }, + weight: { + title: gettext('Weight'), + type: 'number' + }, + uri: { + title: gettext('URI'), + type: 'string' + }, + options: { + title: gettext('Options'), + type: 'string' + } + } + }; + + // form + form = [ + { + type: 'section', + htmlClass: 'row', + items: [ + { + type: 'section', + htmlClass: 'col-sm-6', + items: [ + { + key: 'name', + placeholder: gettext('Name of the pool.'), + required: true, + "readonly": actionType === 'update' + }, + { + key: 'weight', + placeholder: gettext('Weight of the pool.'), + required: true + }, + { + key: 'uri', + placeholder: gettext('URI for storage engine of this pool.'), + description: gettext('e.g. mongodb://127.0.0.1:27017'), + required: true + } + ] + }, + { + type: 'section', + htmlClass: 'col-sm-6', + items: [ + { + key: 'group', + placeholder: gettext('Group of the pool.') + }, + { + key: 'options', + type: 'textarea', + placeholder: optionsPlaceholder + } + ] + } + ] + } + ]; // form + + model = { + name: '', + group: '', + weight: 0, + uri: '', + options: '' + }; + + var config = { + title: title, + submitText: submitText, + schema: schema, + form: form, + model: model + }; + + return config; + } + + return workflow; + } +})(); diff --git a/zaqar_ui/static/dashboard/admin/pools/drawer.html b/zaqar_ui/static/dashboard/admin/pools/drawer.html index fe6617c..55192d1 100644 --- a/zaqar_ui/static/dashboard/admin/pools/drawer.html +++ b/zaqar_ui/static/dashboard/admin/pools/drawer.html @@ -1,5 +1,5 @@ + property-groups="[['uri', 'options']]"> diff --git a/zaqar_ui/static/dashboard/admin/pools/panel.html b/zaqar_ui/static/dashboard/admin/pools/panel.html index c3c2528..ca0a5d5 100644 --- a/zaqar_ui/static/dashboard/admin/pools/panel.html +++ b/zaqar_ui/static/dashboard/admin/pools/panel.html @@ -1,4 +1,4 @@ - + diff --git a/zaqar_ui/static/dashboard/admin/pools/pools.module.js b/zaqar_ui/static/dashboard/admin/pools/pools.module.js index 3045d58..558e6e8 100644 --- a/zaqar_ui/static/dashboard/admin/pools/pools.module.js +++ b/zaqar_ui/static/dashboard/admin/pools/pools.module.js @@ -22,7 +22,8 @@ angular .module('horizon.dashboard.admin.pools', [ - 'ngRoute' + 'ngRoute', + 'horizon.dashboard.admin.pools.actions' ]) .constant('horizon.dashboard.admin.pools.resourceType', 'OS::Zaqar::Pools') .run(run) diff --git a/zaqar_ui/static/dashboard/admin/pools/pools.service.js b/zaqar_ui/static/dashboard/admin/pools/pools.service.js index 32dfaf7..914dcfa 100644 --- a/zaqar_ui/static/dashboard/admin/pools/pools.service.js +++ b/zaqar_ui/static/dashboard/admin/pools/pools.service.js @@ -43,7 +43,19 @@ * pools. This is used in displaying lists of Pools. */ function getPoolsPromise(params) { - return zaqar.getPools(params); + return zaqar.getPools(params).then(modifyResponse); + } + + function modifyResponse(response) { + return {data: {items: response.data.items.map(modifyItem)}}; + + function modifyItem(item) { + // we should set 'trackBy' as follows ideally. + // item.trackBy = item.id + item.updated_at; + var timestamp = new Date(); + item.trackBy = item.name.concat(timestamp.getTime()); + return item; + } } } })();