From b7b29add1128861056e26b368e3e181ccd8fdbd3 Mon Sep 17 00:00:00 2001 From: Angus Salkeld Date: Mon, 16 Apr 2012 15:37:26 +1000 Subject: [PATCH] Make an attempt at passing exception messages from engine to cli. Signed-off-by: Angus Salkeld --- heat/api/v1/stacks.py | 40 +++++++++++++++++++++++++++++++--------- heat/common/client.py | 2 +- heat/common/exception.py | 4 +++- heat/engine/parser.py | 18 ++++++++---------- heat/engine/resources.py | 10 ++++------ 5 files changed, 47 insertions(+), 27 deletions(-) diff --git a/heat/api/v1/stacks.py b/heat/api/v1/stacks.py index 50bd3ac3..11c135c8 100644 --- a/heat/api/v1/stacks.py +++ b/heat/api/v1/stacks.py @@ -31,6 +31,9 @@ from heat.common import wsgi from heat.common import config from heat import rpc from heat import context +import heat.rpc.common as rpc_common + + logger = logging.getLogger('heat.api.v1.stacks') @@ -67,10 +70,15 @@ class StackController(object): """ con = context.get_admin_context() - stack_list = rpc.call(con, 'engine', + try: + stack_list = rpc.call(con, 'engine', {'method': 'show_stack', 'args': {'stack_name': req.params['StackName'], 'params': dict(req.params)}}) + + except rpc_common.RemoteError as ex: + return webob.exc.HTTPBadRequest(str(ex)) + res = {'DescribeStacksResult': {'Stacks': [] } } stacks = res['DescribeStacksResult']['Stacks'] for s in stack_list['stacks']: @@ -125,11 +133,15 @@ class StackController(object): return webob.exc.HTTPBadRequest(explanation=msg) stack['StackName'] = req.params['StackName'] - return rpc.call(con, 'engine', - {'method': 'create_stack', - 'args': {'stack_name': req.params['StackName'], - 'template': stack, - 'params': dict(req.params)}}) + try: + return rpc.call(con, 'engine', + {'method': 'create_stack', + 'args': {'stack_name': req.params['StackName'], + 'template': stack, + 'params': dict(req.params)}}) + except rpc_common.RemoteError as ex: + return webob.exc.HTTPBadRequest(str(ex)) + def validate_template(self, req): @@ -151,7 +163,10 @@ class StackController(object): return webob.exc.HTTPBadRequest(explanation=msg) logger.info('validate_template') - return con.validate_template(stack, **req.params) + try: + return con.validate_template(stack, **req.params) + except rpc_common.RemoteError as ex: + return webob.exc.HTTPBadRequest(str(ex)) def delete(self, req): """ @@ -159,10 +174,13 @@ class StackController(object): """ con = context.get_admin_context() - res = rpc.call(con, 'engine', + try: + res = rpc.call(con, 'engine', {'method': 'delete_stack', 'args': {'stack_name': req.params['StackName'], 'params': dict(req.params)}}) + except rpc_common.RemoteError as ex: + return webob.exc.HTTPBadRequest(str(ex)) if res == None: return {'DeleteStackResult': ''} @@ -176,9 +194,13 @@ class StackController(object): """ con = context.get_admin_context() stack_name = req.params.get('StackName', None) - event_res = rpc.call(con, 'engine', + try: + event_res = rpc.call(con, 'engine', {'method': 'list_events', 'args': {'stack_name': stack_name}}) + except rpc_common.RemoteError as ex: + return webob.exc.HTTPBadRequest(str(ex)) + events = 'Error' not in event_res and event_res['events'] or [] return {'DescribeStackEventsResult': {'StackEvents': events}} diff --git a/heat/common/client.py b/heat/common/client.py index a3a49d34..9cf0d7cf 100644 --- a/heat/common/client.py +++ b/heat/common/client.py @@ -537,7 +537,7 @@ class BaseClient(object): elif status_code == httplib.CONFLICT: raise exception.Duplicate(res.read()) elif status_code == httplib.BAD_REQUEST: - raise exception.Invalid(res.read()) + raise exception.Invalid(reason=res.read()) elif status_code == httplib.MULTIPLE_CHOICES: raise exception.MultipleChoices(body=res.read()) elif status_code == httplib.REQUEST_ENTITY_TOO_LARGE: diff --git a/heat/common/exception.py b/heat/common/exception.py index 8b6e875b..46ab31aa 100644 --- a/heat/common/exception.py +++ b/heat/common/exception.py @@ -103,7 +103,7 @@ class NotAuthorized(Forbidden): class Invalid(OpenstackException): - message = _("Data supplied was not valid.") + message = _("Data supplied was not valid: %(reason)s") class AuthorizationRedirect(OpenstackException): @@ -168,6 +168,8 @@ class RegionAmbiguity(OpenstackException): class UserParameterMissing(OpenstackException): message = _("The Parameter (%(key)s) was not provided.") +class InvalidTemplateAttribute(OpenstackException): + message = _("The Referenced Attribute (%(resource)s %(key)s) is incorrect.") class UserKeyPairMissing(OpenstackException): message = _("The Key (%(key_name)s) could not be found.") diff --git a/heat/engine/parser.py b/heat/engine/parser.py index 9454ed76..08869255 100644 --- a/heat/engine/parser.py +++ b/heat/engine/parser.py @@ -16,7 +16,8 @@ import eventlet import json import logging -import traceback + +from heat.common import exception from heat.engine import resources from heat.db import api as db_api @@ -144,9 +145,7 @@ class Stack(object): try: self.resources[r].create() except Exception as ex: - readable = traceback.format_exc() - logger.error('%s', readable) - logger.error('create: %s' % str(ex)) + logger.exception('create') failed = True self.resources[r].state_set(self.resources[r].CREATE_FAILED, str(ex)) else: @@ -238,15 +237,13 @@ class Stack(object): def parameter_get(self, key): if self.parms[key] == None: - logger.warn('Trying to reference parameter: %s, but it is empty' % key) - return '' + raise exception.UserParameterMissing(key=key) elif self.parms[key].has_key('Value'): return self.parms[key]['Value'] elif self.parms[key].has_key('Default'): return self.parms[key]['Default'] else: - logger.warn('Trying to reference parameter: %s, but no Value or Default' % key) - return '' + raise exception.UserParameterMissing(key=key) def resolve_static_refs(self, s): ''' @@ -309,8 +306,9 @@ class Stack(object): res = self.resources.get(resource_name) rc = None if res: - rc = res.FnGetAtt(key_name) - #print 'found attr:%s.%s=%s' % (res.name, key_name, rc) + return res.FnGetAtt(key_name) + else: + raise exception.InvalidTemplateAttribute(resource=resource_name, key=key_name) return rc else: s[i] = self.resolve_attributes(s[i]) diff --git a/heat/engine/resources.py b/heat/engine/resources.py index 430d27c3..303d331d 100644 --- a/heat/engine/resources.py +++ b/heat/engine/resources.py @@ -165,8 +165,7 @@ http://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/intrinsic-f ''' http://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html ''' - print '%s.GetAtt(%s)' % (self.name, key) - return unicode('not-this-surely') + raise exception.InvalidTemplateAttribute(resource=self.name, key=key) def FnBase64(self, data): ''' @@ -278,8 +277,7 @@ class ElasticIp(Resource): if key == 'AllocationId': return unicode(self.instance_id) else: - logger.warn('%s.GetAtt(%s) is not handled' % (self.name, key)) - return unicode('') + raise exception.InvalidTemplateAttribute(resource=self.name, key=key) class ElasticIpAssociation(Resource): def __init__(self, name, json_snippet, stack): @@ -437,13 +435,13 @@ class Instance(Resource): def FnGetAtt(self, key): - res = 'not-this-surely' + res = None if key == 'AvailabilityZone': res = self.t['Properties']['AvailabilityZone'] elif key == 'PublicIp': res = self.ipaddress else: - logger.warn('%s.GetAtt(%s) is not handled' % (self.name, key)) + raise exception.InvalidTemplateAttribute(resource=self.name, key=key) # TODO(asalkeld) PrivateDnsName, PublicDnsName & PrivateIp -- 2.45.2