HOT templates get_param allows extra attributes
HOT templates now allow the use of extra attributes for the get_param function. With this change it is possible to traverse complex parameters. If one of the extra arguments does not match with a key or a valid index, an empty string is returned. Change-Id: I49f46b164fd21f6cc7dcea8d4ec1046f6cc912b3 Implements: blueprint hot-select
This commit is contained in:
parent
40b0ec4715
commit
53a059fb44
|
@ -450,11 +450,20 @@ follows:
|
|||
|
||||
::
|
||||
|
||||
get_param: <parameter name>
|
||||
get_param:
|
||||
- <parameter name>
|
||||
- <key/index 1> (optional)
|
||||
- <key/index 2> (optional)
|
||||
- ...
|
||||
|
||||
The *parameter name* of the input parameter to be resolved is given as single
|
||||
parameter to this function. A sample use of this function in context of a
|
||||
resource definition is shown below.
|
||||
parameter name
|
||||
The parameter name is required as it specifies the parameter
|
||||
to be resolved. If the parameter returns a complex data structure
|
||||
such as a list or a map, then subsequent keys or indexes can be specified
|
||||
which navigate the data structure to return the desired value.
|
||||
|
||||
A sample use of this function in context of a resource definition
|
||||
is shown below.
|
||||
|
||||
::
|
||||
|
||||
|
@ -462,14 +471,30 @@ resource definition is shown below.
|
|||
instance_type:
|
||||
type: string
|
||||
description: Instance type to be used.
|
||||
server_data:
|
||||
type: json
|
||||
|
||||
resources:
|
||||
my_instance:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: instance_type}
|
||||
metadata: { get_param: [ server_data, metadata ] }
|
||||
key_name: { get_param: [ server_data, keys, 0 ] }
|
||||
|
||||
|
||||
In this example, if the instance_type/server_data parameters contained
|
||||
the following data:
|
||||
|
||||
::
|
||||
|
||||
{"instance_type": "m1.tiny",
|
||||
{"server_data": {"metadata": {"foo": "bar"},
|
||||
"keys": ["a_key","other_key"]}}}
|
||||
|
||||
then the value of the property 'flavor' would resolve to "m1.tiny", 'metadata'
|
||||
would resolve to {"foo": "bar"} and 'key_name' would resolve to "a_key".
|
||||
|
||||
get_attr
|
||||
--------
|
||||
The *get_attr* function allows referencing an attribute of a resource. At
|
||||
|
|
|
@ -133,12 +133,12 @@ class HOTemplate(template.Template):
|
|||
return cfn_outputs
|
||||
|
||||
@staticmethod
|
||||
def resolve_param_refs(s, params, transform=None):
|
||||
def _resolve_ref(s, params, transform=None):
|
||||
"""
|
||||
Resolve constructs of the form { get_param: my_param }
|
||||
Resolve constructs of the form { Ref: my_param }
|
||||
"""
|
||||
def match_param_ref(key, value):
|
||||
return (key in ['get_param', 'Ref'] and
|
||||
return (key == 'Ref' and
|
||||
value is not None and
|
||||
value in params)
|
||||
|
||||
|
@ -151,6 +151,41 @@ class HOTemplate(template.Template):
|
|||
return template._resolve(match_param_ref, handle_param_ref, s,
|
||||
transform)
|
||||
|
||||
@staticmethod
|
||||
def _resolve_get_param(s, params, transform=None):
|
||||
"""
|
||||
Resolve constructs of the form { get_param: my_param }
|
||||
"""
|
||||
def match_param_ref(key, value):
|
||||
return (key == 'get_param' and
|
||||
value is not None)
|
||||
|
||||
def handle_param_ref(args):
|
||||
try:
|
||||
if not isinstance(args, list):
|
||||
args = [args]
|
||||
|
||||
parameter = params[args[0]]
|
||||
try:
|
||||
for inner_param in args[1:]:
|
||||
if hasattr(parameter, str(inner_param)):
|
||||
parameter = getattr(parameter, inner_param)
|
||||
else:
|
||||
parameter = parameter[inner_param]
|
||||
return parameter
|
||||
except (KeyError, IndexError, TypeError):
|
||||
return ''
|
||||
except (KeyError, ValueError):
|
||||
raise exception.UserParameterMissing(key=args[0])
|
||||
|
||||
return template._resolve(match_param_ref, handle_param_ref, s,
|
||||
transform)
|
||||
|
||||
@staticmethod
|
||||
def resolve_param_refs(s, params, transform=None):
|
||||
resolved = HOTemplate._resolve_ref(s, params, transform)
|
||||
return HOTemplate._resolve_get_param(resolved, params, transform)
|
||||
|
||||
@staticmethod
|
||||
def resolve_resource_refs(s, resources, transform=None):
|
||||
'''
|
||||
|
|
|
@ -97,24 +97,6 @@ class HOTemplateTest(HeatTestCase):
|
|||
tmpl = parser.Template(hot_tpl)
|
||||
self.assertEqual(expected, tmpl[tmpl.OUTPUTS])
|
||||
|
||||
def test_param_refs(self):
|
||||
"""Test if parameter references work."""
|
||||
params = {'foo': 'bar', 'blarg': 'wibble'}
|
||||
snippet = {'properties': {'key1': {'get_param': 'foo'},
|
||||
'key2': {'get_param': 'blarg'}}}
|
||||
snippet_resolved = {'properties': {'key1': 'bar',
|
||||
'key2': 'wibble'}}
|
||||
tmpl = parser.Template(hot_tpl_empty)
|
||||
self.assertEqual(snippet_resolved,
|
||||
tmpl.resolve_param_refs(snippet, params))
|
||||
snippet = {'properties': {'key1': {'Ref': 'foo'},
|
||||
'key2': {'Ref': 'blarg'}}}
|
||||
snippet_resolved = {'properties': {'key1': 'bar',
|
||||
'key2': 'wibble'}}
|
||||
tmpl = parser.Template(hot_tpl_empty)
|
||||
self.assertEqual(snippet_resolved,
|
||||
tmpl.resolve_param_refs(snippet, params))
|
||||
|
||||
def test_str_replace(self):
|
||||
"""Test str_replace function."""
|
||||
|
||||
|
@ -506,6 +488,98 @@ class StackAttributesTest(HeatTestCase):
|
|||
self.assertEqual(self.expected, resolved)
|
||||
|
||||
|
||||
class StackParametersTest(HeatTestCase):
|
||||
"""
|
||||
Test stack get_param function when stack was created from HOT template.
|
||||
"""
|
||||
class AnObject(object):
|
||||
def __init__(self, first, second, third):
|
||||
self.first = first
|
||||
self.second = second
|
||||
self.third = third
|
||||
|
||||
simple_object = AnObject('a', 'b', 'c')
|
||||
complex_object = AnObject('a',
|
||||
{'key1': 'val1', 'key2': 'val2', 'key3': 'val3'},
|
||||
simple_object)
|
||||
|
||||
scenarios = [
|
||||
('Ref_string',
|
||||
dict(params={'foo': 'bar', 'blarg': 'wibble'},
|
||||
snippet={'properties': {'prop1': {'Ref': 'foo'},
|
||||
'prop2': {'Ref': 'blarg'}}},
|
||||
expected={'properties': {'prop1': 'bar',
|
||||
'prop2': 'wibble'}})),
|
||||
('get_param_string',
|
||||
dict(params={'foo': 'bar', 'blarg': 'wibble'},
|
||||
snippet={'properties': {'prop1': {'get_param': 'foo'},
|
||||
'prop2': {'get_param': 'blarg'}}},
|
||||
expected={'properties': {'prop1': 'bar',
|
||||
'prop2': 'wibble'}})),
|
||||
('get_list_attr',
|
||||
dict(params={'list': ['foo', 'bar']},
|
||||
snippet={'properties': {'prop1': {'get_param': ['list', 1]}}},
|
||||
expected={'properties': {'prop1': 'bar'}})),
|
||||
('get_flat_dict_attr',
|
||||
dict(params={'flat_dict':
|
||||
{'key1': 'val1', 'key2': 'val2', 'key3': 'val3'}},
|
||||
snippet={'properties': {'prop1': {'get_param':
|
||||
['flat_dict', 'key2']}}},
|
||||
expected={'properties': {'prop1': 'val2'}})),
|
||||
('get_nested_attr_list',
|
||||
dict(params={'nested_dict':
|
||||
{'list': [1, 2, 3],
|
||||
'string': 'abc',
|
||||
'dict': {'a': 1, 'b': 2, 'c': 3}}},
|
||||
snippet={'properties': {'prop1': {'get_param':
|
||||
['nested_dict',
|
||||
'list',
|
||||
0]}}},
|
||||
expected={'properties': {'prop1': 1}})),
|
||||
('get_nested_attr_dict',
|
||||
dict(params={'nested_dict':
|
||||
{'list': [1, 2, 3],
|
||||
'string': 'abc',
|
||||
'dict': {'a': 1, 'b': 2, 'c': 3}}},
|
||||
snippet={'properties': {'prop1': {'get_param':
|
||||
['nested_dict',
|
||||
'dict',
|
||||
'a']}}},
|
||||
expected={'properties': {'prop1': 1}})),
|
||||
('get_simple_object',
|
||||
dict(params={'simple_object': simple_object},
|
||||
snippet={'properties': {'prop1': {'get_param':
|
||||
['simple_object',
|
||||
'first']}}},
|
||||
expected={'properties': {'prop1': 'a'}})),
|
||||
('get_complex_object',
|
||||
dict(params={'complex_object': complex_object},
|
||||
snippet={'properties': {'prop1': {'get_param':
|
||||
['complex_object',
|
||||
'second',
|
||||
'key1']}}},
|
||||
expected={'properties': {'prop1': 'val1'}})),
|
||||
('get_complex_object_invalid_argument',
|
||||
dict(params={'complex_object': complex_object},
|
||||
snippet={'properties': {'prop1': {'get_param':
|
||||
['complex_object',
|
||||
'not_there']}}},
|
||||
expected={'properties': {'prop1': ''}})),
|
||||
('get_attr_none',
|
||||
dict(params={'none': None},
|
||||
snippet={'properties': {'prop1': {'get_param':
|
||||
['none',
|
||||
'who_cares']}}},
|
||||
expected={'properties': {'prop1': ''}})),
|
||||
]
|
||||
|
||||
def test_param_refs(self):
|
||||
"""Test if parameter references work."""
|
||||
tmpl = parser.Template(hot_tpl_empty)
|
||||
self.assertEqual(self.expected,
|
||||
tmpl.resolve_param_refs(self.snippet, self.params))
|
||||
|
||||
|
||||
class HOTParamValidatorTest(HeatTestCase):
|
||||
"""Test HOTParamValidator"""
|
||||
|
||||
|
|
Loading…
Reference in New Issue