From 61b638e722ff2e6aa47596ddd8e4ea312af4e9fa Mon Sep 17 00:00:00 2001 From: Shu Muto Date: Wed, 14 Dec 2016 15:58:36 +0900 Subject: [PATCH] Add REST APIs for pools and flavors This patch adds REST APIs for storage pools and pool flavors. And this includes AngularJS services for these REST APIs. Change-Id: Ideb3847f6192afb9c99891f8bf4d68da11988214 Partial-Implements: blueprint support-pool Partial-Implements: blueprint support-pool-flavor --- zaqar_ui/api/rest/zaqar.py | 192 +++++++++++++++++- zaqar_ui/api/zaqar.py | 52 +++++ .../openstack-service-api/zaqar.service.js | 66 +++++- 3 files changed, 306 insertions(+), 4 deletions(-) diff --git a/zaqar_ui/api/rest/zaqar.py b/zaqar_ui/api/rest/zaqar.py index c71cc26..816d523 100644 --- a/zaqar_ui/api/rest/zaqar.py +++ b/zaqar_ui/api/rest/zaqar.py @@ -12,12 +12,34 @@ # License for the specific language governing permissions and limitations # under the License. +import six +import yaml + from django.views import generic - -from zaqar_ui.api import zaqar - from openstack_dashboard.api.rest import urls from openstack_dashboard.api.rest import utils as rest_utils +from zaqar_ui.api import zaqar + + +def _convert_to_yaml(data, default_flow_style=False): + if not data: + return '' + try: + return yaml.safe_dump(data, default_flow_style=default_flow_style) + except Exception: + return '' + + +def _load_yaml(data): + if not data: + loaded_data = {} + else: + try: + loaded_data = yaml.load(data) + except Exception as ex: + raise Exception(_('The specified input is not a valid ' + 'YAML format: %s') % six.text_type(ex)) + return loaded_data @urls.register @@ -128,3 +150,167 @@ class Subscription(generic.View): def post(self, request, queue_name, subscriber): zaqar.subscription_update(request, queue_name, {'id': subscriber}, request.DATA) + + +@urls.register +class Pool(generic.View): + """API for retrieving a single pool""" + url_regex = r'zaqar/pools/(?P[^/]+)$' + + @rest_utils.ajax() + def get(self, request, pool_name): + """Get a specific pool""" + pool = zaqar.pool_get(request, pool_name) + pool['id'] = pool.get('name') + pool['options'] = _convert_to_yaml(pool.get('options')) + return pool + + @rest_utils.ajax(data_required=True) + def post(self, request, pool_name): + """Update a pool. + + Returns the updated pool object on success. + """ + request.DATA["options"] = _load_yaml(request.DATA.get("options")) + params = request.DATA + pool_name = params.pop('name') + new_pool = zaqar.pool_update(request, pool_name, params) + location = '/api/zaqar/pools/%s' % new_pool.name + response = {'name': new_pool.name, + 'uri': new_pool.uri, + 'weight': new_pool.weight, + 'group': new_pool.group, + 'options': new_pool.options} + return rest_utils.CreatedResponse(location, response) + + +@urls.register +class Pools(generic.View): + """API for pools""" + url_regex = r'zaqar/pools/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of the Pools for admin. + + The returned result is an object with property 'items' and each + item under this is a pool. + """ + result = zaqar.pool_list(request) + pools = [] + for p in result: + options = _convert_to_yaml(p.options) + pools.append({'id': p.name, + 'name': p.name, + 'uri': p.uri, + 'weight': p.weight, + 'group': p.group, + 'options': options}) + return {'items': pools} + + @rest_utils.ajax(data_required=True) + def delete(self, request): + """Delete one or more pool by name. + + Returns HTTP 204 (no content) on successful deletion. + """ + for pool_name in request.DATA: + zaqar.pool_delete(request, pool_name) + + @rest_utils.ajax(data_required=True) + def put(self, request): + """Create a new pool. + + Returns the new pool object on success. + """ + request.DATA['options'] = _load_yaml(request.DATA.get('options')) + params = request.DATA + pool_name = params.pop('name') + new_pool = zaqar.pool_create(request, pool_name, params) + location = '/api/zaqar/pools/%s' % new_pool.name + response = {'name': new_pool.name, + 'uri': new_pool.uri, + 'weight': new_pool.weight, + 'group': new_pool.group, + 'options': new_pool.options} + return rest_utils.CreatedResponse(location, response) + + +@urls.register +class Flavor(generic.View): + """API for retrieving a single flavor""" + url_regex = r'zaqar/flavors/(?P[^/]+)$' + + @rest_utils.ajax() + def get(self, request, flavor_name): + """Get a specific flavor""" + flavor = zaqar.flavor_get(request, flavor_name) + flavor['id'] = flavor.get('name') + flavor['capabilities'] = _convert_to_yaml(flavor.get('capabilities')) + return flavor + + @rest_utils.ajax(data_required=True) + def post(self, request, flavor_name): + """Update a flavor. + + Returns the updated flavor object on success. + """ + capabilities = request.DATA.get('capabilities') + request.DATA['capabilities'] = _load_yaml(capabilities) + params = request.DATA + flavor_name = params.pop('name') + new_flavor = zaqar.flavor_update(request, flavor_name, params) + location = '/api/zaqar/flavors/%s' % new_flavor.name + response = {'name': new_flavor.name, + 'pool_group': new_flavor.pool_group, + 'capabilities': new_flavor.capabilities} + return rest_utils.CreatedResponse(location, response) + + +@urls.register +class Flavors(generic.View): + """API for flavors""" + url_regex = r'zaqar/flavors/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of the Flavors for admin. + + The returned result is an object with property 'items' and each + item under this is a flavor. + """ + result = zaqar.flavor_list(request) + flavors = [] + for f in result: + capabilities = _convert_to_yaml(f.capabilities) + flavors.append({'id': f.name, + 'name': f.name, + 'pool_group': f.pool_group, + 'capabilities': capabilities}) + return {'items': flavors} + + @rest_utils.ajax(data_required=True) + def delete(self, request): + """Delete one or more flavor by name. + + Returns HTTP 204 (no content) on successful deletion. + """ + for flavor_name in request.DATA: + zaqar.flavor_delete(request, flavor_name) + + @rest_utils.ajax(data_required=True) + def put(self, request): + """Create a new flavor. + + Returns the new flavor object on success. + """ + capabilities = request.DATA.get('capabilities') + request.DATA['capabilities'] = _load_yaml(capabilities) + params = request.DATA + flavor_name = params.pop('name') + new_flavor = zaqar.flavor_create(request, flavor_name, params) + location = '/api/zaqar/flavors/%s' % new_flavor.name + response = {'name': new_flavor.name, + 'pool_group': new_flavor.pool_group, + 'capabilities': new_flavor.capabilities} + return rest_utils.CreatedResponse(location, response) diff --git a/zaqar_ui/api/zaqar.py b/zaqar_ui/api/zaqar.py index 4d79b6f..5fa430a 100644 --- a/zaqar_ui/api/zaqar.py +++ b/zaqar_ui/api/zaqar.py @@ -108,3 +108,55 @@ def subscription_update(request, queue_name, old_data, new_data): subscription = zaqarclient(request).subscription(queue_name, **old_data) subscription.update(new_data) return subscription + + +def pool_list(request, limit=None, marker=None): + return zaqarclient(request).pools(limit=limit, + marker=marker, + detailed=True) + + +def pool_create(request, pool_name, params): + pool = zaqarclient(request).pool(pool_name, **params) + return pool + + +def pool_delete(request, pool_name): + pool = zaqarclient(request).pool(pool_name, auto_create=False) + pool.delete() + + +def pool_update(request, pool_name, params): + pool = zaqarclient(request).pool(pool_name, auto_create=False) + pool.update(params) + return pool + + +def pool_get(request, pool_name): + return zaqarclient(request).pool(pool_name, auto_create=False).get() + + +def flavor_list(request, limit=None, marker=None): + return zaqarclient(request).flavors(limit=limit, + marker=marker, + detailed=True) + + +def flavor_create(request, flavor_name, params): + flavor = zaqarclient(request).flavor(flavor_name, **params) + return flavor + + +def flavor_delete(request, flavor_name): + flavor = zaqarclient(request).flavor(flavor_name, auto_create=False) + flavor.delete() + + +def flavor_update(request, flavor_name, params): + flavor = zaqarclient(request).flavor(flavor_name, auto_create=False) + flavor.update(params) + return flavor + + +def flavor_get(request, flavor_name): + return zaqarclient(request).flavor(flavor_name, auto_create=False).get() 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 52aa64b..25830c3 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 @@ -30,6 +30,8 @@ var queuePath = '/api/zaqar/queues/'; var subPath = '/api/zaqar/queues/%s/subscriptions/'; + var poolPath = '/api/zaqar/pools/'; + var flavorPath = '/api/zaqar/flavors/'; var service = { getQueues: getQueues, @@ -38,7 +40,17 @@ updateQueue: updateQueue, getSubscriptions: getSubscriptions, addSubscription: addSubscription, - deleteSubscription: deleteSubscription + deleteSubscription: deleteSubscription, + getPools: getPools, + getPool: getPool, + createPool: createPool, + deletePool: deletePool, + updatePool: updatePool, + getFlavors: getFlavors, + getFlavor: getFlavor, + createFlavor: createFlavor, + deleteFlavor: deleteFlavor, + updateFlavor: updateFlavor }; return service; @@ -83,6 +95,58 @@ return apiService.delete(url, subscription).error(error(msg)); } + function getPools() { + var msg = gettext('Unable to retrieve the pools.'); + return apiService.get(poolPath).error(error(msg)); + } + + function getPool(poolName) { + var msg = gettext('Unable to retrieve the pool.'); + var url = poolPath + poolName; + return apiService.get(url).error(error(msg)); + } + + function createPool(newPool) { + var msg = gettext('Unable to create the pool.'); + return apiService.put(poolPath, newPool).error(error(msg)); + } + + function deletePool(poolName) { + return apiService.delete(poolPath, [poolName]); + } + + function updatePool(pool) { + var msg = gettext('Unable to update the pool.'); + var url = poolPath + pool.name; + return apiService.post(url, pool).error(error(msg)); + } + + function getFlavors() { + var msg = gettext('Unable to retrieve the flavors.'); + return apiService.get(flavorPath).error(error(msg)); + } + + function getFlavor(flavorName) { + var msg = gettext('Unable to retrieve the flavor.'); + var url = flavorPath + flavorName; + return apiService.get(url).error(error(msg)); + } + + function createFlavor(newFlavor) { + var msg = gettext('Unable to create the flavor.'); + return apiService.put(flavorPath, newFlavor).error(error(msg)); + } + + function deleteFlavor(flavorName) { + return apiService.delete(flavorPath, [flavorName]); + } + + function updateFlavor(flavor) { + var msg = gettext('Unable to update the flavor.'); + var url = flavorPath + flavor.name; + return apiService.post(url, flavor).error(error(msg)); + } + function error(message) { return function() { toast.add('error', message);