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 _
33 from toscaparser.utils import validateutils
34 from org.openecomp.sdc.toscaparser.jython import JyNodeTemplate
36 log = logging.getLogger('tosca')
39 class NodeTemplate(EntityTemplate, JyNodeTemplate):
40 '''Node template from a Tosca profile.'''
41 def __init__(self, name, node_templates, custom_def=None,
42 available_rel_tpls=None, available_rel_types=None):
43 nodeTemplate = node_templates[name]
44 super(NodeTemplate, self).__init__(name, nodeTemplate,
47 self.templates = node_templates
48 self._validate_fields(nodeTemplate)
49 self.custom_def = custom_def
51 self.relationship_tpl = []
52 self.available_rel_tpls = available_rel_tpls
53 self.available_rel_types = available_rel_types
54 self._relationships = {}
55 self.sub_mapping_tosca_template = None
57 if self.METADATA in nodeTemplate:
58 self.meta_data = nodeTemplate.get(self.METADATA)
59 validateutils.validate_map(self.meta_data)
61 def getJyMetadata(self):
65 def relationships(self):
66 if not self._relationships:
67 requires = self.requirements
68 if requires and isinstance(requires, list):
70 for r1, value in r.items():
71 explicit = self._get_explicit_relationship(r, value)
73 for key, value in explicit.items():
74 self._relationships[key] = value
75 return self._relationships
77 def _get_explicit_relationship(self, req, value):
78 """Handle explicit relationship
83 relationship: tosca.relationships.HostedOn
85 explicit_relation = {}
86 node = value.get('node') if isinstance(value, dict) else value
89 # TODO(spzala) implement look up once Glance meta data is available
90 # to find a matching TOSCA node using the TOSCA types
91 msg = _('Lookup by TOSCA types is not supported. '
92 'Requirement for "%s" can not be full-filled.') % self.name
93 if (node in list(self.type_definition.TOSCA_DEF.keys())
94 or node in self.custom_def):
95 ExceptionCollector.appendException(NotImplementedError(msg))
98 if node not in self.templates:
99 ExceptionCollector.appendException(
100 KeyError(_('Node template "%s" was not found.') % node))
103 related_tpl = NodeTemplate(node, self.templates, self.custom_def)
104 relationship = value.get('relationship') \
105 if isinstance(value, dict) else None
106 # check if it's type has relationship defined
108 parent_reqs = self.type_definition.get_all_requirements()
109 if parent_reqs is None:
110 ExceptionCollector.appendException(
111 ValidationError(message='parent_req is ' +
114 for key in req.keys():
115 for req_dict in parent_reqs:
116 if key in req_dict.keys():
117 relationship = (req_dict.get(key).
121 found_relationship_tpl = False
122 # apply available relationship templates if found
123 if self.available_rel_tpls:
124 for tpl in self.available_rel_tpls:
125 if tpl.name == relationship:
126 rtype = RelationshipType(tpl.type, None,
128 explicit_relation[rtype] = related_tpl
129 tpl.target = related_tpl
131 self.relationship_tpl.append(tpl)
132 found_relationship_tpl = True
133 # create relationship template object.
134 rel_prfx = self.type_definition.RELATIONSHIP_PREFIX
135 if not found_relationship_tpl:
136 if isinstance(relationship, dict):
137 relationship = relationship.get('type')
139 if self.available_rel_types and \
140 relationship in self.available_rel_types.keys():
142 elif not relationship.startswith(rel_prfx):
143 relationship = rel_prfx + relationship
145 ExceptionCollector.appendException(
146 MissingRequiredFieldError(
147 what=_('"relationship" used in template '
148 '"%s"') % related_tpl.name,
150 for rtype in self.type_definition.relationship.keys():
151 if rtype.type == relationship:
152 explicit_relation[rtype] = related_tpl
153 related_tpl._add_relationship_template(req,
156 elif self.available_rel_types:
157 if relationship in self.available_rel_types.keys():
158 rel_type_def = self.available_rel_types.\
160 if 'derived_from' in rel_type_def:
162 rel_type_def.get('derived_from')
163 if not super_type.startswith(rel_prfx):
164 super_type = rel_prfx + super_type
165 if rtype.type == super_type:
166 explicit_relation[rtype] = related_tpl
168 _add_relationship_template(
169 req, rtype.type, self)
170 return explicit_relation
172 def _add_relationship_template(self, requirement, rtype, source):
173 req = requirement.copy()
175 tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
176 self.relationship_tpl.append(tpl)
178 def get_relationship_template(self):
179 return self.relationship_tpl
181 def _add_next(self, nodetpl, relationship):
182 self.related[nodetpl] = relationship
185 def related_nodes(self):
187 for relation, node in self.type_definition.relationship.items():
188 for tpl in self.templates:
190 self.related[NodeTemplate(tpl)] = relation
191 return self.related.keys()
193 def validate(self, tosca_tpl=None):
194 self._validate_capabilities()
195 self._validate_requirements()
196 self._validate_properties(self.entity_tpl, self.type_definition)
197 self._validate_interfaces()
198 for prop in self.get_properties_objects():
201 def _validate_requirements(self):
202 type_requires = self.type_definition.get_all_requirements()
203 allowed_reqs = ["template"]
205 for treq in type_requires:
206 for key, value in treq.items():
207 allowed_reqs.append(key)
208 if isinstance(value, dict):
210 allowed_reqs.append(key)
212 requires = self.type_definition.get_value(self.REQUIREMENTS,
215 if not isinstance(requires, list):
216 ExceptionCollector.appendException(
218 what='"requirements" of template "%s"' % self.name,
222 for r1, value in req.items():
223 if isinstance(value, dict):
224 self._validate_requirements_keys(value)
225 self._validate_requirements_properties(value)
226 allowed_reqs.append(r1)
227 self._common_validate_field(req, allowed_reqs,
230 def _validate_requirements_properties(self, requirements):
231 # TODO(anyone): Only occurrences property of the requirements is
232 # validated here. Validation of other requirement properties are being
233 # validated in different files. Better to keep all the requirements
234 # properties validation here.
235 for key, value in requirements.items():
236 if key == 'occurrences':
237 self._validate_occurrences(value)
240 def _validate_occurrences(self, occurrences):
241 DataEntity.validate_datatype('list', occurrences)
242 for value in occurrences:
243 DataEntity.validate_datatype('integer', value)
244 if len(occurrences) != 2 or not (0 <= occurrences[0] <= occurrences[1]) \
245 or occurrences[1] == 0:
246 ExceptionCollector.appendException(
247 InvalidPropertyValueError(what=(occurrences)))
249 def _validate_requirements_keys(self, requirement):
250 for key in requirement.keys():
251 if key not in self.REQUIREMENTS_SECTION:
252 ExceptionCollector.appendException(
254 what='"requirements" of template "%s"' % self.name,
257 def _validate_interfaces(self):
258 ifaces = self.type_definition.get_value(self.INTERFACES,
261 for name, value in ifaces.items():
262 if name in (LIFECYCLE, LIFECYCLE_SHORTNAME):
263 self._common_validate_field(
264 value, InterfacesDef.
265 interfaces_node_lifecycle_operations,
267 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
268 self._common_validate_field(
269 value, InterfacesDef.
270 interfaces_relationship_configure_operations,
272 elif name in self.type_definition.interfaces.keys():
273 self._common_validate_field(
275 self._collect_custom_iface_operations(name),
278 ExceptionCollector.appendException(
280 what='"interfaces" of template "%s"' %
281 self.name, field=name))
283 def _collect_custom_iface_operations(self, name):
284 allowed_operations = []
285 nodetype_iface_def = self.type_definition.interfaces[name]
286 allowed_operations.extend(nodetype_iface_def.keys())
287 if 'type' in nodetype_iface_def:
288 iface_type = nodetype_iface_def['type']
289 if iface_type in self.type_definition.custom_def:
290 iface_type_def = self.type_definition.custom_def[iface_type]
292 iface_type_def = self.type_definition.TOSCA_DEF[iface_type]
293 allowed_operations.extend(iface_type_def.keys())
294 allowed_operations = [op for op in allowed_operations if
295 op not in INTERFACE_DEF_RESERVED_WORDS]
296 return allowed_operations
298 def _validate_fields(self, nodetemplate):
299 for name in nodetemplate.keys():
300 if name not in self.SECTIONS and name not in self.SPECIAL_SECTIONS:
301 ExceptionCollector.appendException(
302 UnknownFieldError(what='Node template "%s"' % self.name,