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.topology_template import TopologyTemplate
27 from toscaparser.tpl_relationship_graph import ToscaGraph
28 from toscaparser.utils.gettextutils import _
29 import toscaparser.utils.yamlparser
32 # TOSCA template key names
33 SECTIONS = (DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME,
34 TOPOLOGY_TEMPLATE, TEMPLATE_AUTHOR, TEMPLATE_VERSION,
35 DESCRIPTION, IMPORTS, DSL_DEFINITIONS, NODE_TYPES,
36 RELATIONSHIP_TYPES, RELATIONSHIP_TEMPLATES,
37 CAPABILITY_TYPES, ARTIFACT_TYPES, DATA_TYPES,
38 POLICY_TYPES, GROUP_TYPES, REPOSITORIES) = \
39 ('tosca_definitions_version', 'tosca_default_namespace',
40 'template_name', 'topology_template', 'template_author',
41 'template_version', 'description', 'imports', 'dsl_definitions',
42 'node_types', 'relationship_types', 'relationship_templates',
43 'capability_types', 'artifact_types', 'data_types',
44 'policy_types', 'group_types', 'repositories')
45 # Sections that are specific to individual template definitions
46 SPECIAL_SECTIONS = (METADATA) = ('metadata')
48 log = logging.getLogger("tosca.model")
50 YAML_LOADER = toscaparser.utils.yamlparser.load_yaml
53 class ToscaTemplate(object):
56 VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0']
58 VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
60 ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS}
62 ADDITIONAL_SECTIONS.update(exttools.get_sections())
64 '''Load the template data.'''
65 def __init__(self, path=None, parsed_params=None, a_file=True,
67 ExceptionCollector.start()
69 self.input_path = None
72 self.nested_tosca_template = []
74 self.input_path = path
75 self.path = self._get_path(path)
77 self.tpl = YAML_LOADER(self.path, self.a_file)
79 msg = (_('Both path and yaml_dict_tpl arguments were '
80 'provided. Using path and ignoring yaml_dict_tpl.'))
85 self.tpl = yaml_dict_tpl
87 ExceptionCollector.appendException(
88 ValueError(_('No path or yaml_dict_tpl was provided. '
89 'There is nothing to parse.')))
92 self.parsed_params = parsed_params
93 self._validate_field()
94 self.version = self._tpl_version()
95 self.relationship_types = self._tpl_relationship_types()
96 self.description = self._tpl_description()
97 self.topology_template = self._topology_template()
98 if self.topology_template.tpl:
99 self.inputs = self._inputs()
100 self.relationship_templates = self._relationship_templates()
101 self.nodetemplates = self._nodetemplates()
102 self.outputs = self._outputs()
103 self.graph = ToscaGraph(self.nodetemplates)
105 ExceptionCollector.stop()
106 self.verify_template()
108 def _topology_template(self):
109 return TopologyTemplate(self._tpl_topology_template(),
110 self._get_all_custom_defs(),
111 self.relationship_types,
115 return self.topology_template.inputs
117 def _nodetemplates(self):
118 return self.topology_template.nodetemplates
120 def _relationship_templates(self):
121 return self.topology_template.relationship_templates
124 return self.topology_template.outputs
126 def _tpl_version(self):
127 return self.tpl.get(DEFINITION_VERSION)
129 def _tpl_description(self):
130 desc = self.tpl.get(DESCRIPTION)
134 def _tpl_imports(self):
135 return self.tpl.get(IMPORTS)
137 def _tpl_relationship_types(self):
138 return self._get_custom_types(RELATIONSHIP_TYPES)
140 def _tpl_relationship_templates(self):
141 topology_template = self._tpl_topology_template()
142 return topology_template.get(RELATIONSHIP_TEMPLATES)
144 def _tpl_topology_template(self):
145 return self.tpl.get(TOPOLOGY_TEMPLATE)
147 def _get_all_custom_defs(self, imports=None):
148 types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
149 DATA_TYPES, POLICY_TYPES, GROUP_TYPES]
150 custom_defs_final = {}
151 custom_defs = self._get_custom_types(types, imports)
153 custom_defs_final.update(custom_defs)
154 if custom_defs.get(IMPORTS):
155 import_defs = self._get_all_custom_defs(
156 custom_defs.get(IMPORTS))
157 custom_defs_final.update(import_defs)
159 # As imports are not custom_types, removing from the dict
160 custom_defs_final.pop(IMPORTS, None)
161 return custom_defs_final
163 def _get_custom_types(self, type_definitions, imports=None):
164 """Handle custom types defined in imported template files
166 This method loads the custom type definitions referenced in "imports"
167 section of the TOSCA YAML template.
172 if not isinstance(type_definitions, list):
173 type_defs.append(type_definitions)
175 type_defs = type_definitions
178 imports = self._tpl_imports()
181 custom_service = toscaparser.imports.\
182 ImportsLoader(imports, self.path,
185 nested_topo_tpls = custom_service.get_nested_topo_tpls()
186 self._handle_nested_topo_tpls(nested_topo_tpls)
188 custom_defs = custom_service.get_custom_defs()
192 # Handle custom types defined in current template file
193 for type_def in type_defs:
194 if type_def != IMPORTS:
195 inner_custom_types = self.tpl.get(type_def) or {}
196 if inner_custom_types:
197 custom_defs.update(inner_custom_types)
200 def _handle_nested_topo_tpls(self, nested_topo_tpls):
201 for tpl in nested_topo_tpls:
202 filename, tosca_tpl = tpl.items()[0]
203 if tosca_tpl.get(TOPOLOGY_TEMPLATE):
204 nested_template = ToscaTemplate(
205 path=filename, parsed_params=self.parsed_params,
206 yaml_dict_tpl=tosca_tpl)
207 if nested_template.topology_template.substitution_mappings:
208 self.nested_tosca_template.apend(nested_template)
210 def _validate_field(self):
211 version = self._tpl_version()
213 ExceptionCollector.appendException(
214 MissingRequiredFieldError(what='Template',
215 required=DEFINITION_VERSION))
217 self._validate_version(version)
218 self.version = version
220 for name in self.tpl:
221 if (name not in SECTIONS and
222 name not in self.ADDITIONAL_SECTIONS.get(version, ())):
223 ExceptionCollector.appendException(
224 UnknownFieldError(what='Template', field=name))
226 def _validate_version(self, version):
227 if version not in self.VALID_TEMPLATE_VERSIONS:
228 ExceptionCollector.appendException(
229 InvalidTemplateVersion(
231 valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
233 if version != 'tosca_simple_yaml_1_0':
234 update_definitions(version)
236 def _get_path(self, path):
237 if path.lower().endswith('.yaml'):
239 elif path.lower().endswith(('.zip', '.csar')):
241 csar = CSAR(path, self.a_file)
244 self.a_file = True # the file has been decompressed locally
245 return os.path.join(csar.temp_dir, csar.get_main_template())
247 ExceptionCollector.appendException(
248 ValueError(_('"%(path)s" is not a valid file.')
251 def verify_template(self):
252 if ExceptionCollector.exceptionsCaught():
254 raise ValidationError(
255 message=(_('\nThe input "%(path)s" failed validation with '
256 'the following error(s): \n\n\t')
257 % {'path': self.input_path}) +
258 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
260 raise ValidationError(
261 message=_('\nThe pre-parsed input failed validation with '
262 'the following error(s): \n\n\t') +
263 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
266 msg = (_('The input "%(path)s" successfully passed '
267 'validation.') % {'path': self.input_path})
269 msg = _('The pre-parsed input successfully passed validation.')