1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
2 # not use this file except in compliance with the License. You may obtain
3 # a copy of the License at
5 # http://www.apache.org/licenses/LICENSE-2.0
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 # License for the specific language governing permissions and limitations
17 from toscaparser.common.exception import ExceptionCollector
18 from toscaparser.common.exception import InvalidTemplateVersion
19 from toscaparser.common.exception import MissingRequiredFieldError
20 from toscaparser.common.exception import UnknownFieldError
21 from toscaparser.common.exception import ValidationError
22 from toscaparser.elements.entity_type import update_definitions
23 from toscaparser.extensions.exttools import ExtTools
24 import toscaparser.imports
25 from toscaparser.prereq.csar import CSAR
26 from toscaparser.repositories import Repository
27 from toscaparser.topology_template import TopologyTemplate
28 from toscaparser.tpl_relationship_graph import ToscaGraph
29 from toscaparser.utils.gettextutils import _
30 import toscaparser.utils.yamlparser
33 # TOSCA template key names
34 SECTIONS = (DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME,
35 TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION,
36 DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES,
37 RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES,
38 CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES,
39 POLICY_TYPES, GROUP_TYPES, REPOSITORIES) = \
40 ('tosca_definitions_version', 'tosca_default_namespace',
41 'template_name', 'topology_template', 'template_author',
42 'template_version', 'description', 'imports', 'dsl_definitions',
43 'node_types', 'relationship_types', 'relationship_templates',
44 'capability_types', 'artifact_types', 'data_types',
45 'policy_types', 'group_types', 'repositories')
46 # Sections that are specific to individual template definitions
47 SPECIAL_SECTIONS = (METADATA) = ('metadata')
49 log = logging.getLogger("tosca.model")
51 YAML_LOADER = toscaparser.utils.yamlparser.load_yaml
54 class ToscaTemplate(object):
57 VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0']
59 VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
61 ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS}
63 ADDITIONAL_SECTIONS.update(exttools.get_sections())
65 '''Load the template data.'''
66 def __init__(self, path=None, parsed_params=None, a_file=True,
68 ExceptionCollector.start()
70 self.input_path = None
73 self.nested_tosca_template = []
75 self.input_path = path
76 self.path = self._get_path(path)
78 self.tpl = YAML_LOADER(self.path, self.a_file)
80 msg = (_('Both path and yaml_dict_tpl arguments were '
81 'provided. Using path and ignoring yaml_dict_tpl.'))
86 self.tpl = yaml_dict_tpl
88 ExceptionCollector.appendException(
89 ValueError(_('No path or yaml_dict_tpl was provided. '
90 'There is nothing to parse.')))
93 self.parsed_params = parsed_params
94 self._validate_field()
95 self.version = self._tpl_version()
96 self.relationship_types = self._tpl_relationship_types()
97 self.description = self._tpl_description()
98 self.topology_template = self._topology_template()
99 self.repositories = self._tpl_repositories()
100 if self.topology_template.tpl:
101 self.inputs = self._inputs()
102 self.relationship_templates = self._relationship_templates()
103 self.nodetemplates = self._nodetemplates()
104 self.outputs = self._outputs()
105 self.graph = ToscaGraph(self.nodetemplates)
107 ExceptionCollector.stop()
108 self.verify_template()
110 def _topology_template(self):
111 return TopologyTemplate(self._tpl_topology_template(),
112 self._get_all_custom_defs(),
113 self.relationship_types,
117 return self.topology_template.inputs
119 def _nodetemplates(self):
120 return self.topology_template.nodetemplates
122 def _relationship_templates(self):
123 return self.topology_template.relationship_templates
126 return self.topology_template.outputs
128 def _tpl_version(self):
129 return self.tpl.get(DEFINITION_VERSION)
131 def _tpl_description(self):
132 desc = self.tpl.get(DESCRIPTION)
136 def _tpl_imports(self):
137 return self.tpl.get(IMPORTS)
139 def _tpl_repositories(self):
140 repositories = self.tpl.get(REPOSITORIES)
143 for name, val in repositories.items():
144 reposits = Repository(name, val)
145 reposit.append(reposits)
148 def _tpl_relationship_types(self):
149 return self._get_custom_types(RELATIONSHIP_TYPES)
151 def _tpl_relationship_templates(self):
152 topology_template = self._tpl_topology_template()
153 return topology_template.get(RELATIONSHIP_TEMPLATES)
155 def _tpl_topology_template(self):
156 return self.tpl.get(TOPOLOGY_TEMPLATE)
158 def _get_all_custom_defs(self, imports=None):
159 types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
160 DATA_TYPES, POLICY_TYPES, GROUP_TYPES]
161 custom_defs_final = {}
162 custom_defs = self._get_custom_types(types, imports)
164 custom_defs_final.update(custom_defs)
165 if custom_defs.get(IMPORTS):
166 import_defs = self._get_all_custom_defs(
167 custom_defs.get(IMPORTS))
168 custom_defs_final.update(import_defs)
170 # As imports are not custom_types, removing from the dict
171 custom_defs_final.pop(IMPORTS, None)
172 return custom_defs_final
174 def _get_custom_types(self, type_definitions, imports=None):
175 """Handle custom types defined in imported template files
177 This method loads the custom type definitions referenced in "imports"
178 section of the TOSCA YAML template.
183 if not isinstance(type_definitions, list):
184 type_defs.append(type_definitions)
186 type_defs = type_definitions
189 imports = self._tpl_imports()
192 custom_service = toscaparser.imports.\
193 ImportsLoader(imports, self.path,
196 nested_topo_tpls = custom_service.get_nested_topo_tpls()
197 self._handle_nested_topo_tpls(nested_topo_tpls)
199 custom_defs = custom_service.get_custom_defs()
203 # Handle custom types defined in current template file
204 for type_def in type_defs:
205 if type_def != IMPORTS:
206 inner_custom_types = self.tpl.get(type_def) or {}
207 if inner_custom_types:
208 custom_defs.update(inner_custom_types)
211 def _handle_nested_topo_tpls(self, nested_topo_tpls):
212 for tpl in nested_topo_tpls:
213 filename, tosca_tpl = list(tpl.items())[0]
214 if tosca_tpl.get(TOPOLOGY_TEMPLATE):
215 nested_template = ToscaTemplate(
216 path=filename, parsed_params=self.parsed_params,
217 yaml_dict_tpl=tosca_tpl)
218 if nested_template.topology_template.substitution_mappings:
219 self.nested_tosca_template.apend(nested_template)
221 def _validate_field(self):
222 version = self._tpl_version()
224 ExceptionCollector.appendException(
225 MissingRequiredFieldError(what='Template',
226 required=DEFINITION_VERSION))
228 self._validate_version(version)
229 self.version = version
231 for name in self.tpl:
232 if (name not in SECTIONS and
233 name not in self.ADDITIONAL_SECTIONS.get(version, ())):
234 ExceptionCollector.appendException(
235 UnknownFieldError(what='Template', field=name))
237 def _validate_version(self, version):
238 if version not in self.VALID_TEMPLATE_VERSIONS:
239 ExceptionCollector.appendException(
240 InvalidTemplateVersion(
242 valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
244 if version != 'tosca_simple_yaml_1_0':
245 update_definitions(version)
247 def _get_path(self, path):
248 if path.lower().endswith('.yaml'):
250 elif path.lower().endswith(('.zip', '.csar')):
252 csar = CSAR(path, self.a_file)
255 self.a_file = True # the file has been decompressed locally
256 return os.path.join(csar.temp_dir, csar.get_main_template())
258 ExceptionCollector.appendException(
259 ValueError(_('"%(path)s" is not a valid file.')
262 def verify_template(self):
263 if ExceptionCollector.exceptionsCaught():
265 raise ValidationError(
266 message=(_('\nThe input "%(path)s" failed validation with '
267 'the following error(s): \n\n\t')
268 % {'path': self.input_path}) +
269 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
271 raise ValidationError(
272 message=_('\nThe pre-parsed input failed validation with '
273 'the following error(s): \n\n\t') +
274 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
277 msg = (_('The input "%(path)s" successfully passed '
278 'validation.') % {'path': self.input_path})
280 msg = _('The pre-parsed input successfully passed validation.')