From 7c324982f2e7e6d4473d5574ad65ef5754971130 Mon Sep 17 00:00:00 2001 From: Sergey Lukjanov Date: Tue, 25 Jun 2013 16:31:00 +0400 Subject: [PATCH] The 'check_exists' applied to all API calls * 404 is now returned instead of 500 in all API calls; * direct calls to nova client moved from api/v10 to service/api * utils/api has been updated to avoid implicit magic. Partailly implements blueprint savanna-rest-api-1-0-validation Change-Id: Id1e64b03920c59bc96da6c8f709bd1beccb59d76 --- savanna/api/v10.py | 53 +++++++++++++++++++++++------------------- savanna/service/api.py | 24 +++++++++++++++++++ savanna/utils/api.py | 28 +++++++++------------- 3 files changed, 64 insertions(+), 41 deletions(-) diff --git a/savanna/api/v10.py b/savanna/api/v10.py index 1f844a12..b3e78059 100644 --- a/savanna/api/v10.py +++ b/savanna/api/v10.py @@ -15,8 +15,8 @@ from savanna.openstack.common import log as logging from savanna.service import api +from savanna.service import validation as v import savanna.utils.api as u -from savanna.utils.openstack import nova LOG = logging.getLogger(__name__) @@ -36,16 +36,19 @@ def clusters_create(data): @rest.get('/clusters/') +@v.check_exists(api.get_cluster, 'cluster_id') def clusters_get(cluster_id): return u.render(api.get_cluster(id=cluster_id).wrapped_dict) @rest.put('/clusters/') -def clusters_update(_cluster_id): +@v.check_exists(api.get_cluster, 'cluster_id') +def clusters_update(cluster_id): return _not_implemented() @rest.delete('/clusters/') +@v.check_exists(api.get_cluster, 'cluster_id') def clusters_delete(cluster_id): api.terminate_cluster(id=cluster_id) return u.render() @@ -65,17 +68,20 @@ def cluster_templates_create(data): @rest.get('/cluster-templates/') +@v.check_exists(api.get_cluster_template, 'cluster_template_id') def cluster_templates_get(cluster_template_id): return u.render( api.get_cluster_template(id=cluster_template_id).wrapped_dict) @rest.put('/cluster-templates/') -def cluster_templates_update(_cluster_template_id): +@v.check_exists(api.get_cluster_template, 'cluster_template_id') +def cluster_templates_update(cluster_template_id): return _not_implemented() @rest.delete('/cluster-templates/') +@v.check_exists(api.get_cluster_template, 'cluster_template_id') def cluster_templates_delete(cluster_template_id): api.terminate_cluster_template(id=cluster_template_id) return u.render() @@ -95,17 +101,20 @@ def node_group_templates_create(data): @rest.get('/node-group-templates/') +@v.check_exists(api.get_node_group_template, 'node_group_template_id') def node_group_templates_get(node_group_template_id): return u.render( api.get_node_group_template(id=node_group_template_id).wrapped_dict) @rest.put('/node-group-templates/') -def node_group_templates_update(_node_group_template_id): +@v.check_exists(api.get_node_group_template, 'node_group_template_id') +def node_group_templates_update(node_group_template_id): return _not_implemented() @rest.delete('/node-group-templates/') +@v.check_exists(api.get_node_group_template, 'node_group_template_id') def node_group_templates_delete(node_group_template_id): api.terminate_node_group_template(id=node_group_template_id) return u.render() @@ -119,16 +128,19 @@ def plugins_list(): @rest.get('/plugins/') +@v.check_exists(api.get_plugin, plugin_name='plugin_name') def plugins_get(plugin_name): return u.render(api.get_plugin(plugin_name).wrapped_dict) @rest.get('/plugins//') +@v.check_exists(api.get_plugin, plugin_name='plugin_name', version='version') def plugins_get_version(plugin_name, version): return u.render(api.get_plugin(plugin_name, version).wrapped_dict) @rest.post_file('/plugins///convert-config') +@v.check_exists(api.get_plugin, plugin_name='plugin_name', version='version') def plugins_convert_to_cluster_template(plugin_name, version, data): return u.render( api.convert_to_cluster_template(plugin_name, version, data)) @@ -137,47 +149,40 @@ def plugins_convert_to_cluster_template(plugin_name, version, data): ## Image Registry ops @rest.get('/images') -def images_list(request): - tags = request.args.getlist('tags') - return u.render( - images=[i.dict for i in nova.client().images.list_registered(tags)]) - - -def _render_image(image_id, novaclient): - return u.render(novaclient.images.get(image_id).wrapped_dict) +def images_list(): + tags = u.get_request_args().getlist('tags') + return u.render(images=[i.dict for i in api.get_images(tags)]) @rest.get('/images/') +@v.check_exists(api.get_image, id='image_id') def images_get(image_id): - return _render_image(image_id, nova.client()) + return u.render(api.get_image(id=image_id).wrapped_dict) @rest.post('/images/') +@v.check_exists(api.get_image, id='image_id') def images_set(image_id, data): - novaclient = nova.client() - novaclient.images.set_description(image_id, **data) - return _render_image(image_id, novaclient) + return u.render(api.register_image(image_id, **data).wrapped_dict) @rest.delete('/images/') +@v.check_exists(api.get_image, id='image_id') def images_unset(image_id): - novaclient = nova.client() - novaclient.images.unset_description(image_id) + api.unregister_image(image_id) return u.render() @rest.post('/images//tag') +@v.check_exists(api.get_image, id='image_id') def image_tags_add(image_id, data): - novaclient = nova.client() - novaclient.images.tag(image_id, **data) - return _render_image(image_id, novaclient) + return u.render(api.add_image_tags(image_id, **data).wrapped_dict) @rest.post('/images//untag') +@v.check_exists(api.get_image, id='image_id') def image_tags_delete(image_id, data): - novaclient = nova.client() - novaclient.images.untag(image_id, **data) - return _render_image(image_id, novaclient) + return u.render(api.remove_image_tags(image_id, **data).wrapped_dict) def _not_implemented(): diff --git a/savanna/service/api.py b/savanna/service/api.py index 5b8eed13..d86fba91 100644 --- a/savanna/service/api.py +++ b/savanna/service/api.py @@ -173,3 +173,27 @@ def get_image(**kwargs): return nova.client().images.get(kwargs['id']) else: return nova.client().images.find(**kwargs) + + +def register_image(image_id, username, description=None): + client = nova.client() + client.images.set_description(image_id, username, description) + return client.images.get(image_id) + + +def unregister_image(image_id): + client = nova.client() + client.images.unset_description(image_id) + return client.images.get(image_id) + + +def add_image_tags(image_id, tags): + client = nova.client() + client.images.tag(image_id, tags) + return client.images.get(image_id) + + +def remove_image_tags(image_id, tags): + client = nova.client() + client.images.untag(image_id, tags) + return client.images.get(image_id) diff --git a/savanna/utils/api.py b/savanna/utils/api.py index df9c4c1e..5db2a0a0 100644 --- a/savanna/utils/api.py +++ b/savanna/utils/api.py @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import inspect import mimetypes import traceback @@ -76,33 +75,24 @@ class Rest(flask.Blueprint): ctx = context.Context( flask.request.headers['X-User-Id'], flask.request.headers['X-Tenant-Id'], - flask.request.headers[ - 'X-Auth-Token'], + flask.request.headers['X-Auth-Token'], flask.request.headers) context.set_ctx(ctx) - # set func implicit args - args = inspect.getargspec(func).args - - if 'ctx' in args: - kwargs['ctx'] = ctx - if 'request' in args: - kwargs['request'] = flask.request - - if flask.request.method in ['POST', 'PUT'] and 'data' in args: + if flask.request.method in ['POST', 'PUT']: kwargs['data'] = request_data() - return func(**kwargs) + try: + return func(**kwargs) + except Exception, e: + return internal_error(500, 'Exception in REST API call', e) f_rule = "/" + rule self.add_url_rule(f_rule, endpoint, handler, **options) ext_rule = f_rule + '.' self.add_url_rule(ext_rule, endpoint, handler, **options) - try: - return func - except Exception, e: - return internal_error(500, 'Exception in API call', e) + return func return decorator @@ -203,6 +193,10 @@ def request_data(): return flask.request.parsed_data +def get_request_args(): + return flask.request.args + + def abort_and_log(status_code, descr, exc=None): LOG.error("Request aborted with status code %s and message '%s'", status_code, descr)