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:
Pablo Andres Fuente 2014-01-31 14:40:33 -03:00
parent 40b0ec4715
commit 53a059fb44
3 changed files with 159 additions and 25 deletions

View File

@ -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

View File

@ -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):
'''

View File

@ -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"""