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,
67 yaml_dict_tpl=None, sub_mapped_node_template=None):
68 if sub_mapped_node_template is None:
69 ExceptionCollector.start()
71 self.input_path = None
74 self.sub_mapped_node_template = sub_mapped_node_template
75 self.nested_tosca_tpls_with_topology = {}
76 self.nested_tosca_templates_with_topology = []
78 self.input_path = path
79 self.path = self._get_path(path)
81 self.tpl = YAML_LOADER(self.path, self.a_file)
83 msg = (_('Both path and yaml_dict_tpl arguments were '
84 'provided. Using path and ignoring yaml_dict_tpl.'))
89 self.tpl = yaml_dict_tpl
91 ExceptionCollector.appendException(
92 ValueError(_('No path or yaml_dict_tpl was provided. '
93 'There is nothing to parse.')))
96 self.parsed_params = parsed_params
97 self._validate_field()
98 self.version = self._tpl_version()
99 self.relationship_types = self._tpl_relationship_types()
100 self.description = self._tpl_description()
101 self.topology_template = self._topology_template()
102 self.repositories = self._tpl_repositories()
103 if self.topology_template.tpl:
104 self.inputs = self._inputs()
105 self.relationship_templates = self._relationship_templates()
106 self.nodetemplates = self._nodetemplates()
107 self.outputs = self._outputs()
108 self._handle_nested_tosca_templates_with_topology()
109 self.graph = ToscaGraph(self.nodetemplates)
111 if sub_mapped_node_template is None:
112 ExceptionCollector.stop()
113 self.verify_template()
115 def _topology_template(self):
116 return TopologyTemplate(self._tpl_topology_template(),
117 self._get_all_custom_defs(),
118 self.relationship_types,
120 self.sub_mapped_node_template)
123 return self.topology_template.inputs
125 def _nodetemplates(self):
126 return self.topology_template.nodetemplates
128 def _relationship_templates(self):
129 return self.topology_template.relationship_templates
132 return self.topology_template.outputs
134 def _tpl_version(self):
135 return self.tpl.get(DEFINITION_VERSION)
137 def _tpl_description(self):
138 desc = self.tpl.get(DESCRIPTION)
142 def _tpl_imports(self):
143 return self.tpl.get(IMPORTS)
145 def _tpl_repositories(self):
146 repositories = self.tpl.get(REPOSITORIES)
149 for name, val in repositories.items():
150 reposits = Repository(name, val)
151 reposit.append(reposits)
154 def _tpl_relationship_types(self):
155 return self._get_custom_types(RELATIONSHIP_TYPES)
157 def _tpl_relationship_templates(self):
158 topology_template = self._tpl_topology_template()
159 return topology_template.get(RELATIONSHIP_TEMPLATES)
161 def _tpl_topology_template(self):
162 return self.tpl.get(TOPOLOGY_TEMPLATE)
164 def _get_all_custom_defs(self, imports=None):
165 types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
166 DATA_TYPES, POLICY_TYPES, GROUP_TYPES]
167 custom_defs_final = {}
168 custom_defs = self._get_custom_types(types, imports)
170 custom_defs_final.update(custom_defs)
171 if custom_defs.get(IMPORTS):
172 import_defs = self._get_all_custom_defs(
173 custom_defs.get(IMPORTS))
174 custom_defs_final.update(import_defs)
176 # As imports are not custom_types, removing from the dict
177 custom_defs_final.pop(IMPORTS, None)
178 return custom_defs_final
180 def _get_custom_types(self, type_definitions, imports=None):
181 """Handle custom types defined in imported template files
183 This method loads the custom type definitions referenced in "imports"
184 section of the TOSCA YAML template.
189 if not isinstance(type_definitions, list):
190 type_defs.append(type_definitions)
192 type_defs = type_definitions
195 imports = self._tpl_imports()
199 toscaparser.imports.ImportsLoader(imports, self.path,
202 nested_tosca_tpls = custom_service.get_nested_tosca_tpls()
203 self._update_nested_tosca_tpls_with_topology(nested_tosca_tpls)
205 custom_defs = custom_service.get_custom_defs()
209 # Handle custom types defined in current template file
210 for type_def in type_defs:
211 if type_def != IMPORTS:
212 inner_custom_types = self.tpl.get(type_def) or {}
213 if inner_custom_types:
214 custom_defs.update(inner_custom_types)
217 def _update_nested_tosca_tpls_with_topology(self, nested_tosca_tpls):
218 for tpl in nested_tosca_tpls:
219 filename, tosca_tpl = list(tpl.items())[0]
220 if (tosca_tpl.get(TOPOLOGY_TEMPLATE) and
221 filename not in list(
222 self.nested_tosca_tpls_with_topology.keys())):
223 self.nested_tosca_tpls_with_topology.update(tpl)
225 def _handle_nested_tosca_templates_with_topology(self):
226 for fname, tosca_tpl in self.nested_tosca_tpls_with_topology.items():
227 for nodetemplate in self.nodetemplates:
228 if self._is_sub_mapped_node(nodetemplate, tosca_tpl):
229 nested_template = ToscaTemplate(
230 path=fname, parsed_params=self.parsed_params,
231 yaml_dict_tpl=tosca_tpl,
232 sub_mapped_node_template=nodetemplate)
233 if nested_template.has_substitution_mappings():
234 # Record the nested templates in top level template
235 self.nested_tosca_templates_with_topology.\
236 append(nested_template)
237 # Set the substitution toscatemplate for mapped node
238 nodetemplate.sub_mapping_tosca_template = \
241 def _validate_field(self):
242 version = self._tpl_version()
244 ExceptionCollector.appendException(
245 MissingRequiredFieldError(what='Template',
246 required=DEFINITION_VERSION))
248 self._validate_version(version)
249 self.version = version
251 for name in self.tpl:
252 if (name not in SECTIONS and
253 name not in self.ADDITIONAL_SECTIONS.get(version, ())):
254 ExceptionCollector.appendException(
255 UnknownFieldError(what='Template', field=name))
257 def _validate_version(self, version):
258 if version not in self.VALID_TEMPLATE_VERSIONS:
259 ExceptionCollector.appendException(
260 InvalidTemplateVersion(
262 valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
264 if version != 'tosca_simple_yaml_1_0':
265 update_definitions(version)
267 def _get_path(self, path):
268 if path.lower().endswith('.yaml'):
270 elif path.lower().endswith(('.zip', '.csar')):
272 csar = CSAR(path, self.a_file)
275 self.a_file = True # the file has been decompressed locally
276 return os.path.join(csar.temp_dir, csar.get_main_template())
278 ExceptionCollector.appendException(
279 ValueError(_('"%(path)s" is not a valid file.')
282 def verify_template(self):
283 if ExceptionCollector.exceptionsCaught():
285 raise ValidationError(
286 message=(_('\nThe input "%(path)s" failed validation with '
287 'the following error(s): \n\n\t')
288 % {'path': self.input_path}) +
289 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
291 raise ValidationError(
292 message=_('\nThe pre-parsed input failed validation with '
293 'the following error(s): \n\n\t') +
294 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
297 msg = (_('The input "%(path)s" successfully passed '
298 'validation.') % {'path': self.input_path})
300 msg = _('The pre-parsed input successfully passed validation.')
304 def _is_sub_mapped_node(self, nodetemplate, tosca_tpl):
305 """Return True if the nodetemple is substituted."""
306 if (nodetemplate and not nodetemplate.substitution_mapped and
307 self.get_sub_mapping_node_type(tosca_tpl) == nodetemplate.type
308 and len(nodetemplate.interfaces) < 1):
313 def get_sub_mapping_node_type(self, tosca_tpl):
314 """Return substitution mappings node type."""
316 return TopologyTemplate.get_sub_mapping_node_type(
317 tosca_tpl.get(TOPOLOGY_TEMPLATE))
319 def has_substitution_mappings(self):
320 """Return True if the template has valid substitution mappings."""
321 return self.topology_template is not None and \
322 self.topology_template.substitution_mappings is not None
324 def has_nested_templates(self):
325 """Return True if the tosca template has nested templates."""
326 return self.nested_tosca_templates_with_topology is not None and \
327 len(self.nested_tosca_templates_with_topology) >= 1