Enable new translation mechanism

Disable old translation mechanism and enable new
one. Integrate it with Properties class and correct
tests.

Change-Id: I953a52e9b165d3ea4fb2fc57ceea8083c7f8f30c
Closes-bug: #1620859
This commit is contained in:
Peter Razumovsky 2017-01-13 17:49:12 +04:00
parent 157ede1949
commit e159868d2f
14 changed files with 190 additions and 530 deletions

View File

@ -24,6 +24,7 @@ from heat.engine import function
from heat.engine.hot import parameters as hot_param
from heat.engine import parameters
from heat.engine import support
from heat.engine import translation as trans
SCHEMA_KEYS = (
REQUIRED, IMPLEMENTED, DEFAULT, TYPE, SCHEMA,
@ -280,14 +281,16 @@ class Property(object):
raise ValueError(_('Value must be a string'))
return value
def _get_children(self, child_values, keys=None, validate=False):
def _get_children(self, child_values, keys=None, validate=False,
translation=None):
if self.schema.schema is not None:
if keys is None:
keys = list(self.schema.schema)
schemata = dict((k, self.schema.schema[k]) for k in keys)
properties = Properties(schemata, dict(child_values),
context=self.context,
parent_name=self.path)
parent_name=self.path,
translation=translation)
if validate:
properties.validate()
@ -295,7 +298,7 @@ class Property(object):
else:
return child_values
def _get_map(self, value, validate=False):
def _get_map(self, value, validate=False, translation=None):
if value is None:
value = self.default() if self.has_default() else {}
if not isinstance(value, collections.Mapping):
@ -310,9 +313,10 @@ class Property(object):
raise TypeError(_('"%s" is not a map') % value)
return dict(self._get_children(six.iteritems(value),
validate=validate))
validate=validate,
translation=translation))
def _get_list(self, value, validate=False):
def _get_list(self, value, validate=False, translation=None):
if value is None:
value = self.has_default() and self.default() or []
if self.schema.allow_conversion and isinstance(value,
@ -324,7 +328,8 @@ class Property(object):
return [v[1] for v in self._get_children(enumerate(value),
range(len(value)),
validate)]
validate=validate,
translation=translation)]
def _get_bool(self, value):
"""Get value for boolean property.
@ -344,7 +349,8 @@ class Property(object):
raise TypeError(_('"%s" is not a valid boolean') % value)
def get_value(self, value, validate=False, template=None):
def get_value(self, value, validate=False, template=None,
translation=None):
"""Get value from raw value and sanitize according to data type."""
t = self.type()
@ -355,9 +361,9 @@ class Property(object):
elif t == Schema.NUMBER:
_value = self._get_number(value)
elif t == Schema.MAP:
_value = self._get_map(value, validate)
_value = self._get_map(value, validate, translation)
elif t == Schema.LIST:
_value = self._get_list(value, validate)
_value = self._get_list(value, validate, translation)
elif t == Schema.BOOLEAN:
_value = self._get_bool(value)
elif t == Schema.ANY:
@ -373,7 +379,7 @@ class Property(object):
class Properties(collections.Mapping):
def __init__(self, schema, data, resolver=lambda d: d, parent_name=None,
context=None, section=None):
context=None, section=None, translation=None):
self.props = dict((k, Property(s, k, context, path=parent_name))
for k, s in schema.items())
self.resolve = resolver
@ -381,6 +387,11 @@ class Properties(collections.Mapping):
self.error_prefix = [section] if section is not None else []
self.parent_name = parent_name
self.context = context
self.translation = (trans.Translation(properties=self)
if translation is None else translation)
def update_translation(self, rules, client_resolve=True):
self.translation.set_rules(rules, client_resolve=client_resolve)
@staticmethod
def schema_from_params(params_snippet):
@ -402,6 +413,9 @@ class Properties(collections.Mapping):
raise exception.StackValidationFailed(message=msg)
for (key, prop) in self.props.items():
if (self.translation.is_deleted(prop.path) or
self.translation.is_replaced(prop.path)):
continue
if with_value:
try:
self._get_property_value(key,
@ -446,6 +460,9 @@ class Properties(collections.Mapping):
raise KeyError(_('Invalid Property %s') % key)
prop = self.props[key]
if (self.translation.is_deleted(prop.path) or
self.translation.is_replaced(prop.path)):
return
if key in self.data:
try:
unresolved_value = self.data[key]
@ -454,7 +471,14 @@ class Properties(collections.Mapping):
validate = False
value = self.resolve(unresolved_value)
return prop.get_value(value, validate, template=template)
if self.translation.has_translation(prop.path):
value = self.translation.translate(prop.path,
value,
self.data)
return prop.get_value(value, validate, template=template,
translation=self.translation)
# 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.
@ -471,10 +495,17 @@ class Properties(collections.Mapping):
raise KeyError(_('Invalid Property %s') % key)
prop = self.props[key]
if key in self.data:
if not self.translation.is_deleted(prop.path) and key in self.data:
return self.get_user_value(key, validate, template=template)
elif self.translation.has_translation(prop.path):
value = self.translation.translate(prop.path, prop_data=self.data,
validate=validate,
template=template)
if value is not None or prop.has_default():
return prop.get_value(value)
elif prop.has_default():
return prop.get_value(None, validate, template=template)
return prop.get_value(None, validate, template=template,
translation=self.translation)
elif prop.required():
raise ValueError(_('Property %s not assigned') % key)

View File

@ -219,8 +219,7 @@ class Resource(status.ResourceStatus):
self.t = definition
# Only translate in cases where resource_validate is True
# ex. for template-validate
self.reparse(translate=self.stack.resource_validate,
client_resolve=False)
self.reparse(client_resolve=False)
self.update_policy = self.t.update_policy(self.update_policy_schema,
self.context)
self._update_allowed_properties = self.calc_update_allowed(
@ -372,7 +371,7 @@ class Resource(status.ResourceStatus):
{'status': self.COMPLETE, 'replaced_by': self.replaced_by})
return new_rs.id
def reparse(self, translate=True, client_resolve=True):
def reparse(self, client_resolve=True):
"""Reparse the resource properties.
Optional translate flag for property translation and
@ -381,8 +380,7 @@ class Resource(status.ResourceStatus):
"""
self.properties = self.t.properties(self.properties_schema,
self.context)
if translate:
self.translate_properties(self.properties, client_resolve)
self.translate_properties(self.properties, client_resolve)
def calc_update_allowed(self, props):
update_allowed_set = set(self.update_allowed_properties)
@ -1081,28 +1079,18 @@ class Resource(status.ResourceStatus):
def translation_rules(self, properties):
"""Return specified rules for resource."""
return []
def translate_properties(self, properties,
client_resolve=True):
"""Translates properties with resource specific rules.
"""Set resource specific rules for properties translation.
The properties parameter is a properties object and the
optional client_resolve flag is to specify whether to
do 'RESOLVE' translation with client lookup.
"""
rules = self.translation_rules(properties) or []
for rule in rules:
try:
rule.execute_rule(client_resolve)
except exception.ResourcePropertyConflict as ex:
path = [self.stack.t.RESOURCES, self.name,
self.stack.t.get_section_name(
self.stack.t.RES_PROPERTIES)]
raise exception.StackValidationFailed(
error='Property error',
path=path,
message=ex.message
)
properties.update_translation(rules, client_resolve=client_resolve)
def cancel_grace_period(self):
if self.status != self.IN_PROGRESS:
@ -1279,6 +1267,7 @@ class Resource(status.ResourceStatus):
# Regenerate the schema, else validation would fail
self.regenerate_info_schema(after)
after.set_translation_rules(self.translation_rules(self.properties))
after_props = after.properties(self.properties_schema,
self.context)
self.translate_properties(after_props)
@ -1717,6 +1706,7 @@ class Resource(status.ResourceStatus):
# so use the stored frozen_definition instead
self.properties = self.frozen_definition().properties(
self.properties_schema, self.context)
self.translate_properties(self.properties)
with self._action_recorder(action):
if self.abandon_in_progress:

View File

@ -36,10 +36,9 @@ class NoneResource(resource.Resource):
prev_resource, check_init_complete=True):
return False
def reparse(self, translate=True, client_resolve=True):
def reparse(self, client_resolve=True):
self.properties = properties.Properties(schema={}, data={})
if translate:
self.translate_properties(self.properties, client_resolve)
self.translate_properties(self.properties, client_resolve)
def handle_create(self):
self.resource_id_set(six.text_type(uuid.uuid4()))

View File

@ -138,6 +138,8 @@ class ResourceDefinition(object):
function.Function))
self._hash ^= _hash_data(condition)
self.set_translation_rules()
def freeze(self, **overrides):
"""Return a frozen resource definition, with all functions resolved.
@ -238,15 +240,22 @@ class ResourceDefinition(object):
filter(None, (get_resource(dep) for dep in explicit_depends)),
prop_deps, metadata_deps)
def set_translation_rules(self, rules=None, client_resolve=True):
"""Helper method to update properties with translation rules."""
self._rules = rules or []
self._client_resolve = client_resolve
def properties(self, schema, context=None):
"""Return a Properties object representing the resource properties.
The Properties object is constructed from the given schema, and may
require a context to validate constraints.
"""
return properties.Properties(schema, self._properties or {},
function.resolve, context=context,
section='Properties')
props = properties.Properties(schema, self._properties or {},
function.resolve, context=context,
section='Properties')
props.update_translation(self._rules, self._client_resolve)
return props
def deletion_policy(self):
"""Return the deletion policy for the resource.
@ -261,9 +270,11 @@ class ResourceDefinition(object):
The Properties object is constructed from the given schema, and may
require a context to validate constraints.
"""
return properties.Properties(schema, self._update_policy or {},
function.resolve, context=context,
section='UpdatePolicy')
props = properties.Properties(schema, self._update_policy or {},
function.resolve, context=context,
section='UpdatePolicy')
props.update_translation(self._rules, self._client_resolve)
return props
def metadata(self):
"""Return the resource metadata."""

View File

@ -21,7 +21,6 @@ from heat.common.i18n import _
from heat.engine.cfn import functions as cfn_funcs
from heat.engine import function
from heat.engine.hot import functions as hot_funcs
from heat.engine import properties
@functools.total_ordering
@ -101,9 +100,6 @@ class TranslationRule(object):
'rules is: %(rules)s.') % {
'rule': self.rule,
'rules': ', '.join(self.RULE_KEYS)})
if not isinstance(self.properties, properties.Properties):
raise ValueError(_('Properties must be Properties type. '
'Found %s.') % type(self.properties))
if (not isinstance(self.translation_path, list) or
len(self.translation_path) == 0):
@ -167,41 +163,7 @@ class TranslationRule(object):
client_resolve=client_resolve)
def _prepare_data(self, data, path, props):
def get_props(props, key):
props = props.get(key)
if props.schema.schema is not None:
keys = list(props.schema.schema)
schemata = dict((k, props.schema.schema[k])
for k in keys)
props = dict((k, properties.Property(s, k))
for k, s in schemata.items())
if set(props.keys()) == set('*'):
return get_props(props, '*')
return props
if not path:
return
current_key = path[0]
if data.get(current_key) is None:
if (self.rule in (TranslationRule.DELETE,
TranslationRule.RESOLVE) or
(self.rule == TranslationRule.REPLACE and
self.value_name is not None)):
return
data_type = props.get(current_key).type()
if data_type == properties.Schema.LIST:
data[current_key] = []
if data_type == properties.Schema.MAP:
data[current_key] = {}
return
data[current_key] = self._resolve_param(data.get(current_key))
if isinstance(data[current_key], list):
for item in data[current_key]:
self._prepare_data(item, path[1:],
get_props(props, current_key))
elif isinstance(data[current_key], dict):
self._prepare_data(data[current_key], path[1:],
get_props(props, current_key))
pass
def _exec_action(self, key, data, value=None, value_key=None,
value_data=None, client_resolve=True):
@ -454,7 +416,8 @@ class Translation(object):
return (self.is_active and
(key in self._rules or key in self.resolved_translations))
def translate(self, key, prop_value=None, prop_data=None):
def translate(self, key, prop_value=None, prop_data=None, validate=False,
template=None):
if key in self.resolved_translations:
return self.resolved_translations[key]
@ -468,10 +431,12 @@ class Translation(object):
result = None
if rule.rule == TranslationRule.REPLACE:
result = self.replace(key, rule, result, prop_data)
result = self.replace(key, rule, result, prop_data, validate,
template)
if rule.rule == TranslationRule.ADD:
result = self.add(key, rule, result, prop_data)
result = self.add(key, rule, result, prop_data, validate,
template)
if rule.rule == TranslationRule.RESOLVE:
resolved_value = resolve_and_find(result,
@ -484,7 +449,8 @@ class Translation(object):
return result
def add(self, key, add_rule, prop_value=None, prop_data=None):
def add(self, key, add_rule, prop_value=None, prop_data=None,
validate=False, template=None):
value_path = add_rule.get_value_absolute_path()
if prop_value is None:
prop_value = []
@ -504,7 +470,9 @@ class Translation(object):
self.is_active = False
value = get_value(value_path,
prop_data if add_rule.value_name else
self.properties)
self.properties,
validate,
template)
self.is_active = True
if value is not None:
translation_value.extend(value if isinstance(value, list)
@ -514,7 +482,8 @@ class Translation(object):
self.resolved_translations[key] = translation_value
return translation_value
def replace(self, key, replace_rule, prop_value=None, prop_data=None):
def replace(self, key, replace_rule, prop_value=None, prop_data=None,
validate=False, template=None):
value = None
value_path = replace_rule.get_value_absolute_path(full_value_name=True)
short_path = replace_rule.get_value_absolute_path()
@ -530,7 +499,7 @@ class Translation(object):
subpath = value_path
props = prop_data if replace_rule.value_name else self.properties
self.is_active = False
value = get_value(subpath, props)
value = get_value(subpath, props, validate, template)
self.is_active = True
if self.has_translation(prop_path):
@ -554,12 +523,15 @@ class Translation(object):
return result
def get_value(path, props):
def get_value(path, props, validate=False, template=None):
if not props:
return None
key = path[0]
prop = props.get(key)
if isinstance(props, dict):
prop = props.get(key)
else:
prop = props._get_property_value(key, validate, template)
if len(path[1:]) == 0:
return prop
elif prop is None:

View File

@ -1089,17 +1089,16 @@ class CinderVolumeTest(vt_base.BaseVolumeTest):
def test_cinder_create_with_image_and_imageRef(self):
stack_name = 'test_create_with_image_and_imageRef'
combinations = {'imageRef': 'image-456', 'image': 'image-123'}
err_msg = ("Property error: resources.volume2.properties: Cannot "
"define the following properties at the same time: "
"['image', 'imageRef'].")
err_msg = ("Cannot define the following properties at the same time: "
"image, imageRef")
self.stub_ImageConstraint_validate()
stack = utils.parse_stack(self.t, stack_name=stack_name)
vp = stack.t['Resources']['volume2']['Properties']
vp.pop('size')
vp.update(combinations)
ex = self.assertRaises(exception.StackValidationFailed,
stack.get, 'volume2')
self.assertEqual(err_msg, six.text_type(ex))
rsrc = stack.get('volume2')
ex = self.assertRaises(exception.StackValidationFailed, rsrc.validate)
self.assertIn(err_msg, six.text_type(ex))
def test_cinder_create_with_image_and_size(self):
stack_name = 'test_create_with_image_and_size'

View File

@ -339,7 +339,7 @@ resources:
count: 15
wait_before: 5
wait_after: 5
pause_before: 5
pause_before: true
timeout: 42
concurrency: 5
"""
@ -769,13 +769,12 @@ class TestMistralWorkflow(common.HeatTestCase):
rsrc_defns = stack.t.resource_definitions(stack)['workflow']
workflow_rsrc = workflow.Workflow('workflow', rsrc_defns, stack)
ex = self.assertRaises(exception.StackValidationFailed,
workflow.Workflow, 'workflow', rsrc_defns,
stack)
error_msg = ("Property error: resources.workflow.properties: Cannot "
"define the following properties at the same time: "
"['retry', 'policies'].")
self.assertEqual(error_msg, six.text_type(ex))
workflow_rsrc.validate)
error_msg = ("Cannot define the following properties at "
"the same time: tasks.retry, tasks.policies.retry")
self.assertIn(error_msg, six.text_type(ex))
def validate_json_inputs(self, actual_input, expected_input):
actual_json_input = jsonutils.loads(actual_input)
@ -837,11 +836,12 @@ class TestMistralWorkflow(common.HeatTestCase):
rsrc_defns = stack.t.resource_definitions(stack)['workflow']
wf = workflow.Workflow('workflow', rsrc_defns, stack)
self.assertEqual([{'name': 'check_dat_thing',
'action': 'nova.servers_list',
'retry': {'delay': 5, 'count': 15},
'wait_before': 5,
'wait_after': 5,
'pause_before': 5,
'timeout': 42,
'concurrency': 5}], wf.properties.data['tasks'])
result = {k: v for k, v in wf.properties['tasks'][0].items() if v}
self.assertEqual({'name': 'check_dat_thing',
'action': 'nova.servers_list',
'retry': {'delay': 5, 'count': 15},
'wait_before': 5,
'wait_after': 5,
'pause_before': True,
'timeout': 42,
'concurrency': 5}, result)

View File

@ -362,7 +362,7 @@ class PoolTest(common.HeatTestCase):
'subnet',
'sub9999',
cmd_resource=None,
).AndReturn('sub9999')
).MultipleTimes().AndReturn('sub9999')
neutronclient.Client.create_vip(stvipvsn
).AndReturn({'vip': {'id': 'xyz'}})
else:
@ -386,11 +386,7 @@ class PoolTest(common.HeatTestCase):
self.m.VerifyAll()
def test_create_with_vip_subnet(self):
rsrc = self.create_pool(with_vip_subnet=True)
self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
self.m.VerifyAll()
self._test_create(with_vip_subnet=True)
def test_create_pending(self):
snippet = template_format.parse(pool_template)

View File

@ -429,7 +429,7 @@ class NeutronPortTest(common.HeatTestCase):
create_kwargs = props.copy()
create_kwargs['admin_state_up'] = True
self.find_mock.side_effect = [net1] * 7 + [net2] * 2
self.find_mock.side_effect = [net1] * 8 + [net2] * 2 + [net1]
self.create_mock.return_value = {'port': {
"status": "ACTIVE",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766"
@ -457,23 +457,23 @@ class NeutronPortTest(common.HeatTestCase):
self.assertEqual((port.CREATE, port.COMPLETE), port.state)
self.create_mock.assert_called_once_with({'port': create_kwargs})
# Switch from network_id=ID to network=ID (no replace and no upate)
# Switch from network_id=ID to network=ID (no replace)
new_props = props.copy()
new_props['network'] = new_props.pop('network_id')
update_snippet = rsrc_defn.ResourceDefinition(port.name, port.type(),
new_props)
scheduler.TaskRunner(port.update, update_snippet)()
self.assertEqual((port.CREATE, port.COMPLETE), port.state)
self.assertEqual((port.UPDATE, port.COMPLETE), port.state)
self.assertEqual(0, self.update_mock.call_count)
# Switch from network=ID to network=NAME (no replace and no update)
# Switch from network=ID to network=NAME (no replace)
new_props['network'] = 'net1234'
update_snippet = rsrc_defn.ResourceDefinition(port.name, port.type(),
new_props)
scheduler.TaskRunner(port.update, update_snippet)()
self.assertEqual((port.CREATE, port.COMPLETE), port.state)
self.assertEqual((port.UPDATE, port.COMPLETE), port.state)
self.assertEqual(0, self.update_mock.call_count)
# Switch to a different network (replace)
@ -482,7 +482,7 @@ class NeutronPortTest(common.HeatTestCase):
new_props)
updater = scheduler.TaskRunner(port.update, update_snippet)
self.assertRaises(resource.UpdateReplace, updater)
self.assertEqual(9, self.find_mock.call_count)
self.assertEqual(11, self.find_mock.call_count)
def test_get_port_attributes(self):
t = template_format.parse(neutron_port_template)

View File

@ -485,14 +485,14 @@ class NeutronRouterTest(common.HeatTestCase):
'router',
'3e46229d-8fce-4733-819a-b5fe630550f8',
cmd_resource=None,
).AndReturn('3e46229d-8fce-4733-819a-b5fe630550f8')
).MultipleTimes().AndReturn('3e46229d-8fce-4733-819a-b5fe630550f8')
router_key = 'router'
neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'91e47a57-7508-46fe-afc9-fc454e8580e1',
cmd_resource=None,
).AndReturn('91e47a57-7508-46fe-afc9-fc454e8580e1')
).MultipleTimes().AndReturn('91e47a57-7508-46fe-afc9-fc454e8580e1')
subnet_key = 'subnet'
self.m.ReplayAll()
@ -724,7 +724,7 @@ class NeutronRouterTest(common.HeatTestCase):
'network',
'public',
cmd_resource=None,
).AndReturn('fc68ea2c-b60b-4b4f-bd82-94ec81110766')
).MultipleTimes().AndReturn('fc68ea2c-b60b-4b4f-bd82-94ec81110766')
neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
@ -884,24 +884,12 @@ class NeutronRouterTest(common.HeatTestCase):
'other_public',
cmd_resource=None,
).AndReturn('91e47a57-7508-46fe-afc9-fc454e8580e1')
neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'fc68ea2c-b60b-4b4f-bd82-94ec81110766',
cmd_resource=None,
).AndReturn('fc68ea2c-b60b-4b4f-bd82-94ec81110766')
neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub1234',
cmd_resource=None,
).AndReturn('sub1234')
neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'network',
'91e47a57-7508-46fe-afc9-fc454e8580e1',
cmd_resource=None,
).AndReturn('91e47a57-7508-46fe-afc9-fc454e8580e1')
neutronclient.Client.update_router(
'3e46229d-8fce-4733-819a-b5fe630550f8',
{'router': {
@ -913,7 +901,7 @@ class NeutronRouterTest(common.HeatTestCase):
).AndReturn(None)
neutronclient.Client.show_router(
'3e46229d-8fce-4733-819a-b5fe630550f8').AndReturn({
'3e46229d-8fce-4733-819a-b5fe630550f8').MultipleTimes().AndReturn({
"router": {
"status": "ACTIVE",
"external_gateway_info": {

View File

@ -134,18 +134,18 @@ class VPNServiceTest(common.HeatTestCase):
def create_vpnservice(self, resolve_neutron=True, resolve_router=True):
self.stub_SubnetConstraint_validate()
self.stub_RouterConstraint_validate()
neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123',
cmd_resource=None,
).AndReturn('sub123')
neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'router',
'rou123',
cmd_resource=None,
).AndReturn('rou123')
).MultipleTimes().AndReturn('rou123')
neutronV20.find_resourceid_by_name_or_id(
mox.IsA(neutronclient.Client),
'subnet',
'sub123',
cmd_resource=None,
).MultipleTimes().AndReturn('sub123')
if resolve_neutron:
snippet = template_format.parse(vpnservice_template)
else:

View File

@ -255,9 +255,13 @@ class ServersTest(common.HeatTestCase):
def _setup_test_stack(self, stack_name, test_templ=wp_template):
t = template_format.parse(test_templ)
files = {}
if test_templ == ns_template:
files = {'a_file': 'stub'}
templ = template.Template(t,
env=environment.Environment(
{'key_name': 'test'}))
{'key_name': 'test'}),
files=files)
stack = parser.Stack(utils.dummy_context(), stack_name, templ,
stack_id=uuidutils.generate_uuid(),
stack_user_project_id='8888')
@ -549,38 +553,39 @@ class ServersTest(common.HeatTestCase):
(tmpl, stack) = self._setup_test_stack(stack_name)
mock_image = self.patchobject(glance.GlanceClientPlugin,
'find_image_by_name_or_id')
mock_image.side_effect = [
self.stub_KeypairConstraint_validate()
mock_image.side_effect = (
glance.client_exception.EntityMatchNotFound(
entity='image', args={'name': 'Slackware'})]
entity='image', args={'name': 'Slackware'}))
# Init a server with non exist image name
tmpl['Resources']['WebServer']['Properties']['image'] = 'Slackware'
resource_defns = tmpl.resource_definitions(stack)
server = servers.Server('WebServer',
resource_defns['WebServer'], stack)
error = self.assertRaises(glance.client_exception.EntityMatchNotFound,
error = self.assertRaises(exception.ResourceFailure,
scheduler.TaskRunner(server.create))
self.assertEqual("No image matching {'name': 'Slackware'}.",
six.text_type(error))
self.assertIn("No image matching {'name': 'Slackware'}.",
six.text_type(error))
def test_server_duplicate_image_name_err(self):
stack_name = 'img_dup_err'
(tmpl, stack) = self._setup_test_stack(stack_name)
mock_image = self.patchobject(glance.GlanceClientPlugin,
'find_image_by_name_or_id')
mock_image.side_effect = [
self.stub_KeypairConstraint_validate()
mock_image.side_effect = (
glance.client_exception.EntityUniqueMatchNotFound(
entity='image', args='CentOS 5.2')]
entity='image', args='CentOS 5.2'))
tmpl['Resources']['WebServer']['Properties']['image'] = 'CentOS 5.2'
resource_defns = tmpl.resource_definitions(stack)
server = servers.Server('WebServer',
resource_defns['WebServer'], stack)
error = self.assertRaises(
glance.client_exception.EntityUniqueMatchNotFound,
scheduler.TaskRunner(server.create))
self.assertEqual('No image unique match found for CentOS 5.2.',
six.text_type(error))
error = self.assertRaises(exception.ResourceFailure,
scheduler.TaskRunner(server.create))
self.assertIn('No image unique match found for CentOS 5.2.',
six.text_type(error))
def test_server_create_unexpected_status(self):
# NOTE(pshchelo) checking is done only on check_create_complete
@ -1237,6 +1242,7 @@ class ServersTest(common.HeatTestCase):
def test_server_validate_with_networks(self):
stack_name = 'srv_net'
(tmpl, stack) = self._setup_test_stack(stack_name)
self.stub_KeypairConstraint_validate()
network_name = 'public'
# create a server with 'uuid' and 'network' properties
@ -1245,15 +1251,14 @@ class ServersTest(common.HeatTestCase):
'network': network_name}])
resource_defns = tmpl.resource_definitions(stack)
server = servers.Server('server_validate_with_networks',
resource_defns['WebServer'], stack)
self.stub_NetworkConstraint_validate()
ex = self.assertRaises(exception.StackValidationFailed,
servers.Server,
'server_validate_with_networks',
resource_defns['WebServer'], stack)
server.validate)
self.assertIn("Property error: "
"Resources.server_validate_with_networks.Properties: "
"Cannot define the following properties at the same "
"time: ['network', 'uuid'].",
self.assertIn("Cannot define the following properties at "
"the same time: networks.network, networks.uuid",
six.text_type(ex))
def test_server_validate_with_network_empty_ref(self):
@ -2466,20 +2471,20 @@ class ServersTest(common.HeatTestCase):
(tmpl, stack) = self._setup_test_stack(stack_name,
test_templ=ns_template)
side_effect = [neutron.exceptions.NotFound(),
neutron.exceptions.NeutronClientNoUniqueMatch()]
resolver = self.patchobject(neutron.NeutronClientPlugin,
'find_resourceid_by_name_or_id')
self.patchobject(neutron.NeutronClientPlugin,
'find_resourceid_by_name_or_id',
side_effect=side_effect)
resource_defns = tmpl.resource_definitions(stack)
server = servers.Server('server',
resource_defns['server'], stack)
self.assertRaises(neutron.exceptions.NotFound,
scheduler.TaskRunner(server.create))
self.assertRaises(neutron.exceptions.NeutronClientNoUniqueMatch,
scheduler.TaskRunner(server.create))
resolver.side_effect = neutron.exceptions.NotFound()
server.reparse()
self.assertRaises(ValueError, server.properties.get, 'networks')
resolver.side_effect = neutron.exceptions.NeutronClientNoUniqueMatch()
ex = self.assertRaises(exception.ResourceFailure,
scheduler.TaskRunner(server.create))
self.assertIn('use an ID to be more specific.', six.text_type(ex))
def test_server_without_ip_address(self):
return_server = self.fc.servers.list()[3]
@ -2785,22 +2790,23 @@ class ServersTest(common.HeatTestCase):
resource_defns = tmpl.resource_definitions(stack)
self.server = servers.Server('server',
resource_defns['server'], stack)
self.server.translate_properties(self.server.properties, True)
self.assertEqual(2, self.server.t._properties[
'block_device_mapping_v2'][0]['image'])
self.server.translate_properties(self.server.properties)
self.assertEqual('2',
self.server.properties['block_device_mapping_v2'][
0]['image'])
def test_block_device_mapping_v2_image_prop_conflict(self):
test_templ = bdm_v2_template + "\n image: F17-x86_64-gold"
(tmpl, stack) = self._setup_test_stack('mapping',
test_templ=test_templ)
resource_defns = tmpl.resource_definitions(stack)
msg = ("Property error: resources.server.properties: Cannot define "
"the following properties at the same time: "
"['image', 'image_id'].")
msg = ("Cannot define the following "
"properties at the same time: block_device_mapping_v2.image, "
"block_device_mapping_v2.image_id")
server = servers.Server('server', resource_defns['server'], stack)
exc = self.assertRaises(exception.StackValidationFailed,
servers.Server, 'server',
resource_defns['server'], stack)
self.assertEqual(msg, six.text_type(exc))
server.validate)
self.assertIn(msg, six.text_type(exc))
@mock.patch.object(nova.NovaClientPlugin, '_create')
def test_validate_with_both_blk_dev_map_and_blk_dev_map_v2(self,
@ -3700,9 +3706,11 @@ class ServersTest(common.HeatTestCase):
# update
updater = scheduler.TaskRunner(server.update, update_template)
err = self.assertRaises(glance.client_exception.EntityMatchNotFound,
err = self.assertRaises(exception.ResourceFailure,
updater)
self.assertEqual('No image matching Update Image.',
self.assertEqual("StackValidationFailed: resources.my_server: "
"Property error: Properties.image: Error validating "
"value '1': No image matching Update Image.",
six.text_type(err))
def test_server_snapshot(self):

View File

@ -198,7 +198,7 @@ class ResourceTest(common.HeatTestCase):
stack.resource_validate = False
mock_db_get.return_value = mock.Mock()
self.assertEqual(1, len(stack.resources))
self.assertEqual(1, mock_translate.call_count)
self.assertEqual(2, mock_translate.call_count)
self.assertEqual(1, mock_load.call_count)
self.assertEqual(0, mock_resolve.call_count)

View File

@ -27,6 +27,10 @@ from heat.tests import common
class TestTranslationRule(common.HeatTestCase):
def setUp(self):
super(TestTranslationRule, self).setUp()
self.props = mock.Mock(spec=properties.Properties)
def test_translation_rule(self):
for r in translation.TranslationRule.RULE_KEYS:
props = properties.Properties({}, {})
@ -81,12 +85,6 @@ class TestTranslationRule(common.HeatTestCase):
def test_invalid_translation_rule(self):
props = properties.Properties({}, {})
exc = self.assertRaises(ValueError,
translation.TranslationRule,
'proppy', mock.ANY,
mock.ANY)
self.assertEqual('Properties must be Properties type. '
'Found %s.' % str, six.text_type(exc))
exc = self.assertRaises(ValueError,
translation.TranslationRule,
@ -169,13 +167,8 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.ADD,
['far'],
[{'red': props.get('bar')}])
rule.execute_rule()
self.assertIn({'red': 'dak'}, props.get('far'))
# check for new translation
tran = translation.Translation(properties.Properties(schema,
copy.copy(data)))
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far'))
@ -211,12 +204,8 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.ADD,
['far'],
[{'red': props.get('bar')}])
rule.execute_rule()
self.assertEqual([{'red': 'dak'}], props.get('far'))
# check for new translation
tran = translation.Translation(properties.Properties(schema,
copy.copy(data)))
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far'))
@ -249,13 +238,8 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.ADD,
['far'],
[props.get('bar')])
exc = self.assertRaises(ValueError, rule.execute_rule)
self.assertEqual('Add rule must be used only for lists.',
six.text_type(exc))
# check for new translation
tran = translation.Translation(properties.Properties(schema,
copy.copy(data)))
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far'))
@ -289,11 +273,7 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.REPLACE,
['far', 'red'],
props.get('bar'))
rule.execute_rule()
self.assertEqual({'red': 'dak'}, props.get('far'))
# check for new translation
tran = translation.Translation(props)
tran.set_rules([rule])
@ -326,11 +306,7 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.REPLACE,
['far', 'red'],
props.get('bar'))
rule.execute_rule()
self.assertEqual({'red': 'dak'}, props.get('far'))
# check for new translation
tran = translation.Translation(props)
tran.set_rules([rule])
@ -368,11 +344,7 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.REPLACE,
['far', 'red'],
props.get('bar'))
rule.execute_rule()
self.assertEqual([{'red': 'dak'}, {'red': 'dak'}], props.get('far'))
# check for new translation
tran = translation.Translation(props)
tran.set_rules([rule])
@ -410,18 +382,12 @@ class TestTranslationRule(common.HeatTestCase):
['far', 'red'],
None,
'blue')
rule.execute_rule()
self.assertEqual([{'red': 'white', 'blue': None},
{'blue': None, 'red': 'roses'}],
props.get('far'))
# check for new translation
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.0.red'))
result = tran.translate('far.0.red', data['far'][0]['red'],
result = tran.translate('far.0.red', data['far'][0].get('red'),
data['far'][0])
self.assertEqual('white', result)
self.assertEqual('white', tran.resolved_translations['far.0.red'])
@ -448,12 +414,7 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.REPLACE,
['bar'],
props.get('far'))
rule.execute_rule()
self.assertEqual('one', props.get('bar'))
self.assertEqual('one', props.get('far'))
# check for new translation
tran = translation.Translation(props)
tran.set_rules([rule])
@ -477,13 +438,7 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.REPLACE,
['bar'],
value_path=['far'])
ex = self.assertRaises(exception.ResourcePropertyConflict,
rule.execute_rule)
self.assertEqual("Cannot define the following properties at the "
"same time: ['bar', 'far'].",
six.text_type(ex))
# check for new translation
tran = translation.Translation(props)
tran.set_rules([rule])
@ -506,12 +461,7 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.REPLACE,
['bar'],
value_path=['far'])
rule.execute_rule()
self.assertEqual('one', props.get('bar'))
self.assertIsNone(props.get('far'))
# check for new translation
props = properties.Properties(schema, {'far': 'one'})
tran = translation.Translation(props)
tran.set_rules([rule])
@ -537,8 +487,7 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.REPLACE,
['bar'],
props.get('far'))
rule.execute_rule()
props.update_translation([rule])
exc = self.assertRaises(exception.StackValidationFailed,
props.validate)
self.assertEqual("Property error: bar: Value 'one' is not an integer",
@ -572,27 +521,12 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.DELETE,
['far', 'red'])
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.red'))
self.assertIsNone(tran.translate('far.red'))
self.assertIsNone(tran.resolved_translations['far.red'])
rule.execute_rule()
self.assertEqual([{'check': None, 'red': None},
{'check': None, 'red': None}], props.get('far'))
# check if no data translation still is correct
props = properties.Properties(schema, {'far': [{'check': 'yep'}]})
rule = translation.TranslationRule(
props,
translation.TranslationRule.DELETE,
['far', 'red'])
self.assertIsNone(rule.execute_rule())
def test_delete_rule_other(self):
schema = {
'far': properties.Schema(properties.Schema.STRING)
@ -607,18 +541,12 @@ class TestTranslationRule(common.HeatTestCase):
translation.TranslationRule.DELETE,
['far'])
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far'))
self.assertIsNone(tran.translate('far'))
self.assertIsNone(tran.resolved_translations['far'])
rule.execute_rule()
self.assertIsNone(props.get('far'))
def _test_resolve_rule(self, is_list=False):
class FakeClientPlugin(object):
def find_name_id(self, entity=None,
@ -662,8 +590,6 @@ class TestTranslationRule(common.HeatTestCase):
finder='find_name_id'
)
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.red'))
@ -671,10 +597,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('yellow', result)
self.assertEqual('yellow', tran.resolved_translations['far.0.red'])
rule.execute_rule()
self.assertEqual([{'red': 'yellow'}, {'red': 'yellow'}],
props.get('far'))
def test_resolve_rule_list_with_function(self):
client_plugin, schema = self._test_resolve_rule(is_list=True)
join_func = cfn_funcs.Join(None,
@ -693,8 +615,6 @@ class TestTranslationRule(common.HeatTestCase):
finder='find_name_id'
)
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.red'))
@ -702,10 +622,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('yellow', result)
self.assertEqual('yellow', tran.resolved_translations['far.0.red'])
rule.execute_rule()
self.assertEqual([{'red': 'yellow'}, {'red': 'yellow'}],
props.get('far'))
def test_resolve_rule_list_with_ref(self):
client_plugin, schema = self._test_resolve_rule(is_list=True)
@ -734,8 +650,6 @@ class TestTranslationRule(common.HeatTestCase):
finder='find_name_id'
)
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.red'))
@ -743,9 +657,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('yellow', result)
self.assertEqual('yellow', tran.resolved_translations['far.0.red'])
rule.execute_rule()
self.assertEqual(data, props.data)
def test_resolve_rule_list_strings(self):
client_plugin, schema = self._test_resolve_rule()
data = {'far': ['one', 'rose']}
@ -763,8 +674,6 @@ class TestTranslationRule(common.HeatTestCase):
client_plugin=client_plugin,
finder='find_name_id')
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far'))
@ -772,26 +681,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual(['yellow', 'pink'], result)
self.assertEqual(['yellow', 'pink'], tran.resolved_translations['far'])
rule.execute_rule()
self.assertEqual(['yellow', 'pink'], props.get('far'))
def test_resolve_rule_list_empty(self):
client_plugin, schema = self._test_resolve_rule(is_list=True)
data = {
'far': [],
}
props = properties.Properties(schema, data)
rule = translation.TranslationRule(
props,
translation.TranslationRule.RESOLVE,
['far', 'red'],
client_plugin=client_plugin,
finder='find_name_id'
)
rule.execute_rule()
self.assertEqual([], props.get('far'))
def test_resolve_rule_other(self):
client_plugin, schema = self._test_resolve_rule()
data = {'far': 'one'}
@ -803,8 +692,6 @@ class TestTranslationRule(common.HeatTestCase):
client_plugin=client_plugin,
finder='find_name_id')
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far'))
@ -812,9 +699,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('yellow', result)
self.assertEqual('yellow', tran.resolved_translations['far'])
rule.execute_rule()
self.assertEqual('yellow', props.get('far'))
def test_resolve_rule_other_with_ref(self):
client_plugin, schema = self._test_resolve_rule()
@ -839,17 +723,12 @@ class TestTranslationRule(common.HeatTestCase):
client_plugin=client_plugin,
finder='find_name_id')
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far'))
result = tran.translate('far', data['far'])
self.assertEqual('yellow', result)
rule.execute_rule()
self.assertEqual(data, props.data)
def test_resolve_rule_other_with_function(self):
client_plugin, schema = self._test_resolve_rule()
join_func = cfn_funcs.Join(None,
@ -863,8 +742,6 @@ class TestTranslationRule(common.HeatTestCase):
client_plugin=client_plugin,
finder='find_name_id')
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far'))
@ -872,9 +749,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('yellow', result)
self.assertEqual('yellow', tran.resolved_translations['far'])
rule.execute_rule()
self.assertEqual(data, props.data)
def test_resolve_rule_other_with_get_attr(self):
client_plugin, schema = self._test_resolve_rule()
@ -896,8 +770,6 @@ class TestTranslationRule(common.HeatTestCase):
client_plugin=client_plugin,
finder='find_name_id')
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule], client_resolve=False)
self.assertFalse(tran.store_translated_values)
@ -906,15 +778,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('no_check', result)
self.assertIsNone(tran.resolved_translations.get('far'))
rule.execute_rule(client_resolve=False)
self.assertEqual(data, props.data)
mock_getatt = self.patchobject(attr_func, 'result',
return_value='rose')
rule.execute_rule()
self.assertEqual('pink', props.get('far'))
self.assertEqual(1, mock_getatt.call_count)
def test_resolve_rule_other_with_entity(self):
client_plugin, schema = self._test_resolve_rule()
data = {'far': 'one'}
@ -927,8 +790,6 @@ class TestTranslationRule(common.HeatTestCase):
finder='find_name_id',
entity='rose')
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far'))
@ -936,9 +797,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('pink', result)
self.assertEqual('pink', tran.resolved_translations['far'])
rule.execute_rule()
self.assertEqual('pink', props.get('far'))
def test_property_json_param_correct_translation(self):
"""Test case when property with sub-schema takes json param."""
schema = {
@ -976,8 +834,6 @@ class TestTranslationRule(common.HeatTestCase):
['far', 'bar'],
value_path=['far', 'dar'])
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.bar'))
@ -986,10 +842,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('rad', result)
self.assertEqual('rad', tran.resolved_translations['far.bar'])
rule.execute_rule()
self.assertEqual('rad', props.get('far').get('bar'))
def test_property_json_param_to_list_correct_translation(self):
"""Test case when list property with sub-schema takes json param."""
schema = {
@ -1030,8 +882,6 @@ class TestTranslationRule(common.HeatTestCase):
['far', 'bar'],
value_name='dar')
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.0.bar'))
@ -1041,10 +891,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('rad', result)
self.assertEqual('rad', tran.resolved_translations['far.0.bar'])
rule.execute_rule()
self.assertEqual([{'dar': None, 'bar': 'rad'}], props.get('far'))
def test_property_commadelimitedlist_param_correct_translation(self):
"""Test when property with sub-schema takes comma_delimited_list."""
schema = {
@ -1081,8 +927,6 @@ class TestTranslationRule(common.HeatTestCase):
['far'],
[props.get('boo')])
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far'))
@ -1091,86 +935,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual(['white', 'roses', 'chrysanthemums'],
tran.resolved_translations['far'])
rule.execute_rule()
self.assertEqual(['white', 'roses', 'chrysanthemums'],
props.get('far'))
def test_property_no_translation_if_user_parameter_missing(self):
"""Test translation in the case of missing parameter"""
schema = {
'source': properties.Schema(
properties.Schema.STRING
),
'destination': properties.Schema(
properties.Schema.STRING
)}
class DummyStack(dict):
@property
def parameters(self):
return mock.Mock()
param = hot_funcs.GetParam(DummyStack(),
'get_param',
'source_param')
param.parameters = {}
data = {'source': param, 'destination': ''}
props = properties.Properties(schema, data,
resolver=function.resolve)
rule = translation.TranslationRule(
props,
translation.TranslationRule.REPLACE,
['destination'],
value_path=['source'])
rule.execute_rule()
# ensure that translation rule was not applied
self.assertEqual({'source': param, 'destination': ''},
data)
def test_property_get_attr_translation_successfully_skipped(self):
schema = {
'source': properties.Schema(
properties.Schema.LIST,
schema=properties.Schema(
properties.Schema.MAP,
schema={
'sub-source': properties.Schema(
properties.Schema.STRING
)
}
)
),
'destination': properties.Schema(
properties.Schema.STRING
)}
class DummyStack(dict):
pass
class rsrc(object):
pass
stack = DummyStack(res=rsrc())
attr_func = cfn_funcs.GetAtt(stack, 'Fn::GetAtt',
['res', 'sub-sources'])
data = {'source': attr_func, 'destination': ''}
props = properties.Properties(schema, data)
rule = translation.TranslationRule(
props,
translation.TranslationRule.REPLACE,
['source', 'sub-sources'],
value_path=['destination'])
rule.execute_rule()
self.assertEqual({'source': attr_func, 'destination': ''}, props.data)
def test_list_list_add_translation_rule(self):
schema = {
'far': properties.Schema(
@ -1199,8 +963,6 @@ class TestTranslationRule(common.HeatTestCase):
value_name='car'
)
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.0.bar'))
@ -1213,11 +975,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual(['first'], result)
self.assertEqual(['first'], tran.resolved_translations['far.1.bar'])
rule.execute_rule()
self.assertIn({'bar': ['shar', 'man'], 'car': 'man'}, props.get('far'))
self.assertIn({'bar': ['first'], 'car': 'first'}, props.get('far'))
def test_replace_rule_map_with_custom_value_path(self):
schema = {
'far': properties.Schema(
@ -1245,8 +1002,7 @@ class TestTranslationRule(common.HeatTestCase):
value_path=['bar'],
custom_value_path=['red']
)
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.red'))
@ -1254,11 +1010,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('dak', result)
self.assertEqual('dak', tran.resolved_translations['far.red'])
rule.execute_rule()
self.assertEqual({'red': 'dak'}, props.get('far'))
self.assertEqual({}, props.get('bar'))
def test_replace_rule_list_with_custom_value_path(self):
schema = {
'far': properties.Schema(
@ -1289,8 +1040,7 @@ class TestTranslationRule(common.HeatTestCase):
value_name='blue',
custom_value_path=['black', 'white']
)
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.0.red'))
@ -1298,12 +1048,6 @@ class TestTranslationRule(common.HeatTestCase):
self.assertEqual('daisy', result)
self.assertEqual('daisy', tran.resolved_translations['far.0.red'])
rule.execute_rule()
self.assertEqual([{'red': 'daisy', 'blue': {'black': {}}},
{'blue': None, 'red': 'roses'}],
props.get('far'))
def test_add_rule_list_with_custom_value_path(self):
schema = {
'far': properties.Schema(
@ -1336,8 +1080,6 @@ class TestTranslationRule(common.HeatTestCase):
custom_value_path=['black', 'check']
)
# check for new translation (before old translation, because old style
# change data)
tran = translation.Translation(props)
tran.set_rules([rule])
self.assertTrue(tran.has_translation('far.0.red'))
@ -1349,82 +1091,6 @@ class TestTranslationRule(common.HeatTestCase):
data['far'][1]['red'],
data['far'][1]))
rule.execute_rule()
self.assertEqual([{'red': ['one'],
'blue': {'black': {'white': 'daisy',
'check': ['one']}}},
{'blue': None, 'red': ['roses']}],
props.get('far'))
# check whether if data will contain list-type property to add
data = {
'far': [{'red': ['roses'], 'blue': {'black': {'check': ['two']}}}]}
props = properties.Properties(schema, data)
rule.properties = props
rule.execute_rule()
self.assertEqual([{'red': ['roses', 'two'],
'blue': {'black': {'check': ['two']}}}],
props.get('far'))
def test_list_list_error_translation_rule(self):
schema = {
'far': properties.Schema(
properties.Schema.LIST,
schema=properties.Schema(
properties.Schema.MAP,
schema={
'car': properties.Schema(properties.Schema.STRING),
'dar': properties.Schema(properties.Schema.STRING),
}
)
),
'bar': properties.Schema(
properties.Schema.LIST,
schema=properties.Schema(
properties.Schema.MAP,
schema={
'car': properties.Schema(properties.Schema.STRING),
'dar': properties.Schema(properties.Schema.STRING),
}
)
),
}
data = {'far': [{'car': 'man'}], 'bar': [{'dar': 'check'}]}
props = properties.Properties(schema, data)
rule = translation.TranslationRule(
props,
translation.TranslationRule.REPLACE,
['far'],
value_path=['bar', 'car']
)
ex = self.assertRaises(ValueError, rule.execute_rule)
self.assertEqual('Cannot use value_path for properties inside '
'list-type properties', six.text_type(ex))
class TestTranslation(common.HeatTestCase):
def setUp(self):
super(TestTranslation, self).setUp()
self.props = mock.Mock(spec=properties.Properties)
def test_set_rules(self):
rules = [
translation.TranslationRule(
self.props,
translation.TranslationRule.REPLACE,
['a'],
'b')
]
tran = translation.Translation(self.props)
tran.set_rules(rules)
self.assertEqual({'a': {rules[0]}}, tran._rules)
def test_set_rules_none(self):
tran = translation.Translation()
self.assertEqual({}, tran._rules)