diff --git a/heat/engine/service.py b/heat/engine/service.py index a2d5a76509..a2fc9d6bdc 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -696,7 +696,7 @@ class EngineService(service.ServiceBase): else: tmpl = templatem.Template(template, files=files) env_util.merge_environments(environment_files, files, params, - tmpl.param_schemata()) + tmpl.all_param_schemata(files)) tmpl.env = environment.Environment(params) self._validate_new_stack(cnxt, stack_name, tmpl) @@ -891,7 +891,7 @@ class EngineService(service.ServiceBase): new_files.update(files or {}) tmpl = templatem.Template(new_template, files=new_files) env_util.merge_environments(environment_files, files, params, - tmpl.param_schemata()) + tmpl.all_param_schemata(files)) existing_env = current_stack.env.env_as_dict() existing_params = existing_env[env_fmt.PARAMETERS] clear_params = set(args.get(rpc_api.PARAM_CLEAR_PARAMETERS, [])) @@ -912,7 +912,7 @@ class EngineService(service.ServiceBase): else: tmpl = templatem.Template(template, files=files) env_util.merge_environments(environment_files, files, params, - tmpl.param_schemata()) + tmpl.all_param_schemata(files)) tmpl.env = environment.Environment(params) max_resources = cfg.CONF.max_resources_per_stack @@ -1221,7 +1221,7 @@ class EngineService(service.ServiceBase): tmpl = templatem.Template(template, files=files) env_util.merge_environments(environment_files, files, params, - tmpl.param_schemata()) + tmpl.all_param_schemata(files)) tmpl.env = environment.Environment(params) try: self._validate_template(cnxt, tmpl) diff --git a/heat/engine/template.py b/heat/engine/template.py index 5aa6c51f62..92f395ef91 100644 --- a/heat/engine/template.py +++ b/heat/engine/template.py @@ -22,6 +22,7 @@ from stevedore import extension from heat.common import exception from heat.common.i18n import _ +from heat.common import template_format from heat.engine import conditions from heat.engine import environment from heat.engine import function @@ -195,6 +196,21 @@ class Template(collections.Mapping): """Return a dict of parameters.Schema objects for the parameters.""" pass + def all_param_schemata(self, files): + schema = {} + files = files if files is not None else {} + for f in files.values(): + try: + data = template_format.parse(f) + except ValueError: + continue + else: + sub_tmpl = Template(data) + schema.update(sub_tmpl.param_schemata()) + # Parent template has precedence, so update the schema last. + schema.update(self.param_schemata()) + return schema + @abc.abstractmethod def get_section_name(self, section): """Return a correct section name.""" diff --git a/heat/tests/test_validate.py b/heat/tests/test_validate.py index 4664717c19..234dcd35fe 100644 --- a/heat/tests/test_validate.py +++ b/heat/tests/test_validate.py @@ -1011,6 +1011,27 @@ class ValidateTest(common.HeatTestCase): res['Parameters']['net_name']['Value']) self.assertNotIn('Default', res['Parameters']['net_name']) + def test_validate_parameters_nested(self): + t = template_format.parse(test_template_allowed_integers) + + other_template = test_template_no_default.replace( + 'net_name', 'net_name2') + + files = {'env1': 'parameter_defaults:\n net_name: net1', + 'env2': 'parameter_defaults:' + '\n net_name: net2' + '\n net_name2: net3', + 'tmpl1.yaml': test_template_no_default, + 'tmpl2.yaml': other_template} + params = {'parameters': {}, 'parameter_defaults': {}} + + self.engine.validate_template( + self.ctx, t, + params=params, + files=files, environment_files=['env1', 'env2']) + self.assertEqual('net2', params['parameter_defaults']['net_name']) + self.assertEqual('net3', params['parameter_defaults']['net_name2']) + def test_validate_hot_empty_parameters_valid(self): t = template_format.parse( """