Merge "Add CRUD actions for pool"

This commit is contained in:
Jenkins 2017-02-01 08:26:26 +00:00 committed by Gerrit Code Review
commit 5afea7e101
10 changed files with 566 additions and 4 deletions

View File

@ -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.

View File

@ -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'
}
});
}
})();

View File

@ -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;
}
}
})();

View File

@ -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;
}
}
})();

View File

@ -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;
}
}
})();

View File

@ -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;
}
})();

View File

@ -1,5 +1,5 @@
<hz-resource-property-list
resource-type-name="OS::Zaqar::Pools"
item="item"
property-groups="[['uri'], ['options']]">
property-groups="[['uri', 'options']]">
</hz-resource-property-list>

View File

@ -1,4 +1,4 @@
<hz-resource-panel resource-type-name="OS::Zaqar::Pools">
<hz-resource-table resource-type-name="OS::Zaqar::Pools" track-by="name">
<hz-resource-table resource-type-name="OS::Zaqar::Pools" track-by="trackBy">
</hz-resource-table>
</hz-resource-panel>

View File

@ -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)

View File

@ -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;
}
}
}
})();