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
73 self.input_path = path
74 self.path = self._get_path(path)
76 self.tpl = YAML_LOADER(self.path, self.a_file)
78 msg = (_('Both path and yaml_dict_tpl arguments were '
79 'provided. Using path and ignoring yaml_dict_tpl.'))
84 self.tpl = yaml_dict_tpl
86 ExceptionCollector.appendException(
87 ValueError(_('No path or yaml_dict_tpl was provided. '
88 'There is nothing to parse.')))
91 self.parsed_params = parsed_params
92 self._validate_field()
93 self.version = self._tpl_version()
94 self.relationship_types = self._tpl_relationship_types()
95 self.description = self._tpl_description()
96 self.topology_template = self._topology_template()
97 if self.topology_template.tpl:
98 self.inputs = self._inputs()
99 self.relationship_templates = self._relationship_templates()
100 self.nodetemplates = self._nodetemplates()
101 self.outputs = self._outputs()
102 self.graph = ToscaGraph(self.nodetemplates)
104 ExceptionCollector.stop()
105 self.verify_template()
107 def _topology_template(self):
108 return TopologyTemplate(self._tpl_topology_template(),
109 self._get_all_custom_defs(),
110 self.relationship_types,
114 return self.topology_template.inputs
116 def _nodetemplates(self):
117 return self.topology_template.nodetemplates
119 def _relationship_templates(self):
120 return self.topology_template.relationship_templates
123 return self.topology_template.outputs
125 def _tpl_version(self):
126 return self.tpl.get(DEFINITION_VERSION)
128 def _tpl_description(self):
129 desc = self.tpl.get(DESCRIPTION)
133 def _tpl_imports(self):
134 return self.tpl.get(IMPORTS)
136 def _tpl_relationship_types(self):
137 return self._get_custom_types(RELATIONSHIP_TYPES)
139 def _tpl_relationship_templates(self):
140 topology_template = self._tpl_topology_template()
141 return topology_template.get(RELATIONSHIP_TEMPLATES)
143 def _tpl_topology_template(self):
144 return self.tpl.get(TOPOLOGY_TEMPLATE)
146 def _get_all_custom_defs(self, imports=None):
147 types = [IMPORTS, NODE_TYPES, CAPABILITY_TYPES, RELATIONSHIP_TYPES,
148 DATA_TYPES, POLICY_TYPES, GROUP_TYPES]
149 custom_defs_final = {}
150 custom_defs = self._get_custom_types(types, imports)
152 custom_defs_final.update(custom_defs)
153 if custom_defs.get(IMPORTS):
154 import_defs = self._get_all_custom_defs(
155 custom_defs.get(IMPORTS))
156 custom_defs_final.update(import_defs)
158 # As imports are not custom_types, removing from the dict
159 custom_defs_final.pop(IMPORTS, None)
160 return custom_defs_final
162 def _get_custom_types(self, type_definitions, imports=None):
163 """Handle custom types defined in imported template files
165 This method loads the custom type definitions referenced in "imports"
166 section of the TOSCA YAML template.
171 if not isinstance(type_definitions, list):
172 type_defs.append(type_definitions)
174 type_defs = type_definitions
177 imports = self._tpl_imports()
180 custom_defs = toscaparser.imports.\
181 ImportsLoader(imports, self.path,
182 type_defs, self.tpl).get_custom_defs()
186 # Handle custom types defined in current template file
187 for type_def in type_defs:
188 if type_def != IMPORTS:
189 inner_custom_types = self.tpl.get(type_def) or {}
190 if inner_custom_types:
191 custom_defs.update(inner_custom_types)
194 def _validate_field(self):
195 version = self._tpl_version()
197 ExceptionCollector.appendException(
198 MissingRequiredFieldError(what='Template',
199 required=DEFINITION_VERSION))
201 self._validate_version(version)
202 self.version = version
204 for name in self.tpl:
205 if (name not in SECTIONS and
206 name not in self.ADDITIONAL_SECTIONS.get(version, ())):
207 ExceptionCollector.appendException(
208 UnknownFieldError(what='Template', field=name))
210 def _validate_version(self, version):
211 if version not in self.VALID_TEMPLATE_VERSIONS:
212 ExceptionCollector.appendException(
213 InvalidTemplateVersion(
215 valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
217 if version != 'tosca_simple_yaml_1_0':
218 update_definitions(version)
220 def _get_path(self, path):
221 if path.lower().endswith('.yaml'):
223 elif path.lower().endswith(('.zip', '.csar')):
225 csar = CSAR(path, self.a_file)
228 self.a_file = True # the file has been decompressed locally
229 return os.path.join(csar.temp_dir, csar.get_main_template())
231 ExceptionCollector.appendException(
232 ValueError(_('"%(path)s" is not a valid file.')
235 def verify_template(self):
236 if ExceptionCollector.exceptionsCaught():
238 raise ValidationError(
239 message=(_('\nThe input "%(path)s" failed validation with '
240 'the following error(s): \n\n\t')
241 % {'path': self.input_path}) +
242 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
244 raise ValidationError(
245 message=_('\nThe pre-parsed input failed validation with '
246 'the following error(s): \n\n\t') +
247 '\n\t'.join(ExceptionCollector.getExceptionsReport()))
250 msg = (_('The input "%(path)s" successfully passed '
251 'validation.') % {'path': self.input_path})
253 msg = _('The pre-parsed input successfully passed validation.')