Improve StackValidationFailed response in properties

In some cases, there is no information about resource and
section, where Property error raised. This patch improves
StackValidationFailed msg, so this msg look like "Property
error : resource_name.section_name.key_name: error_msg", where
section_name is section, where Property error raised, e.g.
'update_policy'.

Cherry-picked from 3ad4614276
Change-Id: Iab2a6acdec254b39983de420ab03f994cff48d89
Closes-bug: #1358512
This commit is contained in:
Peter Razumovsky 2014-10-08 19:19:13 +04:00 committed by Steve Baker
parent e35c523f19
commit c0d1b9168d
26 changed files with 217 additions and 147 deletions

View File

@ -83,6 +83,9 @@ class CfnTemplate(template.Template):
return dict((name, parameters.Schema.from_dict(name, schema))
for name, schema in six.iteritems(params))
def get_section_name(self, section):
return section
def parameters(self, stack_identifier, user_params, param_defaults=None):
return parameters.Parameters(stack_identifier, self,
user_params=user_params,

View File

@ -56,6 +56,12 @@ class HOTemplate20130523(template.Template):
cfn_template.CfnTemplate.RESOURCES: RESOURCES,
cfn_template.CfnTemplate.OUTPUTS: OUTPUTS}
_RESOURCE_HOT_TO_CFN_ATTRS = {'type': 'Type',
'properties': 'Properties',
'metadata': 'Metadata',
'depends_on': 'DependsOn',
'deletion_policy': 'DeletionPolicy',
'update_policy': 'UpdatePolicy'}
functions = {
'Fn::GetAZs': cfn_funcs.GetAZs,
'get_param': hot_funcs.GetParam,
@ -157,15 +163,14 @@ class HOTemplate20130523(template.Template):
def _translate_resources(self, resources):
"""Get the resources of the template translated into CFN format."""
HOT_TO_CFN_ATTRS = {'type': 'Type',
'properties': 'Properties',
'metadata': 'Metadata',
'depends_on': 'DependsOn',
'deletion_policy': 'DeletionPolicy',
'update_policy': 'UpdatePolicy'}
return self._translate_section('resources', 'type', resources,
HOT_TO_CFN_ATTRS)
self._RESOURCE_HOT_TO_CFN_ATTRS)
def get_section_name(self, section):
cfn_to_hot_attrs = dict(zip(self._RESOURCE_HOT_TO_CFN_ATTRS.values(),
self._RESOURCE_HOT_TO_CFN_ATTRS.keys()))
return cfn_to_hot_attrs.get(section, section)
def _translate_outputs(self, outputs):
"""Get the outputs of the template translated into CFN format."""

View File

@ -267,7 +267,6 @@ class Property(object):
keys = list(self.schema.schema)
schemata = dict((k, self.schema.schema[k]) for k in keys)
properties = Properties(schemata, dict(child_values),
parent_name=self.name,
context=self.context)
if validate:
properties.validate()
@ -343,15 +342,16 @@ class Property(object):
class Properties(collections.Mapping):
def __init__(self, schema, data, resolver=lambda d: d, parent_name=None,
context=None):
context=None, section=None):
self.props = dict((k, Property(s, k, context))
for k, s in schema.items())
self.resolve = resolver
self.data = data
if parent_name is None:
self.error_prefix = ''
else:
self.error_prefix = '%s: ' % parent_name
self.error_prefix = []
if parent_name is not None:
self.error_prefix.append(parent_name)
if section is not None:
self.error_prefix.append(section)
self.context = context
@staticmethod
@ -369,38 +369,58 @@ class Properties(collections.Mapping):
return {}
def validate(self, with_value=True):
for (key, prop) in self.props.items():
# check that update_allowed and immutable
# do not contradict each other
if prop.update_allowed() and prop.immutable():
msg = _("Property %(prop)s: %(ua)s and %(im)s "
"cannot both be True") % {
'prop': key,
'ua': prop.schema.UPDATE_ALLOWED,
'im': prop.schema.IMMUTABLE}
raise exception.InvalidSchemaError(message=msg)
if with_value:
try:
self._get_property_value(key, validate=True)
except ValueError as e:
msg = _("Property error : %s") % e
try:
for key in self.data:
if key not in self.props:
msg = _("Unknown Property %s") % key
raise exception.StackValidationFailed(message=msg)
# are there unimplemented Properties
if not prop.implemented() and key in self.data:
msg = _("Property %s not implemented yet") % key
raise exception.StackValidationFailed(message=msg)
for (key, prop) in self.props.items():
# check that update_allowed and immutable
# do not contradict each other
if prop.update_allowed() and prop.immutable():
msg = _("Property %(prop)s: %(ua)s and %(im)s "
"cannot both be True") % {
'prop': key,
'ua': prop.schema.UPDATE_ALLOWED,
'im': prop.schema.IMMUTABLE}
raise exception.InvalidSchemaError(message=msg)
for key in self.data:
if key not in self.props:
msg = _("Unknown Property %s") % key
raise exception.StackValidationFailed(message=msg)
if with_value:
try:
self._get_property_value(key, validate=True)
except exception.StackValidationFailed as ex:
path = [key]
path.extend(ex.path)
raise exception.StackValidationFailed(
path=path, message=ex.error_message)
except ValueError as e:
if prop.required() and key not in self.data:
path = []
else:
path = [key]
raise exception.StackValidationFailed(
path=path, message=six.text_type(e))
# are there unimplemented Properties
if not prop.implemented() and key in self.data:
msg = _("Property %s not implemented yet") % key
raise exception.StackValidationFailed(message=msg)
except exception.StackValidationFailed as ex:
# NOTE(prazumovsky): should reraise exception for adding specific
# error name and error_prefix to path for correct error message
# building.
path = self.error_prefix
path.extend(ex.path)
raise exception.StackValidationFailed(
error=ex.error or 'Property error',
path=path,
message=ex.error_message
)
def _get_property_value(self, key, validate=False):
if key not in self:
raise KeyError(_('%(prefix)sInvalid Property %(key)s') %
{'prefix': self.error_prefix, 'key': key})
raise KeyError(_('Invalid Property %s') % key)
prop = self.props[key]
@ -414,16 +434,20 @@ class Properties(collections.Mapping):
value = self.resolve(unresolved_value)
return prop.get_value(value, validate)
# Children can raise StackValidationFailed with unique path which
# is necessary for further use in StackValidationFailed exception.
# So we need to handle this exception in this method.
except exception.StackValidationFailed as e:
raise exception.StackValidationFailed(path=e.path,
message=e.error_message)
# the resolver function could raise any number of exceptions,
# so handle this generically
except Exception as e:
raise ValueError('%s%s %s' % (self.error_prefix, key,
six.text_type(e)))
raise ValueError(six.text_type(e))
elif prop.has_default():
return prop.get_value(None, validate)
elif prop.required():
raise ValueError(_('%(prefix)sProperty %(key)s not assigned') %
{'prefix': self.error_prefix, 'key': key})
raise ValueError(_('Property %s not assigned') % key)
def __getitem__(self, key):
return self._get_property_value(key)

View File

@ -887,7 +887,18 @@ class Resource(object):
function.validate(self.t)
self.validate_deletion_policy(self.t.deletion_policy())
return self.properties.validate(with_value=self.stack.strict_validate)
try:
validate = self.properties.validate(
with_value=self.stack.strict_validate)
except exception.StackValidationFailed as ex:
path = [self.stack.t.RESOURCES, ex.path[0],
self.stack.t.get_section_name(ex.path[1])]
path.extend(ex.path[2:])
raise exception.StackValidationFailed(
error=ex.error,
path=path,
message=ex.error_message)
return validate
@classmethod
def validate_deletion_policy(cls, policy):

View File

@ -183,7 +183,8 @@ class ResourceDefinitionCore(object):
require a context to validate constraints.
"""
return properties.Properties(schema, self._properties or {},
function.resolve, self.name, context)
function.resolve, self.name, context,
section=PROPERTIES)
def deletion_policy(self):
"""
@ -201,7 +202,8 @@ class ResourceDefinitionCore(object):
require a context to validate constraints.
"""
return properties.Properties(schema, self._update_policy or {},
function.resolve, self.name, context)
function.resolve, self.name, context,
section=UPDATE_POLICY)
def metadata(self):
"""

View File

@ -811,7 +811,7 @@ class EngineService(service.Service):
env = environment.Environment(params)
for res in tmpl_resources.values():
for name, res in six.iteritems(tmpl_resources):
ResourceClass = env.get_class(res['Type'])
if ResourceClass == resources.template_resource.TemplateResource:
# we can't validate a TemplateResource unless we instantiate
@ -819,9 +819,12 @@ class EngineService(service.Service):
# parameters into properties_schema.
continue
props = properties.Properties(ResourceClass.properties_schema,
res.get('Properties', {}),
context=cnxt)
props = properties.Properties(
ResourceClass.properties_schema,
res.get('Properties', {}),
parent_name=six.text_type(name),
context=cnxt,
section='Properties')
deletion_policy = res.get('DeletionPolicy', 'Delete')
try:
ResourceClass.validate_deletion_policy(deletion_policy)

View File

@ -157,6 +157,11 @@ class Template(collections.Mapping):
'''Return a dict of parameters.Schema objects for the parameters.'''
pass
@abc.abstractmethod
def get_section_name(self, section):
"""Return a correct section name."""
pass
@abc.abstractmethod
def parameters(self, stack_identifier, user_params, param_defaults=None):
'''Return a parameters.Parameters object for the stack.'''

View File

@ -133,8 +133,10 @@ class LaunchConfigurationTest(common.HeatTestCase):
self.patchobject(nova.NovaClientPlugin, 'get_server',
side_effect=exception.ServerNotFound(server='5678'))
msg = ("Property error : LaunchConfig: InstanceId Error validating "
"value '5678': The server (5678) could not be found.")
msg = ("Property error : "
"Resources.LaunchConfig.Properties.InstanceId: "
"Error validating value '5678': The server (5678) "
"could not be found.")
exc = self.assertRaises(exception.StackValidationFailed,
rsrc.validate)
self.assertIn(msg, six.text_type(exc))
@ -193,9 +195,10 @@ class LaunchConfigurationTest(common.HeatTestCase):
self.validate_launch_config, t,
stack, 'LaunchConfig')
excepted_error = ('Property error : LaunchConfig: BlockDeviceMappings '
'Property error : BlockDeviceMappings: 0 Property '
'error : 0: Property DeviceName not assigned')
excepted_error = (
'Property error : '
'Resources.LaunchConfig.Properties.BlockDeviceMappings[0]: '
'Property DeviceName not assigned')
self.assertIn(excepted_error, six.text_type(e))
self.m.VerifyAll()

View File

@ -220,8 +220,10 @@ class TestAutoScalingGroupValidation(common.HeatTestCase):
rsrc = stack['WebServerGroup']
self._stub_nova_server_get(not_found=True)
self.m.ReplayAll()
msg = ("Property error : WebServerGroup: InstanceId Error validating "
"value '5678': The server (5678) could not be found")
msg = ("Property error : "
"Resources.WebServerGroup.Properties.InstanceId: "
"Error validating value '5678': The server (5678) could "
"not be found.")
exc = self.assertRaises(exception.StackValidationFailed,
rsrc.validate)
self.assertIn(msg, six.text_type(exc))

View File

@ -203,7 +203,9 @@ class VolumeTest(vt_base.BaseVolumeTest):
ex = self.assertRaises(exception.StackValidationFailed,
self.create_volume, self.t, stack, 'DataVolume')
self.assertIn('Tags Property error', six.text_type(ex))
self.assertEqual("Property error : "
"Resources.DataVolume.Properties.Tags[0]: "
"Unknown Property Foo", six.text_type(ex))
self.m.VerifyAll()
@ -664,8 +666,8 @@ class VolumeTest(vt_base.BaseVolumeTest):
self.create_volume,
self.t, stack, 'DataVolume')
self.assertEqual(
"Property error : DataVolume: Size 0 is out of "
"range (min: 1, max: None)", six.text_type(error))
"Property error : Resources.DataVolume.Properties.Size: "
"0 is out of range (min: 1, max: None)", six.text_type(error))
def test_volume_attachment_updates_not_supported(self):
self.m.StubOutWithMock(nova.NovaClientPlugin, 'get_server')

View File

@ -103,8 +103,8 @@ class CinderVolumeTest(vt_base.BaseVolumeTest):
self.create_volume,
self.t, stack, 'volume')
self.assertEqual(
"Property error : volume: size 0 is out of "
"range (min: 1, max: None)", six.text_type(error))
"Property error : resources.volume.properties.size: "
"0 is out of range (min: 1, max: None)", six.text_type(error))
def test_cinder_create(self):
fv = vt_base.FakeVolume('creating')

View File

@ -376,8 +376,8 @@ class CeilometerAlarmTest(common.HeatTestCase):
error = self.assertRaises(exception.StackValidationFailed,
rsrc.validate)
self.assertEqual(
"Property error : MEMAlarmHigh: %s Value '60a' is not an "
"integer" % p, six.text_type(error))
"Property error : Resources.MEMAlarmHigh.Properties.%s: "
"Value '60a' is not an integer" % p, six.text_type(error))
def test_mem_alarm_high_not_integer_parameters(self):
snippet = template_format.parse(not_string_alarm_template)
@ -391,8 +391,9 @@ class CeilometerAlarmTest(common.HeatTestCase):
error = self.assertRaises(exception.StackValidationFailed,
rsrc.validate)
self.assertEqual(
"Property error : MEMAlarmHigh: %s int() argument must be "
"a string or a number, not 'list'" % p, six.text_type(error))
"Property error : Resources.MEMAlarmHigh.Properties.%s: "
"int() argument must be a string or a number, not "
"'list'" % p, six.text_type(error))
def test_mem_alarm_high_check_not_required_parameters(self):
snippet = template_format.parse(not_string_alarm_template)
@ -405,7 +406,8 @@ class CeilometerAlarmTest(common.HeatTestCase):
error = self.assertRaises(exception.StackValidationFailed,
rsrc.validate)
self.assertEqual(
"Property error : MEMAlarmHigh: Property meter_name not assigned",
"Property error : Resources.MEMAlarmHigh.Properties: "
"Property meter_name not assigned",
six.text_type(error))
for p in ('period', 'evaluation_periods', 'statistic',
@ -520,8 +522,9 @@ class CombinationAlarmTest(common.HeatTestCase):
error = self.assertRaises(exception.StackValidationFailed,
rsrc.validate)
self.assertEqual(
"Property error : CombinAlarm: alarm_ids length (0) is out of "
"range (min: 1, max: None)", six.text_type(error))
"Property error : Resources.CombinAlarm.Properties.alarm_ids: "
"length (0) is out of range (min: 1, max: None)",
six.text_type(error))
def test_update(self):
rsrc = self.create_alarm()

View File

@ -98,7 +98,8 @@ class GlanceImageTest(common.HeatTestCase):
)
image = stack['image']
image.t['Properties']['min_disk'] = -1
error_msg = 'min_disk -1 is out of range (min: 0, max: None)'
error_msg = ('Property error : resources.image.properties.min_disk: '
'-1 is out of range (min: 0, max: None)')
self._test_validate(image, error_msg)
def test_invalid_min_ram(self):
@ -110,7 +111,8 @@ class GlanceImageTest(common.HeatTestCase):
)
image = stack['image']
image.t['Properties']['min_ram'] = -1
error_msg = 'min_ram -1 is out of range (min: 0, max: None)'
error_msg = ('Property error : resources.image.properties.min_ram: '
'-1 is out of range (min: 0, max: None)')
self._test_validate(image, error_msg)
def test_miss_disk_format(self):
@ -134,7 +136,9 @@ class GlanceImageTest(common.HeatTestCase):
)
image = stack['image']
image.t['Properties']['disk_format'] = 'incorrect_format'
error_msg = ('disk_format "incorrect_format" is not an allowed value '
error_msg = ('Property error : '
'resources.image.properties.disk_format: '
'"incorrect_format" is not an allowed value '
'[ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, iso]')
self._test_validate(image, error_msg)
@ -159,8 +163,10 @@ class GlanceImageTest(common.HeatTestCase):
)
image = stack['image']
image.t['Properties']['container_format'] = 'incorrect_format'
error_msg = ('container_format "incorrect_format" is not an '
'allowed value [ami, ari, aki, bare, ova, ovf]')
error_msg = ('Property error : '
'resources.image.properties.container_format: '
'"incorrect_format" is not an allowed value '
'[ami, ari, aki, bare, ova, ovf]')
self._test_validate(image, error_msg)
def test_miss_location(self):

View File

@ -227,8 +227,9 @@ class InstancesTest(common.HeatTestCase):
exc = self.assertRaises(exception.StackValidationFailed,
instance.validate)
self.assertIn("VolumeId Error validating value '1234': "
"The Volume (1234) could not be found.",
self.assertIn("WebServer.Properties.Volumes[0].VolumeId: "
"Error validating value '1234': The Volume "
"(1234) could not be found.",
six.text_type(exc))
self.m.VerifyAll()
@ -322,9 +323,10 @@ class InstancesTest(common.HeatTestCase):
exc = self.assertRaises(exception.StackValidationFailed,
instance.validate)
excepted_error = ('Property error : WebServer: BlockDeviceMappings '
'Property error : BlockDeviceMappings: 0 Property '
'error : 0: Property DeviceName not assigned')
excepted_error = (
'Property error : '
'Resources.WebServer.Properties.BlockDeviceMappings[0]: '
'Property DeviceName not assigned')
self.assertIn(excepted_error, six.text_type(exc))
self.m.VerifyAll()
@ -382,9 +384,9 @@ class InstancesTest(common.HeatTestCase):
create = scheduler.TaskRunner(instance.create)
error = self.assertRaises(exception.ResourceFailure, create)
self.assertEqual(
"StackValidationFailed: Property error : WebServer: "
"ImageId Error validating value 'Slackware': "
"The Image (Slackware) could not be found.",
"StackValidationFailed: Property error : "
"WebServer.Properties.ImageId: Error validating value "
"'Slackware': The Image (Slackware) could not be found.",
six.text_type(error))
self.m.VerifyAll()
@ -409,9 +411,9 @@ class InstancesTest(common.HeatTestCase):
create = scheduler.TaskRunner(instance.create)
error = self.assertRaises(exception.ResourceFailure, create)
self.assertEqual(
'StackValidationFailed: Property error : WebServer: '
'ImageId Multiple physical resources were '
'found with name (CentOS 5.2).',
'StackValidationFailed: Property error : '
'WebServer.Properties.ImageId: Multiple physical '
'resources were found with name (CentOS 5.2).',
six.text_type(error))
self.m.VerifyAll()
@ -433,8 +435,8 @@ class InstancesTest(common.HeatTestCase):
create = scheduler.TaskRunner(instance.create)
error = self.assertRaises(exception.ResourceFailure, create)
self.assertEqual(
'StackValidationFailed: Property error : WebServer: '
'ImageId 404 (HTTP 404)',
'StackValidationFailed: Property error : '
'WebServer.Properties.ImageId: 404 (HTTP 404)',
six.text_type(error))
self.m.VerifyAll()

View File

@ -103,11 +103,11 @@ class NovaKeyPairTest(common.HeatTestCase):
definition = stack.t.resource_definitions(stack)['kp']
kp_res = nova_keypair.KeyPair('kp', definition, stack)
self.m.ReplayAll()
create = scheduler.TaskRunner(kp_res.create)
error = self.assertRaises(exception.ResourceFailure, create)
error = self.assertRaises(exception.StackValidationFailed,
kp_res.validate)
self.assertIn("Property error", six.text_type(error))
self.assertIn("name length (0) is out of range (min: 1, max: 255)",
six.text_type(error))
self.assertIn("kp.properties.name: length (0) is out of "
"range (min: 1, max: 255)", six.text_type(error))
self.m.VerifyAll()
def test_create_key_excess_name_length(self):
@ -119,11 +119,11 @@ class NovaKeyPairTest(common.HeatTestCase):
definition = stack.t.resource_definitions(stack)['kp']
kp_res = nova_keypair.KeyPair('kp', definition, stack)
self.m.ReplayAll()
create = scheduler.TaskRunner(kp_res.create)
error = self.assertRaises(exception.ResourceFailure, create)
error = self.assertRaises(exception.StackValidationFailed,
kp_res.validate)
self.assertIn("Property error", six.text_type(error))
self.assertIn("name length (256) is out of range (min: 1, max: 255)",
six.text_type(error))
self.assertIn("kp.properties.name: length (256) is out of "
"range (min: 1, max: 255)", six.text_type(error))
self.m.VerifyAll()
def test_delete_key(self):

View File

@ -883,8 +883,8 @@ class PropertyTest(common.HeatTestCase):
p = properties.Property({'Type': 'Map', 'Schema': map_schema})
ex = self.assertRaises(exception.StackValidationFailed,
p.get_value, {'valid': 'fish'}, True)
self.assertEqual('Property error : valid "fish" is not '
'a valid boolean', six.text_type(ex))
self.assertEqual('Property error : valid: "fish" is not a '
'valid boolean', six.text_type(ex))
def test_map_schema_missing_data(self):
map_schema = {'valid': {'Type': 'Boolean'}}
@ -915,8 +915,8 @@ class PropertyTest(common.HeatTestCase):
ex = self.assertRaises(exception.StackValidationFailed,
p.get_value,
[{'valid': 'True'}, {'valid': 'fish'}], True)
self.assertEqual('Property error : 1 Property error : 1: valid '
'"fish" is not a valid boolean', six.text_type(ex))
self.assertEqual('Property error : [1].valid: "fish" is not '
'a valid boolean', six.text_type(ex))
def test_list_schema_int_good(self):
list_schema = {'Type': 'Integer'}
@ -928,7 +928,7 @@ class PropertyTest(common.HeatTestCase):
p = properties.Property({'Type': 'List', 'Schema': list_schema})
ex = self.assertRaises(exception.StackValidationFailed,
p.get_value, [42, 'fish'], True)
self.assertEqual("Property error : 1 Value 'fish' is not "
self.assertEqual("Property error : [1]: Value 'fish' is not "
"an integer", six.text_type(ex))
@ -1057,16 +1057,6 @@ class PropertiesTest(common.HeatTestCase):
props = properties.Properties(schema, {'foo': None})
self.assertEqual(['one', 'two'], props['foo'])
def test_bad_resolver(self):
schema = {'foo': {'Type': 'String', 'Default': 'bar'}}
def bad_resolver(prop):
raise Exception('resolution failed!')
props = properties.Properties(schema, {'foo': 'baz'}, bad_resolver)
err = self.assertRaises(ValueError, props.get, 'foo')
self.assertEqual('foo resolution failed!', six.text_type(err))
def test_resolve_returns_none(self):
schema = {'foo': {'Type': 'String', "MinLength": "5"}}
@ -1547,14 +1537,14 @@ class PropertiesValidationTest(common.HeatTestCase):
schema = {'foo': {'Type': 'String'}}
props = properties.Properties(schema, {'foo': ['foo', 'bar']})
ex = self.assertRaises(exception.StackValidationFailed, props.validate)
self.assertEqual('Property error : foo Value must be a string',
self.assertEqual('Property error : foo: Value must be a string',
six.text_type(ex))
def test_dict_instead_string(self):
schema = {'foo': {'Type': 'String'}}
props = properties.Properties(schema, {'foo': {'foo': 'bar'}})
ex = self.assertRaises(exception.StackValidationFailed, props.validate)
self.assertEqual('Property error : foo Value must be a string',
self.assertEqual('Property error : foo: Value must be a string',
six.text_type(ex))
def test_none_string(self):
@ -1723,8 +1713,8 @@ class PropertiesValidationTest(common.HeatTestCase):
props = properties.Properties(schema, invalid_data)
ex = self.assertRaises(exception.StackValidationFailed,
props.validate)
self.assertEqual('Property error : foo Property error : foo: 0 '
'Unknown Property bar', six.text_type(ex))
self.assertEqual('Property error : foo[0]: Unknown Property bar',
six.text_type(ex))
def test_nested_properties_schema_invalid_property_in_map(self):
child_schema = {'Key': {'Type': 'String',
@ -1743,8 +1733,8 @@ class PropertiesValidationTest(common.HeatTestCase):
props = properties.Properties(schema, invalid_data)
ex = self.assertRaises(exception.StackValidationFailed,
props.validate)
self.assertEqual('Property error : foo Property error : foo: boo '
'Unknown Property bar', six.text_type(ex))
self.assertEqual('Property error : foo.boo: Unknown Property bar',
six.text_type(ex))
def test_more_nested_properties_schema_invalid_property_in_list(self):
nested_child_schema = {'Key': {'Type': 'String',
@ -1761,8 +1751,7 @@ class PropertiesValidationTest(common.HeatTestCase):
props = properties.Properties(schema, invalid_data)
ex = self.assertRaises(exception.StackValidationFailed,
props.validate)
self.assertEqual('Property error : foo Property error : foo: 0 '
'Property error : 0: doo Unknown Property bar',
self.assertEqual('Property error : foo[0].doo: Unknown Property bar',
six.text_type(ex))
def test_more_nested_properties_schema_invalid_property_in_map(self):
@ -1780,8 +1769,7 @@ class PropertiesValidationTest(common.HeatTestCase):
props = properties.Properties(schema, invalid_data)
ex = self.assertRaises(exception.StackValidationFailed,
props.validate)
self.assertEqual('Property error : foo Property error : foo: boo '
'Property error : boo: doo Unknown Property bar',
self.assertEqual('Property error : foo.boo.doo: Unknown Property bar',
six.text_type(ex))
def test_schema_to_template_empty_schema(self):

View File

@ -223,7 +223,7 @@ Resources:
'''
exc = self.assertRaises(exception.StackValidationFailed,
self.create_stack, template_random_string)
self.assertIn('length 513 is out of range (min: 1, max: 512)',
self.assertIn('513 is out of range (min: 1, max: 512)',
six.text_type(exc))

View File

@ -543,7 +543,8 @@ class ResourceTest(common.HeatTestCase):
tmpl = rsrc_defn.ResourceDefinition(rname, 'Foo', {})
res = generic_rsrc.ResourceWithRequiredProps(rname, tmpl, self.stack)
estr = 'Property error : test_resource: Property Foo not assigned'
estr = ('Property error : test_resource.Properties: '
'Property Foo not assigned')
create = scheduler.TaskRunner(res.create)
err = self.assertRaises(exception.ResourceFailure, create)
self.assertIn(estr, six.text_type(err))
@ -555,7 +556,8 @@ class ResourceTest(common.HeatTestCase):
{'Food': 'abc'})
res = generic_rsrc.ResourceWithProps(rname, tmpl, self.stack)
estr = 'StackValidationFailed: Unknown Property Food'
estr = ('StackValidationFailed: Property error : '
'test_resource.Properties: Unknown Property Food')
create = scheduler.TaskRunner(res.create)
err = self.assertRaises(exception.ResourceFailure, create)
self.assertIn(estr, six.text_type(err))
@ -1444,8 +1446,9 @@ class ResourceDependenciesTest(common.HeatTestCase):
stack = parser.Stack(utils.dummy_context(), 'test', tmpl)
ex = self.assertRaises(exception.StackValidationFailed,
stack.validate)
expected = "FooInt Value 'notanint' is not an integer"
self.assertIn(expected, six.text_type(ex))
self.assertIn("Property error : resources.bar.properties.FooInt: "
"Value 'notanint' is not an integer",
six.text_type(ex))
# You can turn off value validation via strict_validate
stack_novalidate = parser.Stack(utils.dummy_context(), 'test', tmpl,

View File

@ -328,7 +328,7 @@ class ResourceGroupTest(common.HeatTestCase):
resg = resource_group.ResourceGroup('test', snip, stack)
exc = self.assertRaises(exception.StackValidationFailed,
resg.validate)
errstr = "removal_policies \"'notallowed'\" is not a list"
errstr = "removal_policies: \"'notallowed'\" is not a list"
self.assertIn(errstr, six.text_type(exc))
def test_invalid_removal_policies_nomap(self):

View File

@ -188,8 +188,10 @@ class SaharaNodeGroupTemplateTest(common.HeatTestCase):
self.patchobject(ngt, 'is_using_neutron').return_value = False
ex = self.assertRaises(exception.StackValidationFailed, ngt.validate)
self.assertEqual(u"Property error : node-group: flavor Error "
u"validating value u'm1.large'", six.text_type(ex))
self.assertEqual(u"Property error : "
u"resources.node-group.properties.flavor: "
u"Error validating value u'm1.large'",
six.text_type(ex))
class SaharaClusterTemplateTest(common.HeatTestCase):

View File

@ -425,8 +425,8 @@ class ServersTest(common.HeatTestCase):
create = scheduler.TaskRunner(server.create)
error = self.assertRaises(exception.ResourceFailure, create)
self.assertEqual(
"StackValidationFailed: Property error : WebServer: "
"image Error validating value 'Slackware': "
"StackValidationFailed: Property error : "
"WebServer.Properties.image: Error validating value 'Slackware': "
"The Image (Slackware) could not be found.",
six.text_type(error))
@ -450,9 +450,9 @@ class ServersTest(common.HeatTestCase):
create = scheduler.TaskRunner(server.create)
error = self.assertRaises(exception.ResourceFailure, create)
self.assertEqual(
'StackValidationFailed: Property error : WebServer: '
'image Multiple physical resources were '
'found with name (CentOS 5.2).',
'StackValidationFailed: Property error : '
'WebServer.Properties.image: Multiple physical '
'resources were found with name (CentOS 5.2).',
six.text_type(error))
self.m.VerifyAll()
@ -474,8 +474,8 @@ class ServersTest(common.HeatTestCase):
create = scheduler.TaskRunner(server.create)
error = self.assertRaises(exception.ResourceFailure, create)
self.assertEqual(
"StackValidationFailed: Property error : WebServer: "
"image Error validating value '1': "
"StackValidationFailed: Property error : "
"WebServer.Properties.image: Error validating value '1': "
"The Image (1) could not be found.",
six.text_type(error))
@ -1049,9 +1049,9 @@ class ServersTest(common.HeatTestCase):
error = self.assertRaises(exception.StackValidationFailed,
server.validate)
self.assertEqual(
"Property error : WebServer: key_name Error validating "
"value 'test2': The Key (test2) could not be found.",
six.text_type(error))
"Property error : Resources.WebServer.Properties.key_name: "
"Error validating value 'test2': The Key (test2) could not "
"be found.", six.text_type(error))
self.m.VerifyAll()
def test_server_validate_with_networks(self):
@ -3071,8 +3071,9 @@ class ServersTest(common.HeatTestCase):
# update
updater = scheduler.TaskRunner(server.update, update_template)
err = self.assertRaises(exception.ResourceFailure, updater)
self.assertEqual('StackValidationFailed: Property error : WebServer: '
'image The Image (Update Image) could not be found.',
self.assertEqual('StackValidationFailed: Property error : '
'WebServer.Properties.image: The Image '
'(Update Image) could not be found.',
six.text_type(err))
self.m.VerifyAll()

View File

@ -217,8 +217,8 @@ class SoftwareComponentValidationTest(common.HeatTestCase):
tool: script
''',
err=exc.StackValidationFailed,
err_msg='actions length (0) is out of range '
'(min: 1, max: None)')
err_msg='component.properties.configs[0].actions: '
'length (0) is out of range (min: 1, max: None)')
),
(
'multiple_configs_per_action_single',

View File

@ -124,6 +124,9 @@ class TestTemplatePluginManager(common.HeatTestCase):
def param_schemata(self):
pass
def get_section_name(self, section):
pass
def parameters(self, stack_identifier, user_params):
pass

View File

@ -160,8 +160,8 @@ class TroveClusterTest(common.HeatTestCase):
self.rsrc_defn['Properties']['instances'][0]['flavor'] = 'm1.small'
tc = trove_cluster.TroveCluster('cluster', self.rsrc_defn, self.stack)
ex = self.assertRaises(exception.StackValidationFailed, tc.validate)
error_msg = ("Property error : cluster: instances Property error : "
"instances: 0 Property error : 0: flavor "
error_msg = ("Property error : "
"resources.cluster.properties.instances[0].flavor: "
"Error validating value 'm1.small': "
"The Flavor ID (m1.small) could not be found.")
self.assertEqual(error_msg, six.text_type(ex))

View File

@ -1034,7 +1034,8 @@ class validateTest(common.HeatTestCase):
t = template_format.parse(test_template_invalid_property)
engine = service.EngineService('a', 't')
res = dict(engine.validate_template(None, t, {}))
self.assertEqual({'Error': 'Unknown Property UnknownProperty'}, res)
self.assertEqual({'Error': 'Property error : WikiDatabase.Properties: '
'Unknown Property UnknownProperty'}, res)
def test_invalid_resources(self):
t = template_format.parse(test_template_invalid_resources)
@ -1083,7 +1084,8 @@ class validateTest(common.HeatTestCase):
engine = service.EngineService('a', 't')
res = dict(engine.validate_template(None, t, {}))
self.assertEqual(
{'Error': 'Property SourceDestCheck not implemented yet'},
{'Error': 'Property error : WikiDatabase.Properties: '
'Property SourceDestCheck not implemented yet'},
res)
def test_invalid_deletion_policy(self):

View File

@ -98,7 +98,7 @@ resources:
# Prove validation works for non-zero create/update
template_two_nested = self.template.replace("count: 0", "count: 2")
expected_err = "length Value 'BAD' is not an integer"
expected_err = "Value 'BAD' is not an integer"
ex = self.assertRaises(exc.HTTPBadRequest, self.update_stack,
stack_identifier, template_two_nested,
environment=env, files=files)