Пример #1
0
 def test_prepare_properties(self):
     data = {
         'admin_state_up': False,
         'value_specs': {
             'router:external': True
         }
     }
     p = properties.Properties(net.Net.properties_schema, data)
     props = nr.NeutronResource.prepare_properties(p, 'resource_name')
     self.assertEqual(
         {
             'name': 'resource_name',
             'router:external': True,
             'admin_state_up': False,
             'shared': False
         }, props)
Пример #2
0
    def test_validate_properties(self):
        vs = {'router:external': True}
        data = {'admin_state_up': False, 'value_specs': vs}
        p = properties.Properties(net.Net.properties_schema, data)
        self.assertIsNone(nr.NeutronResource.validate_properties(p))

        vs['foo'] = '1234'
        self.assertIsNone(nr.NeutronResource.validate_properties(p))
        vs.pop('foo')

        banned_keys = {'shared': True, 'name': 'foo', 'tenant_id': '1234'}
        for key, val in six.iteritems(banned_keys):
            vs.update({key: val})
            msg = '%s not allowed in value_specs' % key
            self.assertEqual(msg, nr.NeutronResource.validate_properties(p))
            vs.pop(key)
Пример #3
0
    def test_add_rule_list_with_custom_value_path(self):
        schema = {
            'far':
            properties.Schema(properties.Schema.LIST,
                              schema=properties.Schema(
                                  properties.Schema.MAP,
                                  schema={
                                      'red':
                                      properties.Schema(
                                          properties.Schema.LIST,
                                          schema=properties.Schema(
                                              properties.Schema.STRING)),
                                      'blue':
                                      properties.Schema(properties.Schema.MAP)
                                  }))
        }

        data = {
            'far': [{
                'blue': {
                    'black': {
                        'white': 'daisy',
                        'check': ['one']
                    }
                }
            }, {
                'red': ['roses']
            }]
        }
        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.ADD, ['far', 'red'],
            value_name='blue',
            custom_value_path=['black', 'check'])

        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].get('red'),
                                data['far'][0])
        self.assertEqual(['one'], result)
        self.assertEqual(['one'], tran.resolved_translations['far.0.red'])
        self.assertEqual(['roses'],
                         tran.translate('far.1.red', data['far'][1]['red'],
                                        data['far'][1]))
Пример #4
0
    def validate(self):
        """Validate any of the provided params."""
        res = super(CloudLoadBalancer, self).validate()
        if res:
            return res

        if self.properties.get(self.HALF_CLOSED):
            if not (self.properties[self.PROTOCOL] == 'TCP' or
                    self.properties[self.PROTOCOL] == 'TCP_CLIENT_FIRST'):
                message = (_('The %s property is only available for the TCP '
                             'or TCP_CLIENT_FIRST protocols')
                           % self.HALF_CLOSED)
                raise exception.StackValidationFailed(message=message)

        # health_monitor connect and http types require completely different
        # schema
        if self.properties.get(self.HEALTH_MONITOR):
            prop_val = self.properties[self.HEALTH_MONITOR]
            health_monitor = self._remove_none(prop_val)

            schema = self._health_monitor_schema
            if health_monitor[self.HEALTH_MONITOR_TYPE] == 'CONNECT':
                schema = dict((k, v) for k, v in schema.items()
                              if k in self._HEALTH_MONITOR_CONNECT_KEYS)
            properties.Properties(schema,
                                  health_monitor,
                                  function.resolve,
                                  self.name).validate()

        # validate if HTTPS_REDIRECT is true
        self._validate_https_redirect()
        # if a vip specifies and id, it can't specify version or type;
        # otherwise version and type are required
        for vip in self.properties[self.VIRTUAL_IPS]:
            has_id = vip.get(self.VIRTUAL_IP_ID) is not None
            has_version = vip.get(self.VIRTUAL_IP_IP_VERSION) is not None
            has_type = vip.get(self.VIRTUAL_IP_TYPE) is not None
            if has_id:
                if (has_version or has_type):
                    message = _("Cannot specify type or version if VIP id is"
                                " specified.")
                    raise exception.StackValidationFailed(message=message)
            elif not (has_version and has_type):
                message = _("Must specify VIP type and version if no id "
                            "specified.")
                raise exception.StackValidationFailed(message=message)
Пример #5
0
    def test_replace_rule_str(self):
        schema = {
            'far': properties.Schema(properties.Schema.STRING),
            'bar': properties.Schema(properties.Schema.STRING)
        }

        data = {'far': 'one', 'bar': 'two'}

        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(props,
                                           translation.TranslationRule.REPLACE,
                                           ['bar'], props.get('far'))
        rule.execute_rule()

        self.assertEqual('one', props.get('bar'))
        self.assertEqual('one', props.get('far'))
Пример #6
0
    def test_resolve_rule_other(self):
        client_plugin, schema = self._test_resolve_rule()
        data = {'far': 'one'}
        props = properties.Properties(schema, data)
        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.RESOLVE,
            ['far'],
            client_plugin=client_plugin,
            finder='find_name_id')

        tran = translation.Translation(props)
        tran.set_rules([rule])
        self.assertTrue(tran.has_translation('far'))
        result = tran.translate('far', data['far'])
        self.assertEqual('yellow', result)
        self.assertEqual('yellow', tran.resolved_translations['far'])
Пример #7
0
    def test_resolve_rule_other_with_function(self):
        client_plugin, schema = self._test_resolve_rule()
        join_func = cfn_funcs.Join(None, 'Fn::Join', ['.', ['bar', 'baz']])
        data = {'far': join_func}
        props = properties.Properties(schema, data)
        rule = translation.TranslationRule(props,
                                           translation.TranslationRule.RESOLVE,
                                           ['far'],
                                           client_plugin=client_plugin,
                                           finder='find_name_id')

        tran = translation.Translation(props)
        tran.set_rules([rule])
        self.assertTrue(tran.has_translation('far'))
        result = tran.translate('far', data['far'])
        self.assertEqual('yellow', result)
        self.assertEqual('yellow', tran.resolved_translations['far'])
Пример #8
0
    def validate_template(self, cnxt, template):
        """
        The validate_template method uses the stack parser to check
        the validity of a template.

        arg1 -> RPC context.
        arg3 -> Template of stack you want to create.
        arg4 -> Stack Input Params
        """
        logger.info('validate_template')
        if template is None:
            msg = _("No Template provided.")
            return webob.exc.HTTPBadRequest(explanation=msg)

        tmpl = parser.Template(template)
        tmpl_resources = template.get('Resources', [])

        if not tmpl_resources:
            return {'Error': 'At least one Resources member must be defined.'}

        for res in tmpl_resources.values():
            if not res.get('Type'):
                return {
                    'Error':
                    'Every Resources object must contain a Type member.'
                }
            ResourceClass = resource.get_class(res['Type'])
            props = properties.Properties(ResourceClass.properties_schema,
                                          res.get('Properties', {}))
            try:
                ResourceClass.validate_deletion_policy(res)
                props.validate(with_value=False)
            except Exception as ex:
                return {'Error': str(ex)}

        tmpl_params = parser.Parameters(None, tmpl)
        format_validate_parameter = lambda p: dict(p.schema)
        is_real_param = lambda p: p.name not in parameters.PSEUDO_PARAMETERS
        params = tmpl_params.map(format_validate_parameter, is_real_param)

        result = {
            'Description': template.get('Description', ''),
            'Parameters': params,
        }
        return result
Пример #9
0
    def test_replace_rule_list_same(self):
        schema = {
            'far': properties.Schema(
                properties.Schema.LIST,
                schema=properties.Schema(
                    properties.Schema.MAP,
                    schema={
                        'red': properties.Schema(
                            properties.Schema.STRING
                        ),
                        'blue': properties.Schema(
                            properties.Schema.STRING
                        )
                    }
                )
            )}

        data = {
            'far': [{'blue': 'white'},
                    {'red': 'roses'}]
        }
        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.REPLACE,
            ['far', 'red'],
            None,
            'blue')

        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].get('red'),
                                data['far'][0])
        self.assertEqual('white', result)
        self.assertEqual('white', tran.resolved_translations['far.0.red'])
        self.assertIsNone(tran.resolved_translations['far.0.blue'])
        self.assertTrue(tran.has_translation('far.1.red'))
        result = tran.translate('far.1.red', data['far'][1]['red'],
                                data['far'][1])
        self.assertEqual('roses', result)
        self.assertEqual('roses', tran.resolved_translations['far.1.red'])
        self.assertIsNone(tran.resolved_translations['far.1.blue'])
Пример #10
0
    def test_replace_rule_str_invalid(self):
        schema = {
            'far': properties.Schema(properties.Schema.STRING),
            'bar': properties.Schema(properties.Schema.INTEGER)
        }

        data = {'far': 'one', 'bar': 2}

        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(props,
                                           translation.TranslationRule.REPLACE,
                                           ['bar'], props.get('far'))
        props.update_translation([rule])
        exc = self.assertRaises(exception.StackValidationFailed,
                                props.validate)
        self.assertEqual("Property error: bar: Value 'one' is not an integer",
                         six.text_type(exc))
Пример #11
0
    def test_resolve_rule_list_strings(self):
        client_plugin, schema = self._test_resolve_rule()
        data = {'far': ['one', 'rose']}
        schema = {
            'far':
            properties.Schema(properties.Schema.LIST,
                              schema=properties.Schema(
                                  properties.Schema.STRING))
        }
        props = properties.Properties(schema, data)
        rule = translation.TranslationRule(props,
                                           translation.TranslationRule.RESOLVE,
                                           ['far'],
                                           client_plugin=client_plugin,
                                           finder='find_name_id')

        rule.execute_rule()
        self.assertEqual(['yellow', 'pink'], props.get('far'))
Пример #12
0
 def test_translation_rule(self):
     for r in translation.TranslationRule.RULE_KEYS:
         props = properties.Properties({}, {})
         rule = translation.TranslationRule(
             props, r, ['any'], ['value'] if r == 'Add' else 'value',
             'value_name' if r == 'Replace' else None,
             'client_plugin' if r == 'Resolve' else None,
             'finder' if r == 'Resolve' else None)
         self.assertEqual(rule.properties, props)
         self.assertEqual(rule.rule, r)
         if r == 'Add':
             self.assertEqual(['value'], rule.value)
         else:
             self.assertEqual('value', rule.value)
         if r == 'Replace':
             self.assertEqual('value_name', rule.value_name)
         else:
             self.assertIsNone(rule.value_name)
Пример #13
0
    def test_resolve_rule_list_populated(self):
        client_plugin, schema = self._test_resolve_rule(is_list=True)
        data = {
            'far': [{'red': 'blue'},
                    {'red': 'roses'}],
        }
        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([{'red': 'yellow'}, {'red': 'yellow'}],
                         props.get('far'))
Пример #14
0
    def test_replace_rule_str_value_path_error(self):
        schema = {
            'far': properties.Schema(properties.Schema.STRING),
            'bar': properties.Schema(properties.Schema.STRING)
        }

        data = {'far': 'one', 'bar': 'two'}

        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.REPLACE,
            ['bar'],
            value_path=['far'])
        ex = self.assertRaises(ValueError, rule.execute_rule)
        self.assertEqual('Cannot use bar and far at the same time.',
                         six.text_type(ex))
Пример #15
0
 def setUp(self):
     schema = {
         'int': {'Type': 'Integer'},
         'string': {'Type': 'String'},
         'required_int': {'Type': 'Integer', 'Required': True},
         'bad_int': {'Type': 'Integer'},
         'missing': {'Type': 'Integer'},
         'defaulted': {'Type': 'Integer', 'Default': 1},
         'default_override': {'Type': 'Integer', 'Default': 1},
     }
     data = {
         'int': 21,
         'string': 'foo',
         'bad_int': 'foo',
         'default_override': 21,
     }
     double = lambda d: d * 2
     self.props = properties.Properties(schema, data, double, 'wibble')
    def test_replace_rule_list_with_custom_value_path(self):
        schema = {
            'far':
            properties.Schema(properties.Schema.LIST,
                              schema=properties.Schema(
                                  properties.Schema.MAP,
                                  schema={
                                      'red':
                                      properties.Schema(
                                          properties.Schema.STRING),
                                      'blue':
                                      properties.Schema(properties.Schema.MAP)
                                  }))
        }

        data = {
            'far': [{
                'blue': {
                    'black': {
                        'white': 'daisy'
                    }
                }
            }, {
                'red': 'roses'
            }]
        }
        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.REPLACE, ['far', 'red'],
            value_name='blue',
            custom_value_path=['black', 'white'])
        rule.execute_rule()

        self.assertEqual([{
            'red': 'daisy',
            'blue': {
                'black': {}
            }
        }, {
            'blue': None,
            'red': 'roses'
        }], props.get('far'))
Пример #17
0
    def test_property_json_param_correct_translation(self):
        """Test case when property with sub-schema takes json param."""
        schema = {
            'far': properties.Schema(properties.Schema.MAP,
                                     schema={
                                         'bar': properties.Schema(
                                             properties.Schema.STRING,
                                         ),
                                         'dar': properties.Schema(
                                             properties.Schema.STRING
                                         )
                                     })
        }

        class DummyStack(dict):
            @property
            def parameters(self):
                return mock.Mock()

        param = hot_funcs.GetParam(DummyStack(json_far='json_far'),
                                   'get_param',
                                   'json_far')
        param.parameters = {
            'json_far': parameters.JsonParam(
                'json_far',
                {'Type': 'Json'},
                '{"dar": "rad"}').value()}
        data = {'far': param}

        props = properties.Properties(schema, data, resolver=function.resolve)

        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.REPLACE,
            ['far', 'bar'],
            value_path=['far', 'dar'])

        tran = translation.Translation(props)
        tran.set_rules([rule])
        self.assertTrue(tran.has_translation('far.bar'))
        prop_data = props['far']
        result = tran.translate('far.bar', prop_data['bar'], prop_data)
        self.assertEqual('rad', result)
        self.assertEqual('rad', tran.resolved_translations['far.bar'])
Пример #18
0
    def test_property_commadelimitedlist_param_correct_translation(self):
        """Test when property with sub-schema takes comma_delimited_list."""
        schema = {
            'far': properties.Schema(
                properties.Schema.LIST,
                schema=properties.Schema(
                    properties.Schema.STRING,
                )
            ),
            'boo': properties.Schema(
                properties.Schema.STRING
            )}

        class DummyStack(dict):
            @property
            def parameters(self):
                return mock.Mock()

        param = hot_funcs.GetParam(DummyStack(list_far='list_far'),
                                   'get_param',
                                   'list_far')
        param.parameters = {
            'list_far': parameters.CommaDelimitedListParam(
                'list_far',
                {'Type': 'CommaDelimitedList'},
                "white,roses").value()}
        data = {'far': param, 'boo': 'chrysanthemums'}

        props = properties.Properties(schema, data, resolver=function.resolve)

        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.ADD,
            ['far'],
            [props.get('boo')])

        tran = translation.Translation(props)
        tran.set_rules([rule])
        self.assertTrue(tran.has_translation('far'))
        result = tran.translate('far', props['far'])
        self.assertEqual(['white', 'roses', 'chrysanthemums'], result)
        self.assertEqual(['white', 'roses', 'chrysanthemums'],
                         tran.resolved_translations['far'])
Пример #19
0
    def test_replace_rule_str_value_path_error(self):
        schema = {
            'far': properties.Schema(properties.Schema.STRING),
            'bar': properties.Schema(properties.Schema.STRING)
        }

        data = {'far': 'one', 'bar': 'two'}

        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(props,
                                           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))
Пример #20
0
    def test_delete_rule_other(self):
        schema = {
            'far': properties.Schema(properties.Schema.STRING)
        }

        data = {'far': 'one'}

        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.DELETE,
            ['far'])

        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'])
Пример #21
0
    def test_property_no_translation_removed_function(self):
        """Test case when list property with sub-schema takes json param."""
        schema = {
            'far': properties.Schema(properties.Schema.LIST,
                                     schema=properties.Schema(
                                         properties.Schema.MAP,
                                         schema={
                                             'bar': properties.Schema(
                                                 properties.Schema.STRING,
                                             ),
                                             'dar': properties.Schema(
                                                 properties.Schema.STRING
                                             )
                                         }
                                     ))
        }

        class DummyStack(dict):
            @property
            def parameters(self):
                return mock.Mock()

        param = hot_funcs.Removed(DummyStack(json_far='json_far'),
                                  'Ref',
                                  'json_far')
        param.parameters = {
            'json_far': parameters.JsonParam(
                'json_far',
                {'Type': 'Json'},
                '{"dar": "rad"}').value()}
        data = {'far': [param]}

        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.REPLACE,
            ['far', 'bar'],
            value_name='dar')

        rule.execute_rule()

        self.assertEqual([param], props.data.get('far'))
Пример #22
0
    def test_replace_rule_map_dont_exist(self):
        schema = {
            'far':
            properties.Schema(
                properties.Schema.MAP,
                schema={'red': properties.Schema(properties.Schema.STRING)}),
            'bar':
            properties.Schema(properties.Schema.STRING)
        }

        data = {'bar': 'dak'}
        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(props,
                                           translation.TranslationRule.REPLACE,
                                           ['far', 'red'], props.get('bar'))
        rule.execute_rule()

        self.assertEqual({'red': 'dak'}, props.get('far'))
Пример #23
0
    def handle_update(self, json_snippet, tmpl_diff, prop_diff):
        if self.resource_id and prop_diff:
            snippet = json_snippet.get('Properties', {})
            self.properties = properties.Properties(self.properties_schema,
                                                    snippet, function.resolve,
                                                    self.name)

            params = self.properties[self.PARAMETERS]
            env = environment.get_child_environment(self.stack.env, params)
            tmpl = template_format.parse(self.properties[self.TEMPLATE])
            fields = {
                'stack_id': self.resource_id,
                'parameters': params,
                'template': tmpl,
                'timeout_mins': self.properties[self.TIMEOUT],
                'disable_rollback': self.stack.disable_rollback,
                'files': self.stack.t.files,
                'environment': env.user_env_as_dict(),
            }
            self.heat().stacks.update(**fields)
Пример #24
0
    def test_resolve_rule_list_with_function(self):
        client_plugin, schema = self._test_resolve_rule(is_list=True)
        join_func = cfn_funcs.Join(None,
                                   'Fn::Join', ['.', ['bar', 'baz']])
        data = {
            'far': [{'red': 'blue'},
                    {'red': join_func}],
        }
        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([{'red': 'yellow'}, {'red': 'yellow'}],
                         props.get('far'))
Пример #25
0
    def test_add_rule_invalid(self):
        schema = {
            'far':
            properties.Schema(
                properties.Schema.MAP,
                schema={'red': properties.Schema(properties.Schema.STRING)}),
            'bar':
            properties.Schema(properties.Schema.STRING)
        }

        data = {'far': 'tran', 'bar': 'dak'}
        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(props,
                                           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))
Пример #26
0
    def _generate_schema(self, props):
        self._parsed_nested = None
        try:
            tmpl = template.Template(self.child_template())
        except ValueError as download_error:
            self.validation_exception = download_error
            tmpl = template.Template({})

        # re-generate the properties and attributes from the template.
        self.properties_schema = (properties.Properties.schema_from_params(
            tmpl.param_schemata()))
        self.attributes_schema = (attributes.Attributes.schema_from_outputs(
            tmpl[tmpl.OUTPUTS]))

        self.properties = properties.Properties(self.properties_schema, props,
                                                self._resolve_runtime_data,
                                                self.name, self.context)
        self.attributes = attributes.Attributes(self.name,
                                                self.attributes_schema,
                                                self._resolve_attribute)
Пример #27
0
    def test_resolve_rule_other_with_ref(self):
        client_plugin, schema = self._test_resolve_rule()

        class rsrc(object):
            action = INIT = "INIT"

        class DummyStack(dict):
            pass

        stack = DummyStack(another_res=rsrc())
        ref = cfn_funcs.ResourceRef(stack, 'get_resource', 'another_res')
        data = {'far': ref}
        props = properties.Properties(schema, data)
        rule = translation.TranslationRule(props,
                                           translation.TranslationRule.RESOLVE,
                                           ['far'],
                                           client_plugin=client_plugin,
                                           finder='find_name_id')

        rule.execute_rule()
        self.assertEqual(data, props.data)
Пример #28
0
    def test_replace_rule_str(self):
        schema = {
            'far': properties.Schema(properties.Schema.STRING),
            'bar': properties.Schema(properties.Schema.STRING)
        }

        data = {'far': 'one', 'bar': 'two'}

        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(props,
                                           translation.TranslationRule.REPLACE,
                                           ['bar'], props.get('far'))

        tran = translation.Translation(props)
        tran.set_rules([rule])

        self.assertTrue(tran.has_translation('bar'))
        result = tran.translate('bar', data['bar'])
        self.assertEqual('one', result)
        self.assertEqual('one', tran.resolved_translations['bar'])
Пример #29
0
    def test_property_json_param_correct_translation(self):
        """Test case when property with sub-schema takes json param."""
        schema = {
            'far': properties.Schema(properties.Schema.MAP,
                                     schema={
                                         'bar': properties.Schema(
                                             properties.Schema.STRING,
                                         ),
                                         'dar': properties.Schema(
                                             properties.Schema.STRING
                                         )
                                     })
        }

        class DummyStack(dict):
            @property
            def parameters(self):
                return mock.Mock()

        param = hot_funcs.GetParam(DummyStack(json_far='json_far'),
                                   'get_param',
                                   'json_far')
        param.parameters = {
            'json_far': parameters.JsonParam(
                'json_far',
                {'Type': 'Json'},
                '{"dar": "rad"}').value()}
        data = {'far': param}

        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.REPLACE,
            ['far', 'bar'],
            value_path=['far', 'dar'])

        rule.execute_rule()

        self.assertEqual('rad', props.get('far').get('bar'))
Пример #30
0
    def test_replace_rule_list_with_custom_value_path(self):
        schema = {
            'far':
            properties.Schema(properties.Schema.LIST,
                              schema=properties.Schema(
                                  properties.Schema.MAP,
                                  schema={
                                      'red':
                                      properties.Schema(
                                          properties.Schema.STRING),
                                      'blue':
                                      properties.Schema(properties.Schema.MAP)
                                  }))
        }

        data = {
            'far': [{
                'blue': {
                    'black': {
                        'white': 'daisy'
                    }
                }
            }, {
                'red': 'roses'
            }]
        }
        props = properties.Properties(schema, data)

        rule = translation.TranslationRule(
            props,
            translation.TranslationRule.REPLACE, ['far', 'red'],
            value_name='blue',
            custom_value_path=['black', 'white'])

        tran = translation.Translation(props)
        tran.set_rules([rule])
        self.assertTrue(tran.has_translation('far.0.red'))
        result = tran.translate('far.0.red', prop_data=data['far'][0])
        self.assertEqual('daisy', result)
        self.assertEqual('daisy', tran.resolved_translations['far.0.red'])