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):
64 def getJySubstitutionMappings(self):
65 return self.sub_mapping_tosca_template
69 def relationships(self):
70 if not self._relationships:
71 requires = self.requirements
72 if requires and isinstance(requires, list):
74 for r1, value in r.items():
75 explicit = self._get_explicit_relationship(r, value)
77 for key, value in explicit.items():
78 self._relationships[key] = value
79 return self._relationships
81 def _get_explicit_relationship(self, req, value):
82 """Handle explicit relationship
87 relationship: tosca.relationships.HostedOn
89 explicit_relation = {}
90 node = value.get('node') if isinstance(value, dict) else value
93 # TODO(spzala) implement look up once Glance meta data is available
94 # to find a matching TOSCA node using the TOSCA types
95 msg = _('Lookup by TOSCA types is not supported. '
96 'Requirement for "%s" can not be full-filled.') % self.name
97 if (node in list(self.type_definition.TOSCA_DEF.keys())
98 or node in self.custom_def):
99 ExceptionCollector.appendException(NotImplementedError(msg))
102 if node not in self.templates:
103 ExceptionCollector.appendException(
104 KeyError(_('Node template "%s" was not found.') % node))
107 related_tpl = NodeTemplate(node, self.templates, self.custom_def)
108 relationship = value.get('relationship') \
109 if isinstance(value, dict) else None
110 # check if it's type has relationship defined
112 parent_reqs = self.type_definition.get_all_requirements()
113 if parent_reqs is None:
114 ExceptionCollector.appendException(
115 ValidationError(message='parent_req is ' +
118 for key in req.keys():
119 for req_dict in parent_reqs:
120 if key in req_dict.keys():
121 relationship = (req_dict.get(key).
125 found_relationship_tpl = False
126 # apply available relationship templates if found
127 if self.available_rel_tpls:
128 for tpl in self.available_rel_tpls:
129 if tpl.name == relationship:
130 rtype = RelationshipType(tpl.type, None,
132 explicit_relation[rtype] = related_tpl
133 tpl.target = related_tpl
135 self.relationship_tpl.append(tpl)
136 found_relationship_tpl = True
137 # create relationship template object.
138 rel_prfx = self.type_definition.RELATIONSHIP_PREFIX
139 if not found_relationship_tpl:
140 if isinstance(relationship, dict):
141 relationship = relationship.get('type')
143 if self.available_rel_types and \
144 relationship in self.available_rel_types.keys():
146 elif not relationship.startswith(rel_prfx):
147 relationship = rel_prfx + relationship
149 ExceptionCollector.appendException(
150 MissingRequiredFieldError(
151 what=_('"relationship" used in template '
152 '"%s"') % related_tpl.name,
154 for rtype in self.type_definition.relationship.keys():
155 if rtype.type == relationship:
156 explicit_relation[rtype] = related_tpl
157 related_tpl._add_relationship_template(req,
160 elif self.available_rel_types:
161 if relationship in self.available_rel_types.keys():
162 rel_type_def = self.available_rel_types.\
164 if 'derived_from' in rel_type_def:
166 rel_type_def.get('derived_from')
167 if not super_type.startswith(rel_prfx):
168 super_type = rel_prfx + super_type
169 if rtype.type == super_type:
170 explicit_relation[rtype] = related_tpl
172 _add_relationship_template(
173 req, rtype.type, self)
174 return explicit_relation
176 def _add_relationship_template(self, requirement, rtype, source):
177 req = requirement.copy()
179 tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
180 self.relationship_tpl.append(tpl)
182 def get_relationship_template(self):
183 return self.relationship_tpl
185 def _add_next(self, nodetpl, relationship):
186 self.related[nodetpl] = relationship
189 def related_nodes(self):
191 for relation, node in self.type_definition.relationship.items():
192 for tpl in self.templates:
194 self.related[NodeTemplate(tpl)] = relation
195 return self.related.keys()
197 def validate(self, tosca_tpl=None):
198 self._validate_capabilities()
199 self._validate_requirements()
200 self._validate_properties(self.entity_tpl, self.type_definition)
201 self._validate_interfaces()
202 for prop in self.get_properties_objects():
205 def _validate_requirements(self):
206 type_requires = self.type_definition.get_all_requirements()
207 allowed_reqs = ["template"]
209 for treq in type_requires:
210 for key, value in treq.items():
211 allowed_reqs.append(key)
212 if isinstance(value, dict):
214 allowed_reqs.append(key)
216 requires = self.type_definition.get_value(self.REQUIREMENTS,
219 if not isinstance(requires, list):
220 ExceptionCollector.appendException(
222 what='"requirements" of template "%s"' % self.name,
226 for r1, value in req.items():
227 if isinstance(value, dict):
228 self._validate_requirements_keys(value)
229 self._validate_requirements_properties(value)
230 allowed_reqs.append(r1)
231 self._common_validate_field(req, allowed_reqs,
234 def _validate_requirements_properties(self, requirements):
235 # TODO(anyone): Only occurrences property of the requirements is
236 # validated here. Validation of other requirement properties are being
237 # validated in different files. Better to keep all the requirements
238 # properties validation here.
239 for key, value in requirements.items():
240 if key == 'occurrences':
241 self._validate_occurrences(value)
244 def _validate_occurrences(self, occurrences):
245 DataEntity.validate_datatype('list', occurrences)
246 for value in occurrences:
247 DataEntity.validate_datatype('integer', value)
248 if len(occurrences) != 2 or not (0 <= occurrences[0] <= occurrences[1]) \
249 or occurrences[1] == 0:
250 ExceptionCollector.appendException(
251 InvalidPropertyValueError(what=(occurrences)))
253 def _validate_requirements_keys(self, requirement):
254 for key in requirement.keys():
255 if key not in self.REQUIREMENTS_SECTION:
256 ExceptionCollector.appendException(
258 what='"requirements" of template "%s"' % self.name,
261 def _validate_interfaces(self):
262 ifaces = self.type_definition.get_value(self.INTERFACES,
265 for name, value in ifaces.items():
266 if name in (LIFECYCLE, LIFECYCLE_SHORTNAME):
267 self._common_validate_field(
268 value, InterfacesDef.
269 interfaces_node_lifecycle_operations,
271 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
272 self._common_validate_field(
273 value, InterfacesDef.
274 interfaces_relationship_configure_operations,
276 elif name in self.type_definition.interfaces.keys():
277 self._common_validate_field(
279 self._collect_custom_iface_operations(name),
282 ExceptionCollector.appendException(
284 what='"interfaces" of template "%s"' %
285 self.name, field=name))
287 def _collect_custom_iface_operations(self, name):
288 allowed_operations = []
289 nodetype_iface_def = self.type_definition.interfaces[name]
290 allowed_operations.extend(nodetype_iface_def.keys())
291 if 'type' in nodetype_iface_def:
292 iface_type = nodetype_iface_def['type']
293 if iface_type in self.type_definition.custom_def:
294 iface_type_def = self.type_definition.custom_def[iface_type]
296 iface_type_def = self.type_definition.TOSCA_DEF[iface_type]
297 allowed_operations.extend(iface_type_def.keys())
298 allowed_operations = [op for op in allowed_operations if
299 op not in INTERFACE_DEF_RESERVED_WORDS]
300 return allowed_operations
302 def _validate_fields(self, nodetemplate):
303 for name in nodetemplate.keys():
304 if name not in self.SECTIONS and name not in self.SPECIAL_SECTIONS:
305 ExceptionCollector.appendException(
306 UnknownFieldError(what='Node template "%s"' % self.name,