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 UnknownFieldError
22 from toscaparser.common.exception import ValidationError
23 from toscaparser.elements.entity_type import update_definitions
24 from toscaparser.extensions.exttools import ExtTools
25 import toscaparser.imports
26 from toscaparser.prereq.csar import CSAR
27 from toscaparser.repositories import Repository
28 from toscaparser.topology_template import TopologyTemplate
29 from toscaparser.tpl_relationship_graph import ToscaGraph
30 from toscaparser.utils.gettextutils import _
31 import toscaparser.utils.yamlparser
32 from org.openecomp.sdc.toscaparser.jython import JyToscaTemplate
35 # TOSCA template key names
36 SECTIONS = (DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME,
37 TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION,
38 DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES,
39 RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES,
40 CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES, INTERFACE_TYPES,
41 POLICY_TYPES, GROUP_TYPES, REPOSITORIES) = \
42 ('tosca_definitions_version', 'tosca_default_namespace',
43 'template_name', 'topology_template', 'template_author',
44 'template_version', 'description', 'imports', 'dsl_definitions',
45 'node_types', 'relationship_types', 'relationship_templates',
46 'capability_types', 'artifact_types', 'data_types',
47 'interface_types', 'policy_types', 'group_types', 'repositories')
48 # Sections that are specific to individual template definitions
49 SPECIAL_SECTIONS = (METADATA) = ('metadata')
51 log = logging.getLogger("tosca.model")
53 YAML_LOADER = toscaparser.utils.yamlparser.load_yaml
56 class ToscaTemplate(JyToscaTemplate):
59 VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0']
61 VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
63 ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS}
65 ADDITIONAL_SECTIONS.update(exttools.get_sections())
67 '''Load the template data.'''
68 def __init__(self, path=None, parsed_params=None, a_file=True,
71 ExceptionCollector.start()
73 self.input_path = None
76 self.nested_tosca_tpls_with_topology = {}
77 self.nested_tosca_templates_with_topology = []
79 self.input_path = path
80 self.path = self._get_path(path)
82 self.tpl = YAML_LOADER(self.path, self.a_file)
84 msg = (_('Both path and yaml_dict_tpl arguments were '
85 'provided. Using path and ignoring yaml_dict_tpl.'))
90 self.tpl = yaml_dict_tpl
92 ExceptionCollector.appendException(
93 ValueError(_('No path or yaml_dict_tpl was provided. '
94 'There is nothing to parse.')))
97 self.parsed_params = parsed_params
98 self._validate_field()
99 self.version = self._tpl_version()
100 self.relationship_types = self._tpl_relationship_types()
101 self.description = self._tpl_description()
102 self.topology_template = self._topology_template()
103 self.repositories = self._tpl_repositories()
104 if self.topology_template.tpl:
105 self.inputs = self._inputs()
106 self.relationship_templates = self._relationship_templates()
107 self.nodetemplates = self._nodetemplates()
108 self.outputs = self._outputs()
109 self._handle_nested_tosca_templates_with_topology()
110 self.graph = ToscaGraph(self.nodetemplates)
112 ExceptionCollector.stop()
113 self.verify_template()
115 def getJyVersion(self):
118 def getJyDescription(self):
119 return self.description
121 def getJyTopologyTemplate(self):
122 return self.topology_template
124 def getJyNestedTopologyTemplates(self):
125 return self.nested_tosca_templates_with_topology
127 def _topology_template(self):
128 return TopologyTemplate(self._tpl_topology_template(),
129 self._get_all_custom_defs(),
130 self.relationship_types,
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)
176 def _get_all_custom_defs(self, imports=None):
177 types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
178 DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES]
179 custom_defs_final = {}
180 custom_defs = self._get_custom_types(types, imports)
182 custom_defs_final.update(custom_defs)
183 if custom_defs.get(IMPORTS):
184 import_defs = self._get_all_custom_defs(
185 custom_defs.get(IMPORTS))
186 custom_defs_final.update(import_defs)
188 # As imports are not custom_types, removing from the dict
189 custom_defs_final.pop(IMPORTS, None)
190 return custom_defs_final
192 def _get_custom_types(self, type_definitions, imports=None):
193 """Handle custom types defined in imported template files
195 This method loads the custom type definitions referenced in "imports"
196 section of the TOSCA YAML template.
201 if not isinstance(type_definitions, list):
202 type_defs.append(type_definitions)
204 type_defs = type_definitions
207 imports = self._tpl_imports()
210 custom_service = toscaparser.imports.\
211 ImportsLoader(imports, self.path,
214 nested_tosca_tpls = custom_service.get_nested_tosca_tpls()
215 self._update_nested_tosca_tpls_with_topology(nested_tosca_tpls)
217 custom_defs = custom_service.get_custom_defs()
221 # Handle custom types defined in current template file
222 for type_def in type_defs:
223 if type_def != IMPORTS:
224 inner_custom_types = self.tpl.get(type_def) or {}
225 if inner_custom_types:
226 custom_defs.update(inner_custom_types)
229 def _update_nested_tosca_tpls_with_topology(self, nested_tosca_tpls):
230 for tpl in nested_tosca_tpls:
231 filename, tosca_tpl = list(tpl.items())[0]
232 if (tosca_tpl.get(TOPOLOGY_TEMPLATE) and
233 filename not in list(
234 self.nested_tosca_tpls_with_topology.keys())):
235 self.nested_tosca_tpls_with_topology.update(tpl)
237 def _handle_nested_tosca_templates_with_topology(self):
238 for fname, tosca_tpl in self.nested_tosca_tpls_with_topology.items():
239 for nodetemplate in self.nodetemplates:
240 if self._is_sub_mapped_node(nodetemplate, tosca_tpl):
241 parsed_params = self._get_params_for_nested_template(
243 topology_tpl = tosca_tpl.get(TOPOLOGY_TEMPLATE)
244 topology_with_sub_mapping = TopologyTemplate(
246 self._get_all_custom_defs(),
247 self.relationship_types,
250 if topology_with_sub_mapping.substitution_mappings:
251 # Record nested topo templates in top level template
252 self.nested_tosca_templates_with_topology.\
253 append(topology_with_sub_mapping)
254 # Set substitution mapping object for mapped node
255 nodetemplate.sub_mapping_tosca_template = \
256 topology_with_sub_mapping.substitution_mappings
258 def _validate_field(self):
259 version = self._tpl_version()
261 ExceptionCollector.appendException(
262 MissingRequiredFieldError(what='Template',
263 required=DEFINITION_VERSION))
265 self._validate_version(version)
266 self.version = version
268 for name in self.tpl:
269 if (name not in SECTIONS and
270 name not in self.ADDITIONAL_SECTIONS.get(version, ())):
271 ExceptionCollector.appendException(
272 UnknownFieldError(what='Template', field=name))
274 def _validate_version(self, version):
275 if version not in self.VALID_TEMPLATE_VERSIONS:
276 ExceptionCollector.appendException(
277 InvalidTemplateVersion(
279 valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
281 if version != 'tosca_simple_yaml_1_0':
282 update_definitions(version)
284 def _get_path(self, path):
285 if path.lower().endswith('.yaml'):
287 elif path.lower().endswith(('.zip', '.csar')):
289 csar = CSAR(path, self.a_file)
292 self.a_file = True # the file has been decompressed locally
293 return os.path.join(csar.temp_dir, csar.get_main_template())
295 ExceptionCollector.appendException(
296 ValueError(_('"%(path)s" is not a valid file.')
299 def verify_template(self):
300 if ExceptionCollector.exceptionsCaught():
302 raise ValidationError(
303 message=(_('\nThe input "%(path)s" failed validation with '
304 'the following error(s): \n\n\t')
305 % {'path': self.input_path}) +
306 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
308 raise ValidationError(
309 message=_('\nThe pre-parsed input failed validation with '
310 'the following error(s): \n\n\t') +
311 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
314 msg = (_('The input "%(path)s" successfully passed '
315 'validation.') % {'path': self.input_path})
317 msg = _('The pre-parsed input successfully passed validation.')
321 def _is_sub_mapped_node(self, nodetemplate, tosca_tpl):
322 """Return True if the nodetemple is substituted."""
323 if (nodetemplate and not nodetemplate.sub_mapping_tosca_template and
324 self.get_sub_mapping_node_type(tosca_tpl) == nodetemplate.type
325 and len(nodetemplate.interfaces) < 1):
330 def _get_params_for_nested_template(self, nodetemplate):
331 """Return total params for nested_template."""
332 parsed_params = deepcopy(self.parsed_params) \
333 if self.parsed_params else {}
335 for pname in nodetemplate.get_properties():
336 parsed_params.update({pname:
337 nodetemplate.get_property_value(pname)})
340 def get_sub_mapping_node_type(self, tosca_tpl):
341 """Return substitution mappings node type."""
343 return TopologyTemplate.get_sub_mapping_node_type(
344 tosca_tpl.get(TOPOLOGY_TEMPLATE))
346 def _has_substitution_mappings(self):
347 """Return True if the template has valid substitution mappings."""
348 return self.topology_template is not None and \
349 self.topology_template.substitution_mappings is not None
351 def has_nested_templates(self):
352 """Return True if the tosca template has nested templates."""
353 return self.nested_tosca_templates_with_topology is not None and \
354 len(self.nested_tosca_templates_with_topology) >= 1