Merge "Improve StackValidationFailed response in properties"

This commit is contained in:
Jenkins 2015-04-20 03:54:08 +00:00 committed by Gerrit Code Review
commit bc60a1543b
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)) return dict((name, parameters.Schema.from_dict(name, schema))
for name, schema in six.iteritems(params)) 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): def parameters(self, stack_identifier, user_params, param_defaults=None):
return parameters.Parameters(stack_identifier, self, return parameters.Parameters(stack_identifier, self,
user_params=user_params, user_params=user_params,

View File

@ -56,6 +56,12 @@ class HOTemplate20130523(template.Template):
cfn_template.CfnTemplate.RESOURCES: RESOURCES, cfn_template.CfnTemplate.RESOURCES: RESOURCES,
cfn_template.CfnTemplate.OUTPUTS: OUTPUTS} 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 = { functions = {
'Fn::GetAZs': cfn_funcs.GetAZs, 'Fn::GetAZs': cfn_funcs.GetAZs,
'get_param': hot_funcs.GetParam, 'get_param': hot_funcs.GetParam,
@ -157,15 +163,14 @@ class HOTemplate20130523(template.Template):
def _translate_resources(self, resources): def _translate_resources(self, resources):
"""Get the resources of the template translated into CFN format.""" """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, 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): def _translate_outputs(self, outputs):
"""Get the outputs of the template translated into CFN format.""" """Get the outputs of the template translated into CFN format."""

View File

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

View File

@ -874,7 +874,18 @@ class Resource(object):
function.validate(self.t) function.validate(self.t)
self.validate_deletion_policy(self.t.deletion_policy()) 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 @classmethod
def validate_deletion_policy(cls, policy): def validate_deletion_policy(cls, policy):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -99,7 +99,8 @@ class GlanceImageTest(common.HeatTestCase):
) )
image = stack['image'] image = stack['image']
image.t['Properties']['min_disk'] = -1 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) self._test_validate(image, error_msg)
def test_invalid_min_ram(self): def test_invalid_min_ram(self):
@ -111,7 +112,8 @@ class GlanceImageTest(common.HeatTestCase):
) )
image = stack['image'] image = stack['image']
image.t['Properties']['min_ram'] = -1 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) self._test_validate(image, error_msg)
def test_miss_disk_format(self): def test_miss_disk_format(self):
@ -135,7 +137,9 @@ class GlanceImageTest(common.HeatTestCase):
) )
image = stack['image'] image = stack['image']
image.t['Properties']['disk_format'] = 'incorrect_format' 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]') '[ami, ari, aki, vhd, vmdk, raw, qcow2, vdi, iso]')
self._test_validate(image, error_msg) self._test_validate(image, error_msg)
@ -160,8 +164,10 @@ class GlanceImageTest(common.HeatTestCase):
) )
image = stack['image'] image = stack['image']
image.t['Properties']['container_format'] = 'incorrect_format' image.t['Properties']['container_format'] = 'incorrect_format'
error_msg = ('container_format "incorrect_format" is not an ' error_msg = ('Property error : '
'allowed value [ami, ari, aki, bare, ova, ovf]') 'resources.image.properties.container_format: '
'"incorrect_format" is not an allowed value '
'[ami, ari, aki, bare, ova, ovf]')
self._test_validate(image, error_msg) self._test_validate(image, error_msg)
def test_miss_location(self): def test_miss_location(self):

View File

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

View File

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

View File

@ -882,8 +882,8 @@ class PropertyTest(common.HeatTestCase):
p = properties.Property({'Type': 'Map', 'Schema': map_schema}) p = properties.Property({'Type': 'Map', 'Schema': map_schema})
ex = self.assertRaises(exception.StackValidationFailed, ex = self.assertRaises(exception.StackValidationFailed,
p.get_value, {'valid': 'fish'}, True) p.get_value, {'valid': 'fish'}, True)
self.assertEqual('Property error : valid "fish" is not ' self.assertEqual('Property error : valid: "fish" is not a '
'a valid boolean', six.text_type(ex)) 'valid boolean', six.text_type(ex))
def test_map_schema_missing_data(self): def test_map_schema_missing_data(self):
map_schema = {'valid': {'Type': 'Boolean'}} map_schema = {'valid': {'Type': 'Boolean'}}
@ -914,8 +914,8 @@ class PropertyTest(common.HeatTestCase):
ex = self.assertRaises(exception.StackValidationFailed, ex = self.assertRaises(exception.StackValidationFailed,
p.get_value, p.get_value,
[{'valid': 'True'}, {'valid': 'fish'}], True) [{'valid': 'True'}, {'valid': 'fish'}], True)
self.assertEqual('Property error : 1 Property error : 1: valid ' self.assertEqual('Property error : [1].valid: "fish" is not '
'"fish" is not a valid boolean', six.text_type(ex)) 'a valid boolean', six.text_type(ex))
def test_list_schema_int_good(self): def test_list_schema_int_good(self):
list_schema = {'Type': 'Integer'} list_schema = {'Type': 'Integer'}
@ -927,7 +927,7 @@ class PropertyTest(common.HeatTestCase):
p = properties.Property({'Type': 'List', 'Schema': list_schema}) p = properties.Property({'Type': 'List', 'Schema': list_schema})
ex = self.assertRaises(exception.StackValidationFailed, ex = self.assertRaises(exception.StackValidationFailed,
p.get_value, [42, 'fish'], True) 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)) "an integer", six.text_type(ex))
@ -1056,16 +1056,6 @@ class PropertiesTest(common.HeatTestCase):
props = properties.Properties(schema, {'foo': None}) props = properties.Properties(schema, {'foo': None})
self.assertEqual(['one', 'two'], props['foo']) 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): def test_resolve_returns_none(self):
schema = {'foo': {'Type': 'String', "MinLength": "5"}} schema = {'foo': {'Type': 'String', "MinLength": "5"}}
@ -1543,14 +1533,14 @@ class PropertiesValidationTest(common.HeatTestCase):
schema = {'foo': {'Type': 'String'}} schema = {'foo': {'Type': 'String'}}
props = properties.Properties(schema, {'foo': ['foo', 'bar']}) props = properties.Properties(schema, {'foo': ['foo', 'bar']})
ex = self.assertRaises(exception.StackValidationFailed, props.validate) 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)) six.text_type(ex))
def test_dict_instead_string(self): def test_dict_instead_string(self):
schema = {'foo': {'Type': 'String'}} schema = {'foo': {'Type': 'String'}}
props = properties.Properties(schema, {'foo': {'foo': 'bar'}}) props = properties.Properties(schema, {'foo': {'foo': 'bar'}})
ex = self.assertRaises(exception.StackValidationFailed, props.validate) 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)) six.text_type(ex))
def test_none_string(self): def test_none_string(self):
@ -1717,8 +1707,8 @@ class PropertiesValidationTest(common.HeatTestCase):
props = properties.Properties(schema, invalid_data) props = properties.Properties(schema, invalid_data)
ex = self.assertRaises(exception.StackValidationFailed, ex = self.assertRaises(exception.StackValidationFailed,
props.validate) props.validate)
self.assertEqual('Property error : foo Property error : foo: 0 ' self.assertEqual('Property error : foo[0]: Unknown Property bar',
'Unknown Property bar', six.text_type(ex)) six.text_type(ex))
def test_nested_properties_schema_invalid_property_in_map(self): def test_nested_properties_schema_invalid_property_in_map(self):
child_schema = {'Key': {'Type': 'String', child_schema = {'Key': {'Type': 'String',
@ -1736,8 +1726,8 @@ class PropertiesValidationTest(common.HeatTestCase):
props = properties.Properties(schema, invalid_data) props = properties.Properties(schema, invalid_data)
ex = self.assertRaises(exception.StackValidationFailed, ex = self.assertRaises(exception.StackValidationFailed,
props.validate) props.validate)
self.assertEqual('Property error : foo Property error : foo: boo ' self.assertEqual('Property error : foo.boo: Unknown Property bar',
'Unknown Property bar', six.text_type(ex)) six.text_type(ex))
def test_more_nested_properties_schema_invalid_property_in_list(self): def test_more_nested_properties_schema_invalid_property_in_list(self):
nested_child_schema = {'Key': {'Type': 'String', nested_child_schema = {'Key': {'Type': 'String',
@ -1754,8 +1744,7 @@ class PropertiesValidationTest(common.HeatTestCase):
props = properties.Properties(schema, invalid_data) props = properties.Properties(schema, invalid_data)
ex = self.assertRaises(exception.StackValidationFailed, ex = self.assertRaises(exception.StackValidationFailed,
props.validate) props.validate)
self.assertEqual('Property error : foo Property error : foo: 0 ' self.assertEqual('Property error : foo[0].doo: Unknown Property bar',
'Property error : 0: doo Unknown Property bar',
six.text_type(ex)) six.text_type(ex))
def test_more_nested_properties_schema_invalid_property_in_map(self): def test_more_nested_properties_schema_invalid_property_in_map(self):
@ -1773,8 +1762,7 @@ class PropertiesValidationTest(common.HeatTestCase):
props = properties.Properties(schema, invalid_data) props = properties.Properties(schema, invalid_data)
ex = self.assertRaises(exception.StackValidationFailed, ex = self.assertRaises(exception.StackValidationFailed,
props.validate) props.validate)
self.assertEqual('Property error : foo Property error : foo: boo ' self.assertEqual('Property error : foo.boo.doo: Unknown Property bar',
'Property error : boo: doo Unknown Property bar',
six.text_type(ex)) six.text_type(ex))
def test_schema_to_template_empty_schema(self): def test_schema_to_template_empty_schema(self):

View File

@ -223,7 +223,7 @@ Resources:
''' '''
exc = self.assertRaises(exception.StackValidationFailed, exc = self.assertRaises(exception.StackValidationFailed,
self.create_stack, template_random_string) 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)) six.text_type(exc))

View File

@ -547,7 +547,8 @@ class ResourceTest(common.HeatTestCase):
tmpl = rsrc_defn.ResourceDefinition(rname, 'Foo', {}) tmpl = rsrc_defn.ResourceDefinition(rname, 'Foo', {})
res = generic_rsrc.ResourceWithRequiredProps(rname, tmpl, self.stack) 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) create = scheduler.TaskRunner(res.create)
err = self.assertRaises(exception.ResourceFailure, create) err = self.assertRaises(exception.ResourceFailure, create)
self.assertIn(estr, six.text_type(err)) self.assertIn(estr, six.text_type(err))
@ -559,7 +560,8 @@ class ResourceTest(common.HeatTestCase):
{'Food': 'abc'}) {'Food': 'abc'})
res = generic_rsrc.ResourceWithProps(rname, tmpl, self.stack) 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) create = scheduler.TaskRunner(res.create)
err = self.assertRaises(exception.ResourceFailure, create) err = self.assertRaises(exception.ResourceFailure, create)
self.assertIn(estr, six.text_type(err)) self.assertIn(estr, six.text_type(err))
@ -1506,8 +1508,9 @@ class ResourceDependenciesTest(common.HeatTestCase):
stack = parser.Stack(utils.dummy_context(), 'test', tmpl) stack = parser.Stack(utils.dummy_context(), 'test', tmpl)
ex = self.assertRaises(exception.StackValidationFailed, ex = self.assertRaises(exception.StackValidationFailed,
stack.validate) stack.validate)
expected = "FooInt Value 'notanint' is not an integer" self.assertIn("Property error : resources.bar.properties.FooInt: "
self.assertIn(expected, six.text_type(ex)) "Value 'notanint' is not an integer",
six.text_type(ex))
# You can turn off value validation via strict_validate # You can turn off value validation via strict_validate
stack_novalidate = parser.Stack(utils.dummy_context(), 'test', tmpl, 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) resg = resource_group.ResourceGroup('test', snip, stack)
exc = self.assertRaises(exception.StackValidationFailed, exc = self.assertRaises(exception.StackValidationFailed,
resg.validate) 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)) self.assertIn(errstr, six.text_type(exc))
def test_invalid_removal_policies_nomap(self): 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 self.patchobject(ngt, 'is_using_neutron').return_value = False
ex = self.assertRaises(exception.StackValidationFailed, ngt.validate) ex = self.assertRaises(exception.StackValidationFailed, ngt.validate)
self.assertEqual(u"Property error : node-group: flavor Error " self.assertEqual(u"Property error : "
u"validating value u'm1.large'", six.text_type(ex)) u"resources.node-group.properties.flavor: "
u"Error validating value u'm1.large'",
six.text_type(ex))
class SaharaClusterTemplateTest(common.HeatTestCase): class SaharaClusterTemplateTest(common.HeatTestCase):

View File

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

View File

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

View File

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

View File

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

View File

@ -1035,7 +1035,8 @@ class validateTest(common.HeatTestCase):
t = template_format.parse(test_template_invalid_property) t = template_format.parse(test_template_invalid_property)
engine = service.EngineService('a', 't') engine = service.EngineService('a', 't')
res = dict(engine.validate_template(None, 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): def test_invalid_resources(self):
t = template_format.parse(test_template_invalid_resources) t = template_format.parse(test_template_invalid_resources)
@ -1084,7 +1085,8 @@ class validateTest(common.HeatTestCase):
engine = service.EngineService('a', 't') engine = service.EngineService('a', 't')
res = dict(engine.validate_template(None, t, {})) res = dict(engine.validate_template(None, t, {}))
self.assertEqual( self.assertEqual(
{'Error': 'Property SourceDestCheck not implemented yet'}, {'Error': 'Property error : WikiDatabase.Properties: '
'Property SourceDestCheck not implemented yet'},
res) res)
def test_invalid_deletion_policy(self): def test_invalid_deletion_policy(self):

View File

@ -98,7 +98,7 @@ resources:
# Prove validation works for non-zero create/update # Prove validation works for non-zero create/update
template_two_nested = self.template.replace("count: 0", "count: 2") 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, ex = self.assertRaises(exc.HTTPBadRequest, self.update_stack,
stack_identifier, template_two_nested, stack_identifier, template_two_nested,
environment=env, files=files) environment=env, files=files)