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
18 from copy import deepcopy
19 from toscaparser.common.exception import ExceptionCollector
20 from toscaparser.common.exception import InvalidTemplateVersion
21 from toscaparser.common.exception import MissingRequiredFieldError
22 from toscaparser.common.exception import UnknownFieldError
23 from toscaparser.common.exception import ValidationError
24 from toscaparser.elements.entity_type import update_definitions
25 from toscaparser.extensions.exttools import ExtTools
26 import toscaparser.imports
27 from toscaparser.prereq.csar import CSAR
28 from toscaparser.repositories import Repository
29 from toscaparser.topology_template import TopologyTemplate
30 from toscaparser.tpl_relationship_graph import ToscaGraph
31 from toscaparser.utils.gettextutils import _
32 import toscaparser.utils.yamlparser
33 from org.openecomp.sdc.toscaparser.jython import JyToscaTemplate
36 # TOSCA template key names
37 SECTIONS = (DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME,
38 TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION,
39 DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES,
40 RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES,
41 CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES, INTERFACE_TYPES,
42 POLICY_TYPES, GROUP_TYPES, REPOSITORIES) = \
43 ('tosca_definitions_version', 'tosca_default_namespace',
44 'template_name', 'topology_template', 'template_author',
45 'template_version', 'description', 'imports', 'dsl_definitions',
46 'node_types', 'relationship_types', 'relationship_templates',
47 'capability_types', 'artifact_types', 'data_types',
48 'interface_types', 'policy_types', 'group_types', 'repositories')
49 # Sections that are specific to individual template definitions
50 SPECIAL_SECTIONS = (METADATA) = ('metadata')
52 log = logging.getLogger("tosca.model")
54 YAML_LOADER = toscaparser.utils.yamlparser.load_yaml
57 class ToscaTemplate(JyToscaTemplate):
60 VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0']
62 VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
64 ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS}
66 ADDITIONAL_SECTIONS.update(exttools.get_sections())
68 '''Load the template data.'''
69 def __init__(self, path=None, parsed_params=None, a_file=True,
72 ExceptionCollector.start()
74 self.input_path = None
77 self.csar_tempdir = None
78 self.nested_tosca_tpls_with_topology = {}
79 self.nested_tosca_templates_with_topology = []
81 self.input_path = path
82 self.path = self._get_path(path)
84 self.tpl = YAML_LOADER(self.path, self.a_file)
86 msg = (_('Both path and yaml_dict_tpl arguments were '
87 'provided. Using path and ignoring yaml_dict_tpl.'))
92 self.tpl = yaml_dict_tpl
94 ExceptionCollector.appendException(
95 ValueError(_('No path or yaml_dict_tpl was provided. '
96 'There is nothing to parse.')))
99 self.parsed_params = parsed_params
100 self._validate_field()
101 self.version = self._tpl_version()
102 self.metadata = self._tpl_metadata()
103 self.relationship_types = self._tpl_relationship_types()
104 self.description = self._tpl_description()
105 self.topology_template = self._topology_template()
106 self.repositories = self._tpl_repositories()
107 if self.topology_template.tpl:
108 self.inputs = self._inputs()
109 self.relationship_templates = self._relationship_templates()
110 self.nodetemplates = self._nodetemplates()
111 self.outputs = self._outputs()
112 self._handle_nested_tosca_templates_with_topology()
113 self.graph = ToscaGraph(self.nodetemplates)
115 if self.csar_tempdir:
116 #shutil.rmtree(self.csar_tempdir)
119 ExceptionCollector.stop()
120 self.verify_template()
122 def getJyVersion(self):
125 def getJyMetadata(self):
128 def getJyDescription(self):
129 return self.description
131 def getJyTopologyTemplate(self):
132 return self.topology_template
134 def getJyNestedTopologyTemplates(self):
135 return self.nested_tosca_templates_with_topology
137 def _topology_template(self):
138 return TopologyTemplate(self._tpl_topology_template(),
139 self._get_all_custom_defs(),
140 self.relationship_types,
145 return self.topology_template.inputs
147 def _nodetemplates(self):
148 return self.topology_template.nodetemplates
150 def _relationship_templates(self):
151 return self.topology_template.relationship_templates
154 return self.topology_template.outputs
156 def _tpl_version(self):
157 return self.tpl.get(DEFINITION_VERSION)
159 def _tpl_metadata(self):
160 return self.tpl.get(METADATA)
162 def _tpl_description(self):
163 desc = self.tpl.get(DESCRIPTION)
167 def _tpl_imports(self):
168 return self.tpl.get(IMPORTS)
170 def _tpl_repositories(self):
171 repositories = self.tpl.get(REPOSITORIES)
174 for name, val in repositories.items():
175 reposits = Repository(name, val)
176 reposit.append(reposits)
179 def _tpl_relationship_types(self):
180 return self._get_custom_types(RELATIONSHIP_TYPES)
182 def _tpl_relationship_templates(self):
183 topology_template = self._tpl_topology_template()
184 return topology_template.get(RELATIONSHIP_TEMPLATES)
186 def _tpl_topology_template(self):
187 return self.tpl.get(TOPOLOGY_TEMPLATE)
189 def _get_all_custom_defs(self, imports=None):
190 types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
191 DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES]
192 custom_defs_final = {}
193 custom_defs = self._get_custom_types(types, imports)
195 custom_defs_final.update(custom_defs)
196 if custom_defs.get(IMPORTS):
197 import_defs = self._get_all_custom_defs(
198 custom_defs.get(IMPORTS))
199 custom_defs_final.update(import_defs)
201 # As imports are not custom_types, removing from the dict
202 custom_defs_final.pop(IMPORTS, None)
203 return custom_defs_final
205 def _get_custom_types(self, type_definitions, imports=None):
206 """Handle custom types defined in imported template files
208 This method loads the custom type definitions referenced in "imports"
209 section of the TOSCA YAML template.
214 if not isinstance(type_definitions, list):
215 type_defs.append(type_definitions)
217 type_defs = type_definitions
220 imports = self._tpl_imports()
223 custom_service = toscaparser.imports.\
224 ImportsLoader(imports, self.path,
227 nested_tosca_tpls = custom_service.get_nested_tosca_tpls()
228 self._update_nested_tosca_tpls_with_topology(nested_tosca_tpls)
230 custom_defs = custom_service.get_custom_defs()
234 # Handle custom types defined in current template file
235 for type_def in type_defs:
236 if type_def != IMPORTS:
237 inner_custom_types = self.tpl.get(type_def) or {}
238 if inner_custom_types:
239 custom_defs.update(inner_custom_types)
242 def _update_nested_tosca_tpls_with_topology(self, nested_tosca_tpls):
243 for tpl in nested_tosca_tpls:
244 filename, tosca_tpl = list(tpl.items())[0]
245 if (tosca_tpl.get(TOPOLOGY_TEMPLATE) and
246 filename not in list(
247 self.nested_tosca_tpls_with_topology.keys())):
248 self.nested_tosca_tpls_with_topology.update(tpl)
250 def _handle_nested_tosca_templates_with_topology(self):
251 for fname, tosca_tpl in self.nested_tosca_tpls_with_topology.items():
252 for nodetemplate in self.nodetemplates:
253 if self._is_sub_mapped_node(nodetemplate, tosca_tpl):
254 parsed_params = self._get_params_for_nested_template(
256 topology_tpl = tosca_tpl.get(TOPOLOGY_TEMPLATE)
257 topology_with_sub_mapping = TopologyTemplate(
259 self._get_all_custom_defs(),
260 self.relationship_types,
263 if topology_with_sub_mapping.substitution_mappings:
264 # Record nested topo templates in top level template
265 self.nested_tosca_templates_with_topology.\
266 append(topology_with_sub_mapping)
267 # Set substitution mapping object for mapped node
268 nodetemplate.sub_mapping_tosca_template = \
269 topology_with_sub_mapping.substitution_mappings
271 def _validate_field(self):
272 version = self._tpl_version()
274 ExceptionCollector.appendException(
275 MissingRequiredFieldError(what='Template',
276 required=DEFINITION_VERSION))
278 self._validate_version(version)
279 self.version = version
281 for name in self.tpl:
282 if (name not in SECTIONS and
283 name not in self.ADDITIONAL_SECTIONS.get(version, ())):
284 ExceptionCollector.appendException(
285 UnknownFieldError(what='Template', field=name))
287 def _validate_version(self, version):
288 if version not in self.VALID_TEMPLATE_VERSIONS:
289 ExceptionCollector.appendException(
290 InvalidTemplateVersion(
292 valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
294 if version != 'tosca_simple_yaml_1_0':
295 update_definitions(version)
297 def _get_path(self, path):
298 if path.lower().endswith('.yaml'):
300 elif path.lower().endswith(('.zip', '.csar')):
302 csar = CSAR(path, self.a_file)
305 self.a_file = True # the file has been decompressed locally
306 self.csar_tempdir = csar.temp_dir
307 return os.path.join(csar.temp_dir, csar.get_main_template())
309 ExceptionCollector.appendException(
310 ValueError(_('"%(path)s" is not a valid file.')
313 def verify_template(self):
314 if ExceptionCollector.exceptionsCaught():
316 raise ValidationError(
317 message=(_('\nThe input "%(path)s" failed validation with '
318 'the following error(s): \n\n\t')
319 % {'path': self.input_path}) +
320 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
322 raise ValidationError(
323 message=_('\nThe pre-parsed input failed validation with '
324 'the following error(s): \n\n\t') +
325 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
328 msg = (_('The input "%(path)s" successfully passed '
329 'validation.') % {'path': self.input_path})
331 msg = _('The pre-parsed input successfully passed validation.')
335 def _is_sub_mapped_node(self, nodetemplate, tosca_tpl):
336 """Return True if the nodetemple is substituted."""
337 if (nodetemplate and not nodetemplate.sub_mapping_tosca_template and
338 self.get_sub_mapping_node_type(tosca_tpl) == nodetemplate.type
339 and len(nodetemplate.interfaces) < 1):
344 def _get_params_for_nested_template(self, nodetemplate):
345 """Return total params for nested_template."""
346 parsed_params = deepcopy(self.parsed_params) \
347 if self.parsed_params else {}
349 for pname in nodetemplate.get_properties():
350 parsed_params.update({pname:
351 nodetemplate.get_property_value(pname)})
354 def get_sub_mapping_node_type(self, tosca_tpl):
355 """Return substitution mappings node type."""
357 return TopologyTemplate.get_sub_mapping_node_type(
358 tosca_tpl.get(TOPOLOGY_TEMPLATE))
360 def _has_substitution_mappings(self):
361 """Return True if the template has valid substitution mappings."""
362 return self.topology_template is not None and \
363 self.topology_template.substitution_mappings is not None
365 def has_nested_templates(self):
366 """Return True if the tosca template has nested templates."""
367 return self.nested_tosca_templates_with_topology is not None and \
368 len(self.nested_tosca_templates_with_topology) >= 1