Add CRUD actions for pool flavor

This patch adds CRUD actions for pool flavor into pool flavors panel.

Change-Id: I0d5f0fb490d553b3e64d0e278ad1356900b81999
Implements: blueprint support-pool-flavor
This commit is contained in:
Shu Muto 2016-12-16 13:48:33 +09:00
parent b4256ff5c2
commit aa2486bde6
11 changed files with 550 additions and 3 deletions

View File

@ -0,0 +1,8 @@
---
features:
- >
Storage pool flavors 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

@ -18,5 +18,6 @@ PANEL_DASHBOARD = 'admin'
ADD_PANEL = ('zaqar_ui.content.pool-flavors.panel.PoolFlavors')
ADD_ANGULAR_MODULES = ['horizon.dashboard.admin.pool-flavors']
ADD_SCSS_FILES = ['dashboard/admin/pool-flavors/pool-flavors.scss']
AUTO_DISCOVER_STATIC_FILES = True

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.pool-flavors.actions
*
* @description
* Provides all of the actions for pool flavors.
*/
angular.module('horizon.dashboard.admin.pool-flavors.actions', [
'horizon.framework.conf',
'horizon.dashboard.admin.pool-flavors'
])
.run(registerPoolFlavorActions);
registerPoolFlavorActions.$inject = [
'horizon.framework.conf.resource-type-registry.service',
'horizon.dashboard.admin.pool-flavors.actions.create.service',
'horizon.dashboard.admin.pool-flavors.actions.delete.service',
'horizon.dashboard.admin.pool-flavors.actions.update.service',
'horizon.dashboard.admin.pool-flavors.resourceType'
];
function registerPoolFlavorActions(
registry,
createPoolFlavorService,
deletePoolFlavorService,
updatePoolFlavorService,
flavorResourceType
) {
var resourceType = registry.getResourceType(flavorResourceType);
resourceType.globalActions
.append({
id: 'createPoolFlavorAction',
service: createPoolFlavorService,
template: {
text: gettext('Create Pool Flavor'),
type: 'create'
}
});
resourceType.batchActions
.append({
id: 'batchDeletePoolFlavorAction',
service: deletePoolFlavorService,
template: {
type: 'delete-selected',
text: gettext('Delete Pool Flavors')
}
});
resourceType.itemActions
.append({
id: 'updatePoolFlavorAction',
service: updatePoolFlavorService,
template: {
text: gettext('Update Pool Flavor'),
type: 'row'
}
})
.append({
id: 'deletePoolFlavorAction',
service: deletePoolFlavorService,
template: {
text: gettext('Delete Pool Flavor'),
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.pool-flavors.actions.create.service
* @description
* Service for the pool flavor create modal
*/
angular
.module('horizon.dashboard.admin.pool-flavors.actions')
.factory('horizon.dashboard.admin.pool-flavors.actions.create.service', createService);
createService.$inject = [
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.openstack-service-api.zaqar',
'horizon.dashboard.admin.pool-flavors.actions.workflow',
'horizon.dashboard.admin.pool-flavors.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 createService(
policy, zaqar, workflow, resourceType,
actionResult, gettext, $qExtensions, modal, toast
) {
var message = {
success: gettext('Pool flavor %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 Flavor');
submitText = gettext('Create');
var config = workflow.init('create', title, submitText);
return modal.open(config).then(submit);
}
function allowed() {
return policy.ifAllowed({ rules: [['pool_flavor', 'add_flavor']] });
}
function submit(context) {
return zaqar.createFlavor(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.pool-flavors.actions.delete.service
* @Description
* Brings up the delete pool flavors confirmation modal dialog.
* On submit, delete given pool flavors.
* On cancel, do nothing.
*/
angular
.module('horizon.dashboard.admin.pool-flavors.actions')
.factory('horizon.dashboard.admin.pool-flavors.actions.delete.service', deleteService);
deleteService.$inject = [
'$q',
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.openstack-service-api.zaqar',
'horizon.dashboard.admin.pool-flavors.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 pool flavors: %s");
var service = {
initAction: initAction,
allowed: allowed,
perform: perform
};
return service;
//////////////
function initAction() {
context = { };
}
function perform(items, newScope) {
scope = newScope;
var flavors = angular.isArray(items) ? items : [items];
context.labels = labelize(flavors.length);
context.deleteEntity = deleteFlavor;
return $qExtensions.allSettled(flavors.map(checkPermission)).then(afterCheck);
}
function allowed() {
return policy.ifAllowed({ rules: [['pool_flavor', 'delete_flavor']] });
}
function checkPermission(flavor) {
return {promise: allowed(), context: flavor};
}
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 Flavor',
'Confirm Delete Pool Flavors', count),
message: ngettext(
'You have selected "%s". Deleted Pool Flavor is not recoverable.',
'You have selected "%s". Deleted Pool Flavors are not recoverable.', count),
submit: ngettext(
'Delete Pool Flavor',
'Delete Pool Flavors', count),
success: ngettext(
'Deleted Pool Flavor: %s.',
'Deleted Pool Flavors: %s.', count),
error: ngettext(
'Unable to delete Pool Flavor: %s.',
'Unable to delete Pool Flavors: %s.', count)
};
}
function deleteFlavor(flavor) {
return zaqar.deleteFlavor(flavor, 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,93 @@
/**
* 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.pool-flavors.actions.update.service
* @description
* Service for the pool flavor update modal
*/
angular
.module('horizon.dashboard.admin.pool-flavors.actions')
.factory('horizon.dashboard.admin.pool-flavors.actions.update.service', updateService);
updateService.$inject = [
'horizon.app.core.openstack-service-api.policy',
'horizon.app.core.openstack-service-api.zaqar',
'horizon.dashboard.admin.pool-flavors.actions.workflow',
'horizon.dashboard.admin.pool-flavors.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 flavor %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 Flavor');
submitText = gettext('Update');
var config = workflow.init('update', title, submitText);
// load current data
zaqar.getFlavor(selected.name).then(onLoad);
function onLoad(response) {
config.model.name = response.data.name;
config.model.pool_group = response.data.pool_group;
config.model.capabilities = response.data.capabilities;
}
return modal.open(config).then(submit);
}
function allowed() {
return policy.ifAllowed({ rules: [['pool_flavor', 'update_flavor']] });
}
function submit(context) {
return zaqar.updateFlavor(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,120 @@
/**
* 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.pool-flavors.actions.workflow
* @description
* Workflow for creating/updating storage pool flavor
*/
angular
.module('horizon.dashboard.admin.pool-flavors.actions')
.factory('horizon.dashboard.admin.pool-flavors.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 capabilitiesPlaceholder = gettext(
'Describes flavor-specific capabilities in YAML format.');
// schema
schema = {
type: 'object',
properties: {
name: {
title: gettext('Name'),
type: 'string'
},
pool_group: {
title: gettext('Pool Group'),
type: 'string'
},
capabilities: {
title: gettext('Capabilities'),
type: 'string'
}
}
};
// form
form = [
{
type: 'section',
htmlClass: 'row',
items: [
{
type: 'section',
htmlClass: 'col-sm-6',
items: [
{
key: 'name',
placeholder: gettext('Name of the flavor.'),
required: true,
"readonly": actionType === 'update'
},
{
key: 'pool_group',
placeholder: gettext('Pool group for flavor.'),
/* eslint-disable max-len */
description: gettext('You must specify one of the pool groups that is configured in storage pools.'),
required: true
}
]
},
{
type: 'section',
htmlClass: 'col-sm-6',
items: [
{
key: 'capabilities',
type: 'textarea',
placeholder: capabilitiesPlaceholder
}
]
}
]
}
]; // form
model = {
name: '',
pool_group: '',
capabilities: ''
};
var config = {
title: title,
submitText: submitText,
schema: schema,
form: form,
model: model
};
return config;
}
return workflow;
}
})();

View File

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

View File

@ -22,7 +22,8 @@
angular
.module('horizon.dashboard.admin.pool-flavors', [
'ngRoute'
'ngRoute',
'horizon.dashboard.admin.pool-flavors.actions'
])
.constant('horizon.dashboard.admin.pool-flavors.resourceType', 'OS::Zaqar::Flavors')
.run(run)

View File

@ -0,0 +1,4 @@
textarea#capabilities {
height: 10em;
}

View File

@ -43,7 +43,19 @@
* flavors. This is used in displaying lists of Pool Flavors.
*/
function getFlavorsPromise(params) {
return zaqar.getFlavors(params);
return zaqar.getFlavors(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;
}
}
}
})();