]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
HOT parameter validation model translation
authorJUN JIE NAN <nanjj@cn.ibm.com>
Tue, 23 Jul 2013 00:47:38 +0000 (08:47 +0800)
committerJUN JIE NAN <nanjj@cn.ibm.com>
Wed, 24 Jul 2013 01:55:27 +0000 (09:55 +0800)
Translate hot parameters into heat parameter model.

Change-Id: I6733be7c13e2e9fdbed27d5c21044fb5f457ed17
Implements: blueprint hot-parameters

heat/engine/hot.py
heat/tests/test_hot.py

index 50374690fc061717e51dadd749ff96ec76e52985..5dbfa70df54f441c33730ab9e2807bde565ced72 100644 (file)
@@ -81,35 +81,64 @@ class HOTemplate(template.Template):
 
         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."""
index d91ecef2fa0b5bf0ebe8f9449452f633ed761562..f7f759e19512dfbce21d804cf218888a8c0243d9 100644 (file)
@@ -86,11 +86,133 @@ class HOTemplateTest(HeatTestCase):
         ''')
 
         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."""