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 _topology_template(self):
125 return TopologyTemplate(self._tpl_topology_template(),
126 self._get_all_custom_defs(),
127 self.relationship_types,
132 return self.topology_template.inputs
134 def _nodetemplates(self):
135 return self.topology_template.nodetemplates
137 def _relationship_templates(self):
138 return self.topology_template.relationship_templates
141 return self.topology_template.outputs
143 def _tpl_version(self):
144 return self.tpl.get(DEFINITION_VERSION)
146 def _tpl_description(self):
147 desc = self.tpl.get(DESCRIPTION)
151 def _tpl_imports(self):
152 return self.tpl.get(IMPORTS)
154 def _tpl_repositories(self):
155 repositories = self.tpl.get(REPOSITORIES)
158 for name, val in repositories.items():
159 reposits = Repository(name, val)
160 reposit.append(reposits)
163 def _tpl_relationship_types(self):
164 return self._get_custom_types(RELATIONSHIP_TYPES)
166 def _tpl_relationship_templates(self):
167 topology_template = self._tpl_topology_template()
168 return topology_template.get(RELATIONSHIP_TEMPLATES)
170 def _tpl_topology_template(self):
171 return self.tpl.get(TOPOLOGY_TEMPLATE)
173 def _get_all_custom_defs(self, imports=None):
174 types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
175 DATA_TYPES, INTERFACE_TYPES, POLICY_TYPES, GROUP_TYPES]
176 custom_defs_final = {}
177 custom_defs = self._get_custom_types(types, imports)
179 custom_defs_final.update(custom_defs)
180 if custom_defs.get(IMPORTS):
181 import_defs = self._get_all_custom_defs(
182 custom_defs.get(IMPORTS))
183 custom_defs_final.update(import_defs)
185 # As imports are not custom_types, removing from the dict
186 custom_defs_final.pop(IMPORTS, None)
187 return custom_defs_final
189 def _get_custom_types(self, type_definitions, imports=None):
190 """Handle custom types defined in imported template files
192 This method loads the custom type definitions referenced in "imports"
193 section of the TOSCA YAML template.
198 if not isinstance(type_definitions, list):
199 type_defs.append(type_definitions)
201 type_defs = type_definitions
204 imports = self._tpl_imports()
207 custom_service = toscaparser.imports.\
208 ImportsLoader(imports, self.path,
211 nested_tosca_tpls = custom_service.get_nested_tosca_tpls()
212 self._update_nested_tosca_tpls_with_topology(nested_tosca_tpls)
214 custom_defs = custom_service.get_custom_defs()
218 # Handle custom types defined in current template file
219 for type_def in type_defs:
220 if type_def != IMPORTS:
221 inner_custom_types = self.tpl.get(type_def) or {}
222 if inner_custom_types:
223 custom_defs.update(inner_custom_types)
226 def _update_nested_tosca_tpls_with_topology(self, nested_tosca_tpls):
227 for tpl in nested_tosca_tpls:
228 filename, tosca_tpl = list(tpl.items())[0]
229 if (tosca_tpl.get(TOPOLOGY_TEMPLATE) and
230 filename not in list(
231 self.nested_tosca_tpls_with_topology.keys())):
232 self.nested_tosca_tpls_with_topology.update(tpl)
234 def _handle_nested_tosca_templates_with_topology(self):
235 for fname, tosca_tpl in self.nested_tosca_tpls_with_topology.items():
236 for nodetemplate in self.nodetemplates:
237 if self._is_sub_mapped_node(nodetemplate, tosca_tpl):
238 parsed_params = self._get_params_for_nested_template(
240 topology_tpl = tosca_tpl.get(TOPOLOGY_TEMPLATE)
241 topology_with_sub_mapping = TopologyTemplate(
243 self._get_all_custom_defs(),
244 self.relationship_types,
247 if topology_with_sub_mapping.substitution_mappings:
248 # Record nested topo templates in top level template
249 self.nested_tosca_templates_with_topology.\
250 append(topology_with_sub_mapping)
251 # Set substitution mapping object for mapped node
252 nodetemplate.sub_mapping_tosca_template = \
253 topology_with_sub_mapping.substitution_mappings
255 def _validate_field(self):
256 version = self._tpl_version()
258 ExceptionCollector.appendException(
259 MissingRequiredFieldError(what='Template',
260 required=DEFINITION_VERSION))
262 self._validate_version(version)
263 self.version = version
265 for name in self.tpl:
266 if (name not in SECTIONS and
267 name not in self.ADDITIONAL_SECTIONS.get(version, ())):
268 ExceptionCollector.appendException(
269 UnknownFieldError(what='Template', field=name))
271 def _validate_version(self, version):
272 if version not in self.VALID_TEMPLATE_VERSIONS:
273 ExceptionCollector.appendException(
274 InvalidTemplateVersion(
276 valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
278 if version != 'tosca_simple_yaml_1_0':
279 update_definitions(version)
281 def _get_path(self, path):
282 if path.lower().endswith('.yaml'):
284 elif path.lower().endswith(('.zip', '.csar')):
286 csar = CSAR(path, self.a_file)
289 self.a_file = True # the file has been decompressed locally
290 return os.path.join(csar.temp_dir, csar.get_main_template())
292 ExceptionCollector.appendException(
293 ValueError(_('"%(path)s" is not a valid file.')
296 def verify_template(self):
297 if ExceptionCollector.exceptionsCaught():
299 raise ValidationError(
300 message=(_('\nThe input "%(path)s" failed validation with '
301 'the following error(s): \n\n\t')
302 % {'path': self.input_path}) +
303 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
305 raise ValidationError(
306 message=_('\nThe pre-parsed input failed validation with '
307 'the following error(s): \n\n\t') +
308 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
311 msg = (_('The input "%(path)s" successfully passed '
312 'validation.') % {'path': self.input_path})
314 msg = _('The pre-parsed input successfully passed validation.')
318 def _is_sub_mapped_node(self, nodetemplate, tosca_tpl):
319 """Return True if the nodetemple is substituted."""
320 if (nodetemplate and not nodetemplate.sub_mapping_tosca_template and
321 self.get_sub_mapping_node_type(tosca_tpl) == nodetemplate.type
322 and len(nodetemplate.interfaces) < 1):
327 def _get_params_for_nested_template(self, nodetemplate):
328 """Return total params for nested_template."""
329 parsed_params = deepcopy(self.parsed_params) \
330 if self.parsed_params else {}
332 for pname in nodetemplate.get_properties():
333 parsed_params.update({pname:
334 nodetemplate.get_property_value(pname)})
337 def get_sub_mapping_node_type(self, tosca_tpl):
338 """Return substitution mappings node type."""
340 return TopologyTemplate.get_sub_mapping_node_type(
341 tosca_tpl.get(TOPOLOGY_TEMPLATE))
343 def _has_substitution_mappings(self):
344 """Return True if the template has valid substitution mappings."""
345 return self.topology_template is not None and \
346 self.topology_template.substitution_mappings is not None
348 def has_nested_templates(self):
349 """Return True if the tosca template has nested templates."""
350 return self.nested_tosca_templates_with_topology is not None and \
351 len(self.nested_tosca_templates_with_topology) >= 1