Add required definition in class of Input. 53/18653/1
authorshangxdy <shang.xiaodong@zte.com.cn>
Mon, 15 Aug 2016 07:43:07 +0000 (15:43 +0800)
committershangxdy <shang.xiaodong@zte.com.cn>
Mon, 15 Aug 2016 07:43:07 +0000 (15:43 +0800)
As a template designer;
I want to know if the parameters is missed when deploy a template, but
currently there is not validation about the required parameters;
So i add property in class of Input, and validate the require parameter.
Note: the patch will submit to Openstack community too.

JIRA:PARSER-88

Change-Id: I5763a18da4037b0f81a55b8c8d83414e685b03d2
Signed-off-by: shangxdy <shang.xiaodong@zte.com.cn>
tosca2heat/tosca-parser/toscaparser/parameters.py
tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_property_with_host.yaml
tosca2heat/tosca-parser/toscaparser/tests/data/test_tosca_normative_type_by_shortname.yaml
tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/system.yaml
tosca2heat/tosca-parser/toscaparser/tests/data/tosca_single_instance_wordpress.yaml
tosca2heat/tosca-parser/toscaparser/tests/data/tosca_single_instance_wordpress_with_local_abspath_import.yaml
tosca2heat/tosca-parser/toscaparser/tests/data/tosca_single_instance_wordpress_with_url_import.yaml
tosca2heat/tosca-parser/toscaparser/tests/test_functions.py
tosca2heat/tosca-parser/toscaparser/tests/test_toscatpl.py
tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py
tosca2heat/tosca-parser/toscaparser/topology_template.py

index 1d2cb29..ca8e697 100644 (file)
@@ -35,10 +35,17 @@ class Input(object):
         self.name = name
         self.schema = Schema(name, schema_dict)
 
+        self._validate_field()
+        self.validate_type(self.type)
+
     @property
     def type(self):
         return self.schema.type
 
+    @property
+    def required(self):
+        return self.schema.required
+
     @property
     def description(self):
         return self.schema.description
@@ -52,8 +59,6 @@ class Input(object):
         return self.schema.constraints
 
     def validate(self, value=None):
-        self._validate_field()
-        self.validate_type(self.type)
         if value is not None:
             self._validate_value(value)
 
index 1ca69ca..1e5f5e6 100644 (file)
@@ -23,6 +23,7 @@ topology_template:
     db_root_pwd:
       type: string
       description: Root password for MySQL.
+      default: '12345678'
     db_port:
       type: PortDef
       description: Port for the MySQL database.
index 8a702fb..c0653e7 100644 (file)
@@ -10,6 +10,7 @@ topology_template:
       description: Number of CPUs for the server.
       constraints:
         - valid_values: [ 1, 2, 4, 8 ]
+      default: 2
 
   node_templates:
     server:
index 3c946f3..00fb486 100644 (file)
@@ -15,7 +15,7 @@ topology_template:
       description: IP address of the message queuing server to receive messages from.
     mq_server_port:
       type: integer
-      default1: 8080
+      default: 8080
       description: Port to be used for receiving messages.
 
   node_templates:
index 9e686ab..f605b05 100644 (file)
@@ -29,6 +29,7 @@ topology_template:
     db_root_pwd:
       type: string
       description: Root password for MySQL.
+      default: '12345678'
     db_port:
       type: PortDef
       description: Port for the MySQL database.
index 6caac11..9a57eb0 100644 (file)
@@ -31,6 +31,7 @@ topology_template:
     db_root_pwd:
       type: string
       description: Root password for MySQL.
+      default: '12345678'
     db_port:
       type: PortDef
       description: Port for the MySQL database.
index e5f1580..5d41749 100644 (file)
@@ -29,6 +29,7 @@ topology_template:
     db_root_pwd:
       type: string
       description: Root password for MySQL.
+      default: '12345678'
     db_port:
       type: PortDef
       description: Port for the MySQL database.
index 4d063e5..81de909 100644 (file)
@@ -116,7 +116,7 @@ class IntrinsicFunctionsTest(TestCase):
         self.assertEqual(3306, dbms_port.result())
         dbms_root_password = self._get_property(mysql_dbms,
                                                 'root_password')
-        self.assertIsNone(dbms_root_password.result())
+        self.assertEqual(dbms_root_password.result(), "12345678")
 
     def test_get_property_with_host(self):
         tosca_tpl = os.path.join(
index 2488b65..e229d2f 100644 (file)
@@ -452,7 +452,13 @@ class ToscaTemplateTest(TestCase):
         tosca_tpl = ('https://raw.githubusercontent.com/openstack/'
                      'tosca-parser/master/toscaparser/tests/data/'
                      'tosca_single_instance_wordpress.yaml')
-        tosca = ToscaTemplate(tosca_tpl, None, False)
+        tosca = ToscaTemplate(tosca_tpl, a_file=False,
+                              parsed_params={"db_name": "mysql",
+                                             "db_user": "mysql",
+                                             "db_root_pwd": "1234",
+                                             "db_pwd": "5678",
+                                             "db_port": 3306,
+                                             "cpus": 4})
         self.assertTrue(tosca.topology_template.custom_defs)
 
     def test_url_template_with_local_abspath_import(self):
@@ -473,19 +479,27 @@ class ToscaTemplateTest(TestCase):
         tosca_tpl = ('https://raw.githubusercontent.com/openstack/'
                      'tosca-parser/master/toscaparser/tests/data/'
                      'tosca_single_instance_wordpress_with_url_import.yaml')
-        tosca = ToscaTemplate(tosca_tpl, None, False)
+        tosca = ToscaTemplate(tosca_tpl, a_file=False,
+                              parsed_params={"db_root_pwd": "1234"})
         self.assertTrue(tosca.topology_template.custom_defs)
 
     def test_csar_parsing_wordpress(self):
         csar_archive = os.path.join(
             os.path.dirname(os.path.abspath(__file__)),
             'data/CSAR/csar_wordpress.zip')
-        self.assertTrue(ToscaTemplate(csar_archive))
+        self.assertTrue(ToscaTemplate(csar_archive,
+                                      parsed_params={"db_name": "mysql",
+                                                     "db_user": "mysql",
+                                                     "db_root_pwd": "1234",
+                                                     "db_pwd": "5678",
+                                                     "db_port": 3306,
+                                                     "cpus": 4}))
 
     def test_csar_parsing_elk_url_based(self):
         csar_archive = ('https://github.com/openstack/tosca-parser/raw/master/'
                         'toscaparser/tests/data/CSAR/csar_elk.zip')
-        self.assertTrue(ToscaTemplate(csar_archive, None, False))
+        self.assertTrue(ToscaTemplate(csar_archive, a_file=False,
+                                      parsed_params={"my_cpus": 4}))
 
     def test_nested_imports_in_templates(self):
         tosca_tpl = os.path.join(
@@ -593,7 +607,7 @@ class ToscaTemplateTest(TestCase):
         tosca_tpl = os.path.join(
             os.path.dirname(os.path.abspath(__file__)),
             "data/CSAR/csar_elk.csar")
-        tosca = ToscaTemplate(tosca_tpl)
+        tosca = ToscaTemplate(tosca_tpl, parsed_params={"my_cpus": 2})
         self.assertTrue(tosca.topology_template.custom_defs)
 
     def test_available_rel_tpls(self):
@@ -802,4 +816,4 @@ class ToscaTemplateTest(TestCase):
         tosca_tpl = os.path.join(
             os.path.dirname(os.path.abspath(__file__)),
             "data/test_containers.yaml")
-        ToscaTemplate(tosca_tpl)
+        ToscaTemplate(tosca_tpl, parsed_params={"mysql_root_pwd": "12345678"})
index 986d9e4..57daf7e 100644 (file)
@@ -99,7 +99,7 @@ class ToscaTemplateValidationTest(TestCase):
               'verify valid values.'))
 
     def test_inputs(self):
-        tpl_snippet = '''
+        tpl_snippet1 = '''
         inputs:
           cpus:
             type: integer
@@ -109,14 +109,33 @@ class ToscaTemplateValidationTest(TestCase):
             required: yes
             status: supported
         '''
-        inputs = (toscaparser.utils.yamlparser.
-                  simple_parse(tpl_snippet)['inputs'])
-        name, attrs = list(inputs.items())[0]
-        input = Input(name, attrs)
-        err = self.assertRaises(exception.UnknownFieldError, input.validate)
-        self.assertEqual(_('Input "cpus" contains unknown field "constraint". '
-                           'Refer to the definition to verify valid values.'),
-                         err.__str__())
+        tpl_snippet2 = '''
+        inputs:
+          cpus:
+            type: integer
+            description: Number of CPUs for the server.
+            constraints:
+              - valid_values: [ 1, 2, 4 ]
+            required: yes
+            status: supported
+        '''
+        inputs1 = (toscaparser.utils.yamlparser.
+                   simple_parse(tpl_snippet1)['inputs'])
+        name1, attrs1 = list(inputs1.items())[0]
+        inputs2 = (toscaparser.utils.yamlparser.
+                   simple_parse(tpl_snippet2)['inputs'])
+        name2, attrs2 = list(inputs2.items())[0]
+        try:
+            Input(name1, attrs1)
+        except Exception as err:
+            # err=self.assertRaises(exception.UnknownFieldError,
+            #                       input1.validate)
+            self.assertEqual(_('Input "cpus" contains unknown field '
+                               '"constraint". Refer to the definition to '
+                               'verify valid values.'),
+                             err.__str__())
+        input2 = Input(name2, attrs2)
+        self.assertTrue(input2.required)
 
     def _imports_content_test(self, tpl_snippet, path, custom_type_def):
         imports = (toscaparser.utils.yamlparser.
index fe05979..100a06b 100644 (file)
@@ -26,7 +26,6 @@ from toscaparser.substitution_mappings import SubstitutionMappings
 from toscaparser.tpl_relationship_graph import ToscaGraph
 from toscaparser.utils.gettextutils import _
 
-
 # Topology template key names
 SECTIONS = (DESCRIPTION, INPUTS, NODE_TEMPLATES,
             RELATIONSHIP_TEMPLATES, OUTPUTS, GROUPS,
@@ -73,6 +72,14 @@ class TopologyTemplate(object):
                 default = input.default
                 if default:
                     input.validate(default)
+            if (self.parsed_params and input.name not in self.parsed_params
+                or self.parsed_params is None) and input.required \
+                and input.default is None:
+                exception.ExceptionCollector.appendException(
+                    exception.MissingRequiredParameterError(
+                        what='Template',
+                        input_name=input.name))
+
             inputs.append(input)
         return inputs