Fix serialization of InvalidSchemaError

Move InvalidSchemaError to heat.common.exception and pass a keyword
argument so that it can be serialized properly.

Change-Id: Iada3273b2b0b129bc0125b68fecf1e0efc081611
Closes-Bug: #1336740
This commit is contained in:
Thomas Herve 2014-07-02 15:12:46 +02:00
parent a199f46137
commit 4503154c12
11 changed files with 86 additions and 81 deletions

View File

@ -276,6 +276,7 @@ def map_remote_error(ex):
'PhysicalResourceNotFound',
'WatchRuleNotFound',
'StackValidationFailed',
'InvalidSchemaError',
'InvalidTemplateReference',
'InvalidTemplateVersion',
'InvalidTemplateSection',

View File

@ -68,6 +68,7 @@ class FaultWrapper(wsgi.Middleware):
'Forbidden': webob.exc.HTTPForbidden,
'StackExists': webob.exc.HTTPConflict,
'StackValidationFailed': webob.exc.HTTPBadRequest,
'InvalidSchemaError': webob.exc.HTTPBadRequest,
'InvalidTemplateReference': webob.exc.HTTPBadRequest,
'InvalidTemplateVersion': webob.exc.HTTPBadRequest,
'InvalidTemplateSection': webob.exc.HTTPBadRequest,

View File

@ -253,6 +253,10 @@ class StackValidationFailed(HeatException):
msg_fmt = _("%(message)s")
class InvalidSchemaError(HeatException):
msg_fmt = _("%(message)s")
class ResourceNotFound(HeatException):
msg_fmt = _("The Resource (%(resource_name)s) could not be found "
"in Stack %(stack_name)s.")

View File

@ -23,10 +23,6 @@ from heat.engine import resources
from heat.openstack.common import strutils
class InvalidSchemaError(exception.Error):
pass
class Schema(collections.Mapping):
"""
Schema base class for validating properties or parameters.
@ -85,7 +81,8 @@ class Schema(collections.Mapping):
self.label = label
self.type = data_type
if self.type not in self.TYPES:
raise InvalidSchemaError(_('Invalid type (%s)') % self.type)
raise exception.InvalidSchemaError(
message=_('Invalid type (%s)') % self.type)
self.description = description
self.required = required
@ -95,7 +92,7 @@ class Schema(collections.Mapping):
msg = _('Single schema valid only for '
'%(ltype)s, not %(utype)s') % dict(ltype=self.LIST,
utype=self.type)
raise InvalidSchemaError(msg)
raise exception.InvalidSchemaError(message=msg)
self.schema = AnyIndexDict(schema)
else:
@ -106,7 +103,7 @@ class Schema(collections.Mapping):
'%(mtype)s, not %(utype)s') % dict(ltype=self.LIST,
mtype=self.MAP,
utype=self.type)
raise InvalidSchemaError(msg)
raise exception.InvalidSchemaError(message=msg)
self.constraints = constraints or []
self.default = default
@ -124,7 +121,7 @@ class Schema(collections.Mapping):
'invalid for %(utype)s') % dict(
name=type(c).__name__,
utype=self.type)
raise InvalidSchemaError(err_msg)
raise exception.InvalidSchemaError(message=err_msg)
self._validate_default(context)
# validated nested schema(ta)
@ -140,9 +137,9 @@ class Schema(collections.Mapping):
try:
self.validate_constraints(self.default, context)
except (ValueError, TypeError) as exc:
raise InvalidSchemaError(_('Invalid default '
'%(default)s (%(exc)s)') %
dict(default=self.default, exc=exc))
raise exception.InvalidSchemaError(
message=_('Invalid default %(default)s (%(exc)s)') %
dict(default=self.default, exc=exc))
def set_default(self, default=None):
"""Set the default value for this Schema object."""
@ -335,12 +332,13 @@ class Range(Constraint):
for param in (min, max):
if not isinstance(param, (float, int, long, type(None))):
raise InvalidSchemaError(_('min/max must be numeric'))
raise exception.InvalidSchemaError(
message=_('min/max must be numeric'))
if min is max is None:
raise InvalidSchemaError(
_('A range constraint must have a min value and/or a max '
'value specified.'))
raise exception.InvalidSchemaError(
message=_('A range constraint must have a min value and/or '
'a max value specified.'))
def _str(self):
if self.max is None:
@ -395,16 +393,16 @@ class Length(Range):
def __init__(self, min=None, max=None, description=None):
if min is max is None:
raise InvalidSchemaError(
_('A length constraint must have a min value and/or a max '
'value specified.'))
raise exception.InvalidSchemaError(
message=_('A length constraint must have a min value and/or '
'a max value specified.'))
super(Length, self).__init__(min, max, description)
for param in (min, max):
if not isinstance(param, (int, long, type(None))):
msg = _('min/max length must be integral')
raise InvalidSchemaError(msg)
raise exception.InvalidSchemaError(message=msg)
def _str(self):
if self.max is None:
@ -443,7 +441,8 @@ class AllowedValues(Constraint):
super(AllowedValues, self).__init__(description)
if (not isinstance(allowed, collections.Sequence) or
isinstance(allowed, basestring)):
raise InvalidSchemaError(_('AllowedValues must be a list'))
raise exception.InvalidSchemaError(
message=_('AllowedValues must be a list'))
self.allowed = tuple(allowed)
def _str(self):
@ -487,7 +486,8 @@ class AllowedPattern(Constraint):
def __init__(self, pattern, description=None):
super(AllowedPattern, self).__init__(description)
if not isinstance(pattern, basestring):
raise InvalidSchemaError(_('AllowedPattern must be a string'))
raise exception.InvalidSchemaError(
message=_('AllowedPattern must be a string'))
self.pattern = pattern
self.match = re.compile(pattern).match

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from heat.common import exception
from heat.engine import constraints as constr
from heat.engine import parameters
@ -63,9 +64,9 @@ class HOTParamSchema(parameters.Schema):
return
if not isinstance(constraints, list):
raise constr.InvalidSchemaError(
_("Invalid parameter constraints for parameter %s, "
"expected a list") % param_name)
raise exception.InvalidSchemaError(
message=_("Invalid parameter constraints for parameter "
"%s, expected a list") % param_name)
valid_keys = (DESCRIPTION, LENGTH, RANGE, ALLOWED_VALUES,
ALLOWED_PATTERN, CUSTOM_CONSTRAINT)
@ -96,8 +97,8 @@ class HOTParamSchema(parameters.Schema):
cdef = constraint.get(CUSTOM_CONSTRAINT)
yield constr.CustomConstraint(cdef, desc)
else:
raise constr.InvalidSchemaError(
_("No constraint expressed"))
raise exception.InvalidSchemaError(
message=_("No constraint expressed"))
# make update_allowed true by default on TemplateResources
# as the template should deal with this.

View File

@ -75,17 +75,16 @@ class Schema(constr.Schema):
try:
default_value = self.default.split(',')
except (KeyError, AttributeError) as err:
raise constr.InvalidSchemaError(_('Default must be a '
'comma-delimited list '
'string: %s') % err)
raise exception.InvalidSchemaError(
message=_('Default must be a comma-delimited list '
'string: %s') % err)
try:
self.validate_constraints(default_value, context)
except (ValueError, TypeError,
exception.StackValidationFailed) as exc:
raise constr.InvalidSchemaError(_('Invalid default '
'%(default)s (%(exc)s)') %
dict(default=self.default,
exc=exc))
raise exception.InvalidSchemaError(
message=_('Invalid default %(default)s (%(exc)s)') %
dict(default=self.default, exc=exc))
def set_default(self, default=None):
super(Schema, self).set_default(default)
@ -101,12 +100,12 @@ class Schema(constr.Schema):
@staticmethod
def _check_dict(schema_dict, allowed_keys, entity):
if not isinstance(schema_dict, dict):
raise constr.InvalidSchemaError(
_("Invalid %s, expected a mapping") % entity)
raise exception.InvalidSchemaError(
message=_("Invalid %s, expected a mapping") % entity)
for key in schema_dict:
if key not in allowed_keys:
raise constr.InvalidSchemaError(
_("Invalid key '%(key)s' for %(entity)s") % {
raise exception.InvalidSchemaError(
message=_("Invalid key '%(key)s' for %(entity)s") % {
"key": key, "entity": entity})
@classmethod
@ -116,8 +115,9 @@ class Schema(constr.Schema):
"parameter (%s)" % param_name)
if cls.TYPE not in schema_dict:
raise constr.InvalidSchemaError(
_("Missing parameter type for parameter: %s") % param_name)
raise exception.InvalidSchemaError(
message=_("Missing parameter type for parameter: %s") %
param_name)
@classmethod
def from_dict(cls, param_name, schema_dict):

View File

@ -74,7 +74,8 @@ class Schema(constr.Schema):
unknown = [k for k in schema_dict if k not in SCHEMA_KEYS]
if unknown:
raise constr.InvalidSchemaError(_('Unknown key(s) %s') % unknown)
raise exception.InvalidSchemaError(
message=_('Unknown key(s) %s') % unknown)
def constraints():
def get_num(key):
@ -95,7 +96,8 @@ class Schema(constr.Schema):
try:
data_type = schema_dict[TYPE]
except KeyError:
raise constr.InvalidSchemaError(_('No %s specified') % TYPE)
raise exception.InvalidSchemaError(
message=_('No %s specified') % TYPE)
if SCHEMA in schema_dict:
if data_type == Schema.LIST:
@ -104,11 +106,9 @@ class Schema(constr.Schema):
schema_dicts = schema_dict[SCHEMA].items()
ss = dict((n, cls.from_legacy(sd)) for n, sd in schema_dicts)
else:
raise constr.InvalidSchemaError(_('%(schema)s supplied for '
' %(type)s %(data)s') %
dict(schema=SCHEMA,
type=TYPE,
data=data_type))
raise exception.InvalidSchemaError(
message=_('%(schema)s supplied for %(type)s %(data)s') %
dict(schema=SCHEMA, type=TYPE, data=data_type))
else:
ss = None

View File

@ -181,11 +181,11 @@ class SchemaTest(testtools.TestCase):
self.assertEqual(d, dict(l))
def test_invalid_type(self):
self.assertRaises(constraints.InvalidSchemaError, constraints.Schema,
self.assertRaises(exception.InvalidSchemaError, constraints.Schema,
'Fish')
def test_schema_invalid_type(self):
self.assertRaises(constraints.InvalidSchemaError,
self.assertRaises(exception.InvalidSchemaError,
constraints.Schema,
'String',
schema=constraints.Schema('String'))
@ -193,14 +193,14 @@ class SchemaTest(testtools.TestCase):
def test_range_invalid_type(self):
schema = constraints.Schema('String',
constraints=[constraints.Range(1, 10)])
err = self.assertRaises(constraints.InvalidSchemaError,
err = self.assertRaises(exception.InvalidSchemaError,
schema.validate)
self.assertIn('Range constraint invalid for String', str(err))
def test_length_invalid_type(self):
schema = constraints.Schema('Integer',
constraints=[constraints.Length(1, 10)])
err = self.assertRaises(constraints.InvalidSchemaError,
err = self.assertRaises(exception.InvalidSchemaError,
schema.validate)
self.assertIn('Length constraint invalid for Integer', str(err))
@ -209,21 +209,21 @@ class SchemaTest(testtools.TestCase):
'Integer',
constraints=[constraints.AllowedPattern('[0-9]*')]
)
err = self.assertRaises(constraints.InvalidSchemaError,
err = self.assertRaises(exception.InvalidSchemaError,
schema.validate)
self.assertIn('AllowedPattern constraint invalid for Integer',
str(err))
def test_range_vals_invalid_type(self):
self.assertRaises(constraints.InvalidSchemaError,
self.assertRaises(exception.InvalidSchemaError,
constraints.Range, '1', 10)
self.assertRaises(constraints.InvalidSchemaError,
self.assertRaises(exception.InvalidSchemaError,
constraints.Range, 1, '10')
def test_length_vals_invalid_type(self):
self.assertRaises(constraints.InvalidSchemaError,
self.assertRaises(exception.InvalidSchemaError,
constraints.Length, '1', 10)
self.assertRaises(constraints.InvalidSchemaError,
self.assertRaises(exception.InvalidSchemaError,
constraints.Length, 1, '10')
def test_schema_validate_good(self):
@ -236,7 +236,7 @@ class SchemaTest(testtools.TestCase):
s = constraints.Schema(constraints.Schema.STRING, 'A string',
default='wibble', required=True,
constraints=[constraints.Range(max=4)])
err = self.assertRaises(constraints.InvalidSchemaError, s.validate)
err = self.assertRaises(exception.InvalidSchemaError, s.validate)
self.assertIn('Range constraint invalid for String', str(err))
def test_schema_nested_validate_good(self):
@ -253,7 +253,7 @@ class SchemaTest(testtools.TestCase):
constraints=[constraints.Range(max=4)])
s = constraints.Schema(constraints.Schema.MAP, 'A map',
schema={'Foo': nested})
err = self.assertRaises(constraints.InvalidSchemaError, s.validate)
err = self.assertRaises(exception.InvalidSchemaError, s.validate)
self.assertIn('Range constraint invalid for String', str(err))
def test_allowed_values_numeric_int(self):

View File

@ -17,7 +17,6 @@ import six
from heat.common import exception
from heat.common import identifier
from heat.common import template_format
from heat.engine import constraints
from heat.engine import environment
from heat.engine import function
from heat.engine.hot import functions as hot_functions
@ -1235,7 +1234,7 @@ class HOTParamValidatorTest(HeatTestCase):
schema = hot_param.HOTParamSchema.from_dict('db_port',
param['db_port'])
err = self.assertRaises(constraints.InvalidSchemaError,
err = self.assertRaises(exception.InvalidSchemaError,
schema.validate)
self.assertIn(range_desc, str(err))
@ -1247,7 +1246,7 @@ class HOTParamValidatorTest(HeatTestCase):
foo: bar
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual("Invalid key 'foo' for parameter (param1)",
str(error))
@ -1260,7 +1259,7 @@ class HOTParamValidatorTest(HeatTestCase):
description: Hi!
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual("Missing parameter type for parameter: param1",
str(error))
@ -1273,7 +1272,7 @@ class HOTParamValidatorTest(HeatTestCase):
type: Unicode
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual(
"Invalid type (Unicode)", str(error))
@ -1289,7 +1288,7 @@ class HOTParamValidatorTest(HeatTestCase):
default: foo
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual(
"Invalid key 'allowed_valus' for parameter constraints",
@ -1305,7 +1304,7 @@ class HOTParamValidatorTest(HeatTestCase):
default: foo
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual(
"Invalid parameter constraints for parameter param1, "
@ -1321,7 +1320,7 @@ class HOTParamValidatorTest(HeatTestCase):
default: foo
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual(
"Invalid parameter constraints, expected a mapping", str(error))
@ -1337,7 +1336,7 @@ class HOTParamValidatorTest(HeatTestCase):
default: foo
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual("No constraint expressed", str(error))
@ -1352,7 +1351,7 @@ class HOTParamValidatorTest(HeatTestCase):
default: foo
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual(
"Invalid range constraint, expected a mapping", str(error))
@ -1368,7 +1367,7 @@ class HOTParamValidatorTest(HeatTestCase):
default: 1
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual(
"Invalid key 'foo' for range constraint", str(error))
@ -1384,7 +1383,7 @@ class HOTParamValidatorTest(HeatTestCase):
default: foo
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual(
"Invalid length constraint, expected a mapping", str(error))
@ -1400,7 +1399,7 @@ class HOTParamValidatorTest(HeatTestCase):
default: foo
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual(
"Invalid key 'foo' for length constraint", str(error))
@ -1416,7 +1415,7 @@ class HOTParamValidatorTest(HeatTestCase):
default: foo
''')
error = self.assertRaises(
constraints.InvalidSchemaError, parameters.Parameters,
exception.InvalidSchemaError, parameters.Parameters,
"stack_testit", parser.Template(hot_tpl))
self.assertEqual(
"AllowedPattern must be a string", str(error))

View File

@ -17,7 +17,6 @@ import testtools
from heat.common import exception
from heat.common import identifier
from heat.engine import constraints as constr
from heat.engine import parameters
from heat.engine import template
@ -51,8 +50,8 @@ class ParameterTest(testtools.TestCase):
self.assertIsInstance(p, parameters.JsonParam)
def test_new_bad_type(self):
self.assertRaises(constr.InvalidSchemaError, self.new_parameter, 'p',
{'Type': 'List'}, validate_value=False)
self.assertRaises(exception.InvalidSchemaError, self.new_parameter,
'p', {'Type': 'List'}, validate_value=False)
def test_default_no_override(self):
p = self.new_parameter('defaulted', {'Type': 'String',
@ -75,7 +74,7 @@ class ParameterTest(testtools.TestCase):
'AllowedValues': ['foo'],
'ConstraintDescription': 'wibble',
'Default': 'bar'}
err = self.assertRaises(constr.InvalidSchemaError,
err = self.assertRaises(exception.InvalidSchemaError,
self.new_parameter, 'p', schema, 'foo')
self.assertIn('wibble', str(err))
@ -470,7 +469,7 @@ class ParametersTest(testtools.TestCase):
params = {'Parameters': {'Foo': {'Type': 'String'},
'NoAttr': 'No attribute.',
'Bar': {'Type': 'Number', 'Default': '1'}}}
self.assertRaises(constr.InvalidSchemaError,
self.assertRaises(exception.InvalidSchemaError,
self.new_parameters,
'test',
params)
@ -479,14 +478,14 @@ class ParametersTest(testtools.TestCase):
class ParameterSchemaTest(testtools.TestCase):
def test_validate_schema_wrong_key(self):
error = self.assertRaises(constr.InvalidSchemaError,
error = self.assertRaises(exception.InvalidSchemaError,
parameters.Schema.from_dict, 'param_name',
{"foo": "bar"})
self.assertEqual("Invalid key 'foo' for parameter (param_name)",
str(error))
def test_validate_schema_no_type(self):
error = self.assertRaises(constr.InvalidSchemaError,
error = self.assertRaises(exception.InvalidSchemaError,
parameters.Schema.from_dict,
'broken',
{"Description": "Hi!"})

View File

@ -310,7 +310,7 @@ class PropertySchemaTest(testtools.TestCase):
self.assertEqual('wibble', ss.default)
def test_from_legacy_invalid_key(self):
self.assertRaises(constraints.InvalidSchemaError,
self.assertRaises(exception.InvalidSchemaError,
properties.Schema.from_legacy,
{'Type': 'String', 'Foo': 'Bar'})
@ -604,11 +604,11 @@ class PropertyTest(testtools.TestCase):
self.assertEqual('String', p.type())
def test_bad_type(self):
self.assertRaises(constraints.InvalidSchemaError,
self.assertRaises(exception.InvalidSchemaError,
properties.Property, {'Type': 'Fish'})
def test_bad_key(self):
self.assertRaises(constraints.InvalidSchemaError,
self.assertRaises(exception.InvalidSchemaError,
properties.Property,
{'Type': 'String', 'Foo': 'Bar'})