return default
+ @staticmethod
+ def _snake_to_camel(name):
+ tokens = []
+ if name:
+ tokens = name.split('_')
+ for i in xrange(len(tokens)):
+ tokens[i] = tokens[i].capitalize()
+ return "".join(tokens)
+
+ def _translate_constraints(self, constraints):
+ param = {}
+
+ def add_constraint(key, val, desc):
+ cons = param.get(key, [])
+ cons.append((val, desc))
+ param[key] = cons
+
+ def add_min_max(key, val, desc):
+ minv = val.get('min')
+ maxv = val.get('max')
+ if minv:
+ add_constraint('Min%s' % key, minv, desc)
+ if maxv:
+ add_constraint('Max%s' % key, maxv, desc)
+
+ for constraint in constraints:
+ desc = constraint.get('description')
+ for key, val in constraint.iteritems():
+ key = self._snake_to_camel(key)
+ if key == 'Description':
+ continue
+ elif key == 'Range':
+ add_min_max('Value', val, desc)
+ elif key == 'Length':
+ add_min_max(key, val, desc)
+ else:
+ add_constraint(key, val, desc)
+
+ return param
+
def _translate_parameters(self, parameters):
"""Get the parameters of the template translated into CFN format."""
- HOT_TO_CFN_ATTRS = {'type': 'Type',
- 'default': 'Default',
- 'description': 'Description'}
-
- HOT_TO_CFN_TYPES = {'string': 'String'}
-
- cfn_params = {}
-
- for param_name, attrs in parameters.iteritems():
- cfn_param = {}
-
- for attr, attr_value in attrs.iteritems():
- cfn_attr = self._translate(attr, HOT_TO_CFN_ATTRS, attr)
-
- if attr == 'type':
- # try to translate type; if no translation found, keep
- # original value and let engine throw an error for an
- # unsupported type
- attr_value = self._translate(attr_value, HOT_TO_CFN_TYPES,
- attr_value)
-
- cfn_param[cfn_attr] = attr_value
-
- if len(cfn_param) > 0:
- cfn_params[param_name] = cfn_param
-
- return cfn_params
+ params = {}
+ for name, attrs in parameters.iteritems():
+ param = {}
+ for key, val in attrs.iteritems():
+ key = self._snake_to_camel(key)
+ if key == 'Type':
+ val = self._snake_to_camel(val)
+ elif key == 'Constraints':
+ param.update(self._translate_constraints(val))
+ continue
+ elif key == 'Hidden':
+ key = 'NoEcho'
+ param[key] = val
+ if len(param) > 0:
+ params[name] = param
+ return params
def _translate_resources(self, resources):
"""Get the resources of the template translated into CFN format."""
''')
expected = {'param1': {'Description': 'foo',
- 'Type': 'unsupported_type'}}
+ 'Type': 'UnsupportedType'}}
tmpl = parser.Template(hot_tpl)
self.assertEqual(tmpl[hot.PARAMETERS], expected)
+ def test_translate_parameters_length_range(self):
+ hot_tpl = template_format.parse('''
+ heat_template_version: 2013-05-23
+ parameters:
+ wait_time:
+ description: application wait time
+ type: number
+ default: 150
+ constraints:
+ - range: { min: 120, max: 600}
+ description: min value 120 seconds, max value 600 seconds
+ key_name:
+ description: Name of an existing EC2 KeyPair
+ type: string
+ default: heat_key
+ constraints:
+ - length: {min: 1, max: 32}
+ description: length should be between 1 and 32
+ ''')
+
+ expected = {
+ 'wait_time': {
+ 'Description': 'application wait time',
+ 'Type': 'Number',
+ 'Default': 150,
+ 'MaxValue': [
+ (600, 'min value 120 seconds, max value 600 seconds')],
+ 'MinValue': [
+ (120, 'min value 120 seconds, max value 600 seconds')]
+ },
+ 'key_name': {
+ 'Description': 'Name of an existing EC2 KeyPair',
+ 'Type': 'String',
+ 'Default': 'heat_key',
+ 'MaxLength': [(32, u'length should be between 1 and 32')],
+ 'MinLength': [(1, u'length should be between 1 and 32')]
+ }}
+
+ tmpl = parser.Template(hot_tpl)
+ self.assertEqual(expected, tmpl[hot.PARAMETERS])
+
+ def test_translate_parameters_allowed_values(self):
+ hot_tpl = template_format.parse('''
+ heat_template_version: 2013-05-23
+ parameters:
+ instance_type:
+ description: instance type
+ type: string
+ default: m1.small
+ constraints:
+ - allowed_values: ["m1.tiny",
+ "m1.small",
+ "m1.medium", "m1.large", "m1.xlarge"]
+ description: must be a valid EC2 instance type.
+ ''')
+ expected = {
+ 'instance_type': {
+ 'Description': 'instance type',
+ 'Type': 'String',
+ 'Default': 'm1.small',
+ 'AllowedValues': [(["m1.tiny",
+ "m1.small",
+ "m1.medium",
+ "m1.large",
+ "m1.xlarge"],
+ 'must be a valid EC2 instance type.')]}}
+
+ tmpl = parser.Template(hot_tpl)
+ self.assertEqual(expected, tmpl[hot.PARAMETERS])
+
+ def test_translate_parameters_allowed_patterns(self):
+ hot_tpl = template_format.parse('''
+ heat_template_version: 2013-05-23
+ parameters:
+ db_name:
+ description: The WordPress database name
+ type: string
+ default: wordpress
+ constraints:
+ - length: { min: 1, max: 64 }
+ description: string lenght should between 1 and 64
+ - allowed_pattern: "[a-zA-Z]+"
+ description: Value must consist of characters only
+ - allowed_pattern: "[a-z]+[a-zA-Z]*"
+ description: Value must start with a lowercase character
+ ''')
+ expected = {
+ 'db_name': {
+ 'Description': 'The WordPress database name',
+ 'Type': 'String',
+ 'Default': 'wordpress',
+ 'MinLength': [(1, 'string lenght should between 1 and 64')],
+ 'MaxLength': [(64, 'string lenght should between 1 and 64')],
+ 'AllowedPattern': [
+ ('[a-zA-Z]+',
+ 'Value must consist of characters only'),
+ ('[a-z]+[a-zA-Z]*',
+ 'Value must start with a lowercase character')]}}
+ tmpl = parser.Template(hot_tpl)
+ self.assertEqual(expected, tmpl[hot.PARAMETERS])
+
+ def test_translate_parameters_hidden(self):
+ hot_tpl = template_format.parse('''
+ heat_template_version: 2013-05-23
+ parameters:
+ user_roles:
+ description: User roles
+ type: comma_delimited_list
+ default: guest,newhire
+ hidden: TRUE
+ ''')
+ expected = {
+ 'user_roles': {
+ 'Description': 'User roles',
+ 'Type': 'CommaDelimitedList',
+ 'Default': 'guest,newhire',
+ 'NoEcho': True
+ }}
+
+ tmpl = parser.Template(hot_tpl)
+ self.assertEqual(expected, tmpl[hot.PARAMETERS])
+
def test_translate_resources(self):
"""Test translation of resources into internal engine format."""