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
16 from toscaparser.common.exception import ExceptionCollector
17 from toscaparser.common.exception import InvalidPropertyValueError
18 from toscaparser.common.exception import MissingRequiredFieldError
19 from toscaparser.common.exception import TypeMismatchError
20 from toscaparser.common.exception import UnknownFieldError
21 from toscaparser.common.exception import ValidationError
22 from toscaparser.dataentity import DataEntity
23 from toscaparser.elements.interfaces import CONFIGURE
24 from toscaparser.elements.interfaces import CONFIGURE_SHORTNAME
25 from toscaparser.elements.interfaces import INTERFACE_DEF_RESERVED_WORDS
26 from toscaparser.elements.interfaces import InterfacesDef
27 from toscaparser.elements.interfaces import LIFECYCLE
28 from toscaparser.elements.interfaces import LIFECYCLE_SHORTNAME
29 from toscaparser.elements.relationshiptype import RelationshipType
30 from toscaparser.entity_template import EntityTemplate
31 from toscaparser.relationship_template import RelationshipTemplate
32 from toscaparser.utils.gettextutils import _
34 log = logging.getLogger('tosca')
37 class NodeTemplate(EntityTemplate):
38 '''Node template from a Tosca profile.'''
39 def __init__(self, name, node_templates, custom_def=None,
40 available_rel_tpls=None, available_rel_types=None):
41 super(NodeTemplate, self).__init__(name, node_templates[name],
44 self.templates = node_templates
45 self._validate_fields(node_templates[name])
46 self.custom_def = custom_def
48 self.relationship_tpl = []
49 self.available_rel_tpls = available_rel_tpls
50 self.available_rel_types = available_rel_types
51 self._relationships = {}
52 self.substitution_mapped = None
55 def relationships(self):
56 if not self._relationships:
57 requires = self.requirements
58 if requires and isinstance(requires, list):
60 for r1, value in r.items():
61 explicit = self._get_explicit_relationship(r, value)
63 for key, value in explicit.items():
64 self._relationships[key] = value
65 return self._relationships
67 def _get_explicit_relationship(self, req, value):
68 """Handle explicit relationship
73 relationship: tosca.relationships.HostedOn
75 explicit_relation = {}
76 node = value.get('node') if isinstance(value, dict) else value
79 # TODO(spzala) implement look up once Glance meta data is available
80 # to find a matching TOSCA node using the TOSCA types
81 msg = _('Lookup by TOSCA types is not supported. '
82 'Requirement node "%(node)s" for "%(name)s"'
83 ' can not be full-filled.') \
84 % {'node': node, 'name': self.name}
85 if (node in list(self.type_definition.TOSCA_DEF.keys())
86 or node in self.custom_def):
87 ExceptionCollector.appendException(NotImplementedError(msg))
90 if node not in self.templates:
91 ExceptionCollector.appendException(
92 KeyError(_('Node template "%(node)s" was not found'
94 % {'node': node, 'name': self.name}))
97 related_tpl = NodeTemplate(node, self.templates, self.custom_def)
98 relationship = value.get('relationship') \
99 if isinstance(value, dict) else None
100 # check if it's type has relationship defined
102 parent_reqs = self.type_definition.get_all_requirements()
103 if parent_reqs is None:
104 ExceptionCollector.appendException(
105 ValidationError(message='parent_req is ' +
108 for key in req.keys():
109 for req_dict in parent_reqs:
110 if key in req_dict.keys():
111 relationship = (req_dict.get(key).
115 found_relationship_tpl = False
117 # apply available relationship templates if found
118 if self.available_rel_tpls:
119 for tpl in self.available_rel_tpls:
120 if tpl.name == relationship:
121 rtype = RelationshipType(tpl.type, None,
123 explicit_relation[rtype] = related_tpl
124 tpl.target = related_tpl
126 self.relationship_tpl.append(tpl)
127 found_relationship_tpl = True
128 # create relationship template object.
129 rel_prfx = self.type_definition.RELATIONSHIP_PREFIX
130 if not found_relationship_tpl:
131 if isinstance(relationship, dict):
132 relationship = relationship.get('type')
134 if self.available_rel_types and \
135 relationship in self.available_rel_types.keys():
137 elif not relationship.startswith(rel_prfx):
138 relationship = rel_prfx + relationship
140 ExceptionCollector.appendException(
141 MissingRequiredFieldError(
142 what=_('"relationship" used in template '
143 '"%s"') % related_tpl.name,
145 for rtype in self.type_definition.relationship.keys():
146 if rtype.type == relationship:
147 explicit_relation[rtype] = related_tpl
148 related_tpl._add_relationship_template(req,
151 elif self.available_rel_types:
152 if relationship in self.available_rel_types.keys():
153 rel_type_def = self.available_rel_types.\
155 if 'derived_from' in rel_type_def:
157 rel_type_def.get('derived_from')
158 if not super_type.startswith(rel_prfx):
159 super_type = rel_prfx + super_type
160 if rtype.type == super_type:
161 explicit_relation[rtype] = related_tpl
163 _add_relationship_template(
164 req, rtype.type, self)
165 return explicit_relation
167 def _add_relationship_template(self, requirement, rtype, source):
168 req = requirement.copy()
170 tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
171 self.relationship_tpl.append(tpl)
173 def get_relationship_template(self):
174 return self.relationship_tpl
176 def _add_next(self, nodetpl, relationship):
177 self.related[nodetpl] = relationship
180 def related_nodes(self):
182 for relation, node in self.type_definition.relationship.items():
183 for tpl in self.templates:
185 self.related[NodeTemplate(tpl)] = relation
186 return self.related.keys()
188 def validate(self, tosca_tpl=None):
189 self._validate_capabilities()
190 self._validate_requirements()
191 self._validate_properties(self.entity_tpl, self.type_definition)
192 self._validate_interfaces()
193 for prop in self.get_properties_objects():
196 def _validate_requirements(self):
197 type_requires = self.type_definition.get_all_requirements()
198 allowed_reqs = ["template"]
200 for treq in type_requires:
201 for key, value in treq.items():
202 allowed_reqs.append(key)
203 if isinstance(value, dict):
205 allowed_reqs.append(key)
207 requires = self.type_definition.get_value(self.REQUIREMENTS,
210 if not isinstance(requires, list):
211 ExceptionCollector.appendException(
213 what='"requirements" of template "%s"' % self.name,
217 for r1, value in req.items():
218 if isinstance(value, dict):
219 self._validate_requirements_keys(value)
220 self._validate_requirements_properties(value)
221 allowed_reqs.append(r1)
222 self._common_validate_field(req, allowed_reqs,
225 def _validate_requirements_properties(self, requirements):
226 # TODO(anyone): Only occurrences property of the requirements is
227 # validated here. Validation of other requirement properties are being
228 # validated in different files. Better to keep all the requirements
229 # properties validation here.
230 for key, value in requirements.items():
231 if key == 'occurrences':
232 self._validate_occurrences(value)
235 def _validate_occurrences(self, occurrences):
236 DataEntity.validate_datatype('list', occurrences)
237 for value in occurrences:
238 DataEntity.validate_datatype('integer', value)
239 if len(occurrences) != 2 or not (0 <= occurrences[0] <= occurrences[1]) \
240 or occurrences[1] == 0:
241 ExceptionCollector.appendException(
242 InvalidPropertyValueError(what=(occurrences)))
244 def _validate_requirements_keys(self, requirement):
245 for key in requirement.keys():
246 if key not in self.REQUIREMENTS_SECTION:
247 ExceptionCollector.appendException(
249 what='"requirements" of template "%s"' % self.name,
252 def _validate_interfaces(self):
253 ifaces = self.type_definition.get_value(self.INTERFACES,
256 for name, value in ifaces.items():
257 if name in (LIFECYCLE, LIFECYCLE_SHORTNAME):
258 self._common_validate_field(
259 value, InterfacesDef.
260 interfaces_node_lifecycle_operations,
262 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
263 self._common_validate_field(
264 value, InterfacesDef.
265 interfaces_relationship_configure_operations,
267 elif name in self.type_definition.interfaces.keys():
268 self._common_validate_field(
270 self._collect_custom_iface_operations(name),
273 ExceptionCollector.appendException(
275 what='"interfaces" of template "%s"' %
276 self.name, field=name))
278 def _collect_custom_iface_operations(self, name):
279 allowed_operations = []
280 nodetype_iface_def = self.type_definition.interfaces[name]
281 allowed_operations.extend(nodetype_iface_def.keys())
282 if 'type' in nodetype_iface_def:
283 iface_type = nodetype_iface_def['type']
284 if iface_type in self.type_definition.custom_def:
285 iface_type_def = self.type_definition.custom_def[iface_type]
287 iface_type_def = self.type_definition.TOSCA_DEF[iface_type]
288 allowed_operations.extend(iface_type_def.keys())
289 allowed_operations = [op for op in allowed_operations if
290 op not in INTERFACE_DEF_RESERVED_WORDS]
291 return allowed_operations
293 def _validate_fields(self, nodetemplate):
294 for name in nodetemplate.keys():
295 if name not in self.SECTIONS and name not in self.SPECIAL_SECTIONS:
296 ExceptionCollector.appendException(
297 UnknownFieldError(what='Node template "%s"' % self.name,