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 for "%s" can not be full-filled.') % self.name
83 if (node in list(self.type_definition.TOSCA_DEF.keys())
84 or node in self.custom_def):
85 ExceptionCollector.appendException(NotImplementedError(msg))
88 if node not in self.templates:
89 ExceptionCollector.appendException(
90 KeyError(_('Node template "%s" was not found.') % node))
93 related_tpl = NodeTemplate(node, self.templates, self.custom_def)
94 relationship = value.get('relationship') \
95 if isinstance(value, dict) else None
96 # check if it's type has relationship defined
98 parent_reqs = self.type_definition.get_all_requirements()
99 if parent_reqs is None:
100 ExceptionCollector.appendException(
101 ValidationError(message='parent_req is ' +
104 for key in req.keys():
105 for req_dict in parent_reqs:
106 if key in req_dict.keys():
107 relationship = (req_dict.get(key).
111 found_relationship_tpl = False
112 # apply available relationship templates if found
113 if self.available_rel_tpls:
114 for tpl in self.available_rel_tpls:
115 if tpl.name == relationship:
116 rtype = RelationshipType(tpl.type, None,
118 explicit_relation[rtype] = related_tpl
119 tpl.target = related_tpl
121 self.relationship_tpl.append(tpl)
122 found_relationship_tpl = True
123 # create relationship template object.
124 rel_prfx = self.type_definition.RELATIONSHIP_PREFIX
125 if not found_relationship_tpl:
126 if isinstance(relationship, dict):
127 relationship = relationship.get('type')
129 if self.available_rel_types and \
130 relationship in self.available_rel_types.keys():
132 elif not relationship.startswith(rel_prfx):
133 relationship = rel_prfx + relationship
135 ExceptionCollector.appendException(
136 MissingRequiredFieldError(
137 what=_('"relationship" used in template '
138 '"%s"') % related_tpl.name,
140 for rtype in self.type_definition.relationship.keys():
141 if rtype.type == relationship:
142 explicit_relation[rtype] = related_tpl
143 related_tpl._add_relationship_template(req,
146 elif self.available_rel_types:
147 if relationship in self.available_rel_types.keys():
148 rel_type_def = self.available_rel_types.\
150 if 'derived_from' in rel_type_def:
152 rel_type_def.get('derived_from')
153 if not super_type.startswith(rel_prfx):
154 super_type = rel_prfx + super_type
155 if rtype.type == super_type:
156 explicit_relation[rtype] = related_tpl
158 _add_relationship_template(
159 req, rtype.type, self)
160 return explicit_relation
162 def _add_relationship_template(self, requirement, rtype, source):
163 req = requirement.copy()
165 tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
166 self.relationship_tpl.append(tpl)
168 def get_relationship_template(self):
169 return self.relationship_tpl
171 def _add_next(self, nodetpl, relationship):
172 self.related[nodetpl] = relationship
175 def related_nodes(self):
177 for relation, node in self.type_definition.relationship.items():
178 for tpl in self.templates:
180 self.related[NodeTemplate(tpl)] = relation
181 return self.related.keys()
183 def validate(self, tosca_tpl=None):
184 self._validate_capabilities()
185 self._validate_requirements()
186 self._validate_properties(self.entity_tpl, self.type_definition)
187 self._validate_interfaces()
188 for prop in self.get_properties_objects():
191 def _validate_requirements(self):
192 type_requires = self.type_definition.get_all_requirements()
193 allowed_reqs = ["template"]
195 for treq in type_requires:
196 for key, value in treq.items():
197 allowed_reqs.append(key)
198 if isinstance(value, dict):
200 allowed_reqs.append(key)
202 requires = self.type_definition.get_value(self.REQUIREMENTS,
205 if not isinstance(requires, list):
206 ExceptionCollector.appendException(
208 what='"requirements" of template "%s"' % self.name,
212 for r1, value in req.items():
213 if isinstance(value, dict):
214 self._validate_requirements_keys(value)
215 self._validate_requirements_properties(value)
216 allowed_reqs.append(r1)
217 self._common_validate_field(req, allowed_reqs,
220 def _validate_requirements_properties(self, requirements):
221 # TODO(anyone): Only occurrences property of the requirements is
222 # validated here. Validation of other requirement properties are being
223 # validated in different files. Better to keep all the requirements
224 # properties validation here.
225 for key, value in requirements.items():
226 if key == 'occurrences':
227 self._validate_occurrences(value)
230 def _validate_occurrences(self, occurrences):
231 DataEntity.validate_datatype('list', occurrences)
232 for value in occurrences:
233 DataEntity.validate_datatype('integer', value)
234 if len(occurrences) != 2 or not (0 <= occurrences[0] <= occurrences[1]) \
235 or occurrences[1] == 0:
236 ExceptionCollector.appendException(
237 InvalidPropertyValueError(what=(occurrences)))
239 def _validate_requirements_keys(self, requirement):
240 for key in requirement.keys():
241 if key not in self.REQUIREMENTS_SECTION:
242 ExceptionCollector.appendException(
244 what='"requirements" of template "%s"' % self.name,
247 def _validate_interfaces(self):
248 ifaces = self.type_definition.get_value(self.INTERFACES,
251 for name, value in ifaces.items():
252 if name in (LIFECYCLE, LIFECYCLE_SHORTNAME):
253 self._common_validate_field(
254 value, InterfacesDef.
255 interfaces_node_lifecycle_operations,
257 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
258 self._common_validate_field(
259 value, InterfacesDef.
260 interfaces_relationship_configure_operations,
262 elif name in self.type_definition.interfaces.keys():
263 self._common_validate_field(
265 self._collect_custom_iface_operations(name),
268 ExceptionCollector.appendException(
270 what='"interfaces" of template "%s"' %
271 self.name, field=name))
273 def _collect_custom_iface_operations(self, name):
274 allowed_operations = []
275 nodetype_iface_def = self.type_definition.interfaces[name]
276 allowed_operations.extend(nodetype_iface_def.keys())
277 if 'type' in nodetype_iface_def:
278 iface_type = nodetype_iface_def['type']
279 if iface_type in self.type_definition.custom_def:
280 iface_type_def = self.type_definition.custom_def[iface_type]
282 iface_type_def = self.type_definition.TOSCA_DEF[iface_type]
283 allowed_operations.extend(iface_type_def.keys())
284 allowed_operations = [op for op in allowed_operations if
285 op not in INTERFACE_DEF_RESERVED_WORDS]
286 return allowed_operations
288 def _validate_fields(self, nodetemplate):
289 for name in nodetemplate.keys():
290 if name not in self.SECTIONS and name not in self.SPECIAL_SECTIONS:
291 ExceptionCollector.appendException(
292 UnknownFieldError(what='Node template "%s"' % self.name,