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 copy import deepcopy
18 from toscaparser.common.exception import ExceptionCollector
19 from toscaparser.common.exception import InvalidTemplateVersion
20 from toscaparser.common.exception import MissingRequiredFieldError
21 from toscaparser.common.exception import MissingRequiredInputError
22 from toscaparser.common.exception import MissingRequiredOutputError
23 from toscaparser.common.exception import MissingRequiredParameterError
24 from toscaparser.common.exception import UnknownFieldError
25 from toscaparser.common.exception import ValidationError
26 from toscaparser.elements.entity_type import update_definitions
27 from toscaparser.extensions.exttools import ExtTools
28 import toscaparser.imports
29 from toscaparser.prereq.csar import CSAR
30 from toscaparser.repositories import Repository
31 from toscaparser.topology_template import TopologyTemplate
32 from toscaparser.tpl_relationship_graph import ToscaGraph
33 from toscaparser.utils.gettextutils import _
34 import toscaparser.utils.yamlparser
37 # TOSCA template key names
38 SECTIONS = (DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME,
39 TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION,
40 DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES,
41 RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES,
42 CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES, INTERFACE_TYPES,
43 POLICY_TYPES, GROUP_TYPES, REPOSITORIES) = \
44 ('tosca_definitions_version', 'tosca_default_namespace',
45 'template_name', 'topology_template', 'template_author',
46 'template_version', 'description', 'imports', 'dsl_definitions',
47 'node_types', 'relationship_types', 'relationship_templates',
48 'capability_types', 'artifact_types', 'data_types',
49 'interface_types', 'policy_types', 'group_types', 'repositories')
50 # Sections that are specific to individual template definitions
51 SPECIAL_SECTIONS = (METADATA) = ('metadata')
53 log = logging.getLogger("tosca.model")
55 YAML_LOADER = toscaparser.utils.yamlparser.load_yaml
58 class ToscaTemplate(object):
61 VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0',
62 'tosca_simple_yaml_1_1']
64 VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
66 ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS,
67 'tosca_simple_yaml_1_1': SPECIAL_SECTIONS}
69 ADDITIONAL_SECTIONS.update(exttools.get_sections())
71 '''Load the template data.'''
72 def __init__(self, path=None, parsed_params=None, a_file=True,
73 yaml_dict_tpl=None, sub_mapped_node_template=None,
74 no_required_paras_check=False,
76 if sub_mapped_node_template is None:
77 ExceptionCollector.start()
79 self.input_path = None
82 self.sub_mapped_node_template = sub_mapped_node_template
83 self.nested_tosca_tpls_with_topology = {}
84 self.nested_tosca_templates_with_topology = []
85 self.no_required_paras_check = no_required_paras_check
86 self.debug_mode = debug_mode
89 self.input_path = path
90 self.path = self._get_path(path)
92 self.tpl = YAML_LOADER(self.path, self.a_file)
94 msg = (_('Both path and yaml_dict_tpl arguments were '
95 'provided. Using path and ignoring yaml_dict_tpl.'))
100 self.tpl = yaml_dict_tpl
102 ExceptionCollector.appendException(
103 ValueError(_('No path or yaml_dict_tpl was provided. '
104 'There is nothing to parse.')))
107 self.parsed_params = parsed_params
108 self._validate_field()
109 self.version = self._tpl_version()
110 self.relationship_types = self._tpl_relationship_types()
111 self.description = self._tpl_description()
112 self.topology_template = self._topology_template()
113 self.repositories = self._tpl_repositories()
114 if self.topology_template.tpl:
115 self.inputs = self._inputs()
116 self.relationship_templates = self._relationship_templates()
117 self.nodetemplates = self._nodetemplates()
118 self.outputs = self._outputs()
119 self.policies = self._policies()
120 self._handle_nested_tosca_templates_with_topology()
121 self.graph = ToscaGraph(self.nodetemplates)
123 if sub_mapped_node_template is None:
124 ExceptionCollector.stop()
125 self.verify_template()
127 def _topology_template(self):
128 return TopologyTemplate(self._tpl_topology_template(),
129 self._get_all_custom_defs(),
130 self.relationship_types,
132 self.sub_mapped_node_template)
135 return self.topology_template.inputs
137 def _nodetemplates(self):
138 return self.topology_template.nodetemplates
140 def _relationship_templates(self):
141 return self.topology_template.relationship_templates
144 return self.topology_template.outputs
146 def _tpl_version(self):
147 return self.tpl.get(DEFINITION_VERSION)
149 def _tpl_description(self):
150 desc = self.tpl.get(DESCRIPTION)
154 def _tpl_imports(self):
155 return self.tpl.get(IMPORTS)
157 def _tpl_repositories(self):
158 repositories = self.tpl.get(REPOSITORIES)
161 for name, val in repositories.items():
162 reposits = Repository(name, val)
163 reposit.append(reposits)
166 def _tpl_relationship_types(self):
167 return self._get_custom_types(RELATIONSHIP_TYPES)
169 def _tpl_relationship_templates(self):
170 topology_template = self._tpl_topology_template()
171 return topology_template.get(RELATIONSHIP_TEMPLATES)
173 def _tpl_topology_template(self):
174 return self.tpl.get(TOPOLOGY_TEMPLATE)
177 return self.topology_template.policies
179 def _get_all_custom_defs(self, imports=None):
180 types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
181 DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES]
182 custom_defs_final = {}
183 custom_defs = self._get_custom_types(types, imports)
185 custom_defs_final.update(custom_defs)
186 if custom_defs.get(IMPORTS):
187 import_defs = self._get_all_custom_defs(
188 custom_defs.get(IMPORTS))
189 custom_defs_final.update(import_defs)
191 # As imports are not custom_types, removing from the dict
192 custom_defs_final.pop(IMPORTS, None)
193 return custom_defs_final
195 def _get_custom_types(self, type_definitions, imports=None):
196 """Handle custom types defined in imported template files
198 This method loads the custom type definitions referenced in "imports"
199 section of the TOSCA YAML template.
204 if not isinstance(type_definitions, list):
205 type_defs.append(type_definitions)
207 type_defs = type_definitions
210 imports = self._tpl_imports()
213 custom_service = toscaparser.imports.\
214 ImportsLoader(imports, self.path,
217 nested_tosca_tpls = custom_service.get_nested_tosca_tpls()
218 self._update_nested_tosca_tpls_with_topology(nested_tosca_tpls)
220 custom_defs = custom_service.get_custom_defs()
224 # Handle custom types defined in current template file
225 for type_def in type_defs:
226 if type_def != IMPORTS:
227 inner_custom_types = self.tpl.get(type_def) or {}
228 if inner_custom_types:
229 custom_defs.update(inner_custom_types)
232 def _update_nested_tosca_tpls_with_topology(self, nested_tosca_tpls):
233 for tpl in nested_tosca_tpls:
234 filename, tosca_tpl = list(tpl.items())[0]
235 if (tosca_tpl.get(TOPOLOGY_TEMPLATE) and
236 filename not in list(
237 self.nested_tosca_tpls_with_topology.keys())):
238 self.nested_tosca_tpls_with_topology.update(tpl)
240 def _handle_nested_tosca_templates_with_topology(self):
241 for fname, tosca_tpl in self.nested_tosca_tpls_with_topology.items():
242 for nodetemplate in self.nodetemplates:
243 if self._is_sub_mapped_node(nodetemplate, tosca_tpl):
244 parsed_params = self._get_params_for_nested_template(
247 cache_exeptions = deepcopy(ExceptionCollector.exceptions)
248 cache_exeptions_state = \
249 deepcopy(ExceptionCollector.collecting)
250 nested_template = None
252 nrpv = self.no_required_paras_check
253 nested_template = ToscaTemplate(
254 path=fname, parsed_params=parsed_params,
255 sub_mapped_node_template=nodetemplate,
256 no_required_paras_check=nrpv,
257 debug_mode=self.debug_mode)
258 except ValidationError as e:
265 ExceptionCollector.exceptions = deepcopy(cache_exeptions)
266 ExceptionCollector.collecting = \
267 deepcopy(cache_exeptions_state)
269 if nested_template and \
270 nested_template._has_substitution_mappings():
271 # Record the nested templates in top level template
272 self.nested_tosca_templates_with_topology.\
273 append(nested_template)
274 # Set the substitution toscatemplate for mapped node
275 nodetemplate.substitution_mapped = \
278 def _validate_field(self):
279 version = self._tpl_version()
281 ExceptionCollector.appendException(
282 MissingRequiredFieldError(what='Template',
283 required=DEFINITION_VERSION))
285 self._validate_version(version)
286 self.version = version
288 for name in self.tpl:
289 if (name not in SECTIONS and
290 name not in self.ADDITIONAL_SECTIONS.get(version, ())):
291 ExceptionCollector.appendException(
292 UnknownFieldError(what='Template', field=name))
294 def _validate_version(self, version):
295 if version not in self.VALID_TEMPLATE_VERSIONS:
296 ExceptionCollector.appendException(
297 InvalidTemplateVersion(
299 valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
301 if (version != 'tosca_simple_yaml_1_0' and
302 version != 'tosca_simple_yaml_1_1'):
303 update_definitions(version)
305 def _get_path(self, path):
306 if path.lower().endswith('.yaml') or path.lower().endswith('.yml'):
308 elif path.lower().endswith(('.zip', '.csar')):
310 csar = CSAR(path, self.a_file)
313 self.a_file = True # the file has been decompressed locally
314 return os.path.join(csar.temp_dir, csar.get_main_template())
316 ExceptionCollector.appendException(
317 ValueError(_('"%(path)s" is not a valid file.')
320 def verify_template(self):
321 if self.no_required_paras_check:
322 ExceptionCollector.removeException(
323 MissingRequiredParameterError)
324 ExceptionCollector.removeException(
325 MissingRequiredInputError)
326 ExceptionCollector.removeException(
327 MissingRequiredOutputError)
328 if ExceptionCollector.exceptionsCaught():
330 exceptions = ValidationError(
331 message=(_('\nThe input "%(path)s" failed validation with '
332 'the following error(s): \n\n\t')
333 % {'path': self.input_path}) +
334 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
336 exceptions = ValidationError(
337 message=_('\nThe pre-parsed input failed validation with '
338 'the following error(s): \n\n\t') +
339 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
340 if not self.debug_mode:
343 if self.sub_mapped_node_template:
344 msg = _('======== nested service template ======== ')
347 msg = _('======== main service template ======== ')
349 print(exceptions.message)
351 log.error(exceptions.message)
354 msg = (_('The input "%(path)s" successfully passed '
355 'validation.') % {'path': self.input_path})
357 msg = _('The pre-parsed input successfully passed validation.')
361 def _is_sub_mapped_node(self, nodetemplate, tosca_tpl):
362 """Return True if the nodetemple is substituted."""
363 if (nodetemplate and not nodetemplate.substitution_mapped and
364 self.get_sub_mapping_node_type(tosca_tpl) == nodetemplate.type
365 and len(nodetemplate.interfaces) < 1):
370 def _get_params_for_nested_template(self, nodetemplate):
371 """Return total params for nested_template."""
372 parsed_params = deepcopy(self.parsed_params) \
373 if self.parsed_params else {}
375 for pname in nodetemplate.get_properties():
376 parsed_params.update({pname:
377 nodetemplate.get_property_value(pname)})
380 def get_sub_mapping_node_type(self, tosca_tpl):
381 """Return substitution mappings node type."""
383 return TopologyTemplate.get_sub_mapping_node_type(
384 tosca_tpl.get(TOPOLOGY_TEMPLATE))
386 def _has_substitution_mappings(self):
387 """Return True if the template has valid substitution mappings."""
388 return self.topology_template is not None and \
389 self.topology_template.substitution_mappings is not None
391 def has_nested_templates(self):
392 """Return True if the tosca template has nested templates."""
393 return self.nested_tosca_templates_with_topology is not None and \
394 len(self.nested_tosca_templates_with_topology) >= 1