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 = None
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 if tpl.get(TOPOLOGY_TEMPLATE):
203 nested_tosca_template = ToscaTemplate(
204 path=self.path, parsed_params=self.parsed_params,
205 yaml_dict_tpl=nested_topo_tpls)
206 self.nested_tosca_template.apend(nested_tosca_template)
208 def _validate_field(self):
209 version = self._tpl_version()
211 ExceptionCollector.appendException(
212 MissingRequiredFieldError(what='Template',
213 required=DEFINITION_VERSION))
215 self._validate_version(version)
216 self.version = version
218 for name in self.tpl:
219 if (name not in SECTIONS and
220 name not in self.ADDITIONAL_SECTIONS.get(version, ())):
221 ExceptionCollector.appendException(
222 UnknownFieldError(what='Template', field=name))
224 def _validate_version(self, version):
225 if version not in self.VALID_TEMPLATE_VERSIONS:
226 ExceptionCollector.appendException(
227 InvalidTemplateVersion(
229 valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
231 if version != 'tosca_simple_yaml_1_0':
232 update_definitions(version)
234 def _get_path(self, path):
235 if path.lower().endswith('.yaml'):
237 elif path.lower().endswith(('.zip', '.csar')):
239 csar = CSAR(path, self.a_file)
242 self.a_file = True # the file has been decompressed locally
243 return os.path.join(csar.temp_dir, csar.get_main_template())
245 ExceptionCollector.appendException(
246 ValueError(_('"%(path)s" is not a valid file.')
249 def verify_template(self):
250 if ExceptionCollector.exceptionsCaught():
252 raise ValidationError(
253 message=(_('\nThe input "%(path)s" failed validation with '
254 'the following error(s): \n\n\t')
255 % {'path': self.input_path}) +
256 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
258 raise ValidationError(
259 message=_('\nThe pre-parsed input failed validation with '
260 'the following error(s): \n\n\t') +
261 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
264 msg = (_('The input "%(path)s" successfully passed '
265 'validation.') % {'path': self.input_path})
267 msg = _('The pre-parsed input successfully passed validation.')