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 org.openecomp.sdc.toscaparser.jython import JyNodeTemplate
35 log = logging.getLogger('tosca')
38 class NodeTemplate(EntityTemplate, JyNodeTemplate):
39 '''Node template from a Tosca profile.'''
40 def __init__(self, name, node_templates, custom_def=None,
41 available_rel_tpls=None, available_rel_types=None):
42 super(NodeTemplate, self).__init__(name, node_templates[name],
45 self.templates = node_templates
46 self._validate_fields(node_templates[name])
47 self.custom_def = custom_def
49 self.relationship_tpl = []
50 self.available_rel_tpls = available_rel_tpls
51 self.available_rel_types = available_rel_types
52 self._relationships = {}
53 self.sub_mapping_tosca_template = None
56 def relationships(self):
57 if not self._relationships:
58 requires = self.requirements
59 if requires and isinstance(requires, list):
61 for r1, value in r.items():
62 explicit = self._get_explicit_relationship(r, value)
64 for key, value in explicit.items():
65 self._relationships[key] = value
66 return self._relationships
68 def _get_explicit_relationship(self, req, value):
69 """Handle explicit relationship
74 relationship: tosca.relationships.HostedOn
76 explicit_relation = {}
77 node = value.get('node') if isinstance(value, dict) else value
80 # TODO(spzala) implement look up once Glance meta data is available
81 # to find a matching TOSCA node using the TOSCA types
82 msg = _('Lookup by TOSCA types is not supported. '
83 'Requirement for "%s" can not be full-filled.') % self.name
84 if (node in list(self.type_definition.TOSCA_DEF.keys())
85 or node in self.custom_def):
86 ExceptionCollector.appendException(NotImplementedError(msg))
89 if node not in self.templates:
90 ExceptionCollector.appendException(
91 KeyError(_('Node template "%s" was not found.') % node))
94 related_tpl = NodeTemplate(node, self.templates, self.custom_def)
95 relationship = value.get('relationship') \
96 if isinstance(value, dict) else None
97 # check if it's type has relationship defined
99 parent_reqs = self.type_definition.get_all_requirements()
100 if parent_reqs is None:
101 ExceptionCollector.appendException(
102 ValidationError(message='parent_req is ' +
105 for key in req.keys():
106 for req_dict in parent_reqs:
107 if key in req_dict.keys():
108 relationship = (req_dict.get(key).
112 found_relationship_tpl = False
113 # apply available relationship templates if found
114 if self.available_rel_tpls:
115 for tpl in self.available_rel_tpls:
116 if tpl.name == relationship:
117 rtype = RelationshipType(tpl.type, None,
119 explicit_relation[rtype] = related_tpl
120 tpl.target = related_tpl
122 self.relationship_tpl.append(tpl)
123 found_relationship_tpl = True
124 # create relationship template object.
125 rel_prfx = self.type_definition.RELATIONSHIP_PREFIX
126 if not found_relationship_tpl:
127 if isinstance(relationship, dict):
128 relationship = relationship.get('type')
130 if self.available_rel_types and \
131 relationship in self.available_rel_types.keys():
133 elif not relationship.startswith(rel_prfx):
134 relationship = rel_prfx + relationship
136 ExceptionCollector.appendException(
137 MissingRequiredFieldError(
138 what=_('"relationship" used in template '
139 '"%s"') % related_tpl.name,
141 for rtype in self.type_definition.relationship.keys():
142 if rtype.type == relationship:
143 explicit_relation[rtype] = related_tpl
144 related_tpl._add_relationship_template(req,
147 elif self.available_rel_types:
148 if relationship in self.available_rel_types.keys():
149 rel_type_def = self.available_rel_types.\
151 if 'derived_from' in rel_type_def:
153 rel_type_def.get('derived_from')
154 if not super_type.startswith(rel_prfx):
155 super_type = rel_prfx + super_type
156 if rtype.type == super_type:
157 explicit_relation[rtype] = related_tpl
159 _add_relationship_template(
160 req, rtype.type, self)
161 return explicit_relation
163 def _add_relationship_template(self, requirement, rtype, source):
164 req = requirement.copy()
166 tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
167 self.relationship_tpl.append(tpl)
169 def get_relationship_template(self):
170 return self.relationship_tpl
172 def _add_next(self, nodetpl, relationship):
173 self.related[nodetpl] = relationship
176 def related_nodes(self):
178 for relation, node in self.type_definition.relationship.items():
179 for tpl in self.templates:
181 self.related[NodeTemplate(tpl)] = relation
182 return self.related.keys()
184 def validate(self, tosca_tpl=None):
185 self._validate_capabilities()
186 self._validate_requirements()
187 self._validate_properties(self.entity_tpl, self.type_definition)
188 self._validate_interfaces()
189 for prop in self.get_properties_objects():
192 def _validate_requirements(self):
193 type_requires = self.type_definition.get_all_requirements()
194 allowed_reqs = ["template"]
196 for treq in type_requires:
197 for key, value in treq.items():
198 allowed_reqs.append(key)
199 if isinstance(value, dict):
201 allowed_reqs.append(key)
203 requires = self.type_definition.get_value(self.REQUIREMENTS,
206 if not isinstance(requires, list):
207 ExceptionCollector.appendException(
209 what='"requirements" of template "%s"' % self.name,
213 for r1, value in req.items():
214 if isinstance(value, dict):
215 self._validate_requirements_keys(value)
216 self._validate_requirements_properties(value)
217 allowed_reqs.append(r1)
218 self._common_validate_field(req, allowed_reqs,
221 def _validate_requirements_properties(self, requirements):
222 # TODO(anyone): Only occurrences property of the requirements is
223 # validated here. Validation of other requirement properties are being
224 # validated in different files. Better to keep all the requirements
225 # properties validation here.
226 for key, value in requirements.items():
227 if key == 'occurrences':
228 self._validate_occurrences(value)
231 def _validate_occurrences(self, occurrences):
232 DataEntity.validate_datatype('list', occurrences)
233 for value in occurrences:
234 DataEntity.validate_datatype('integer', value)
235 if len(occurrences) != 2 or not (0 <= occurrences[0] <= occurrences[1]) \
236 or occurrences[1] == 0:
237 ExceptionCollector.appendException(
238 InvalidPropertyValueError(what=(occurrences)))
240 def _validate_requirements_keys(self, requirement):
241 for key in requirement.keys():
242 if key not in self.REQUIREMENTS_SECTION:
243 ExceptionCollector.appendException(
245 what='"requirements" of template "%s"' % self.name,
248 def _validate_interfaces(self):
249 ifaces = self.type_definition.get_value(self.INTERFACES,
252 for name, value in ifaces.items():
253 if name in (LIFECYCLE, LIFECYCLE_SHORTNAME):
254 self._common_validate_field(
255 value, InterfacesDef.
256 interfaces_node_lifecycle_operations,
258 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
259 self._common_validate_field(
260 value, InterfacesDef.
261 interfaces_relationship_configure_operations,
263 elif name in self.type_definition.interfaces.keys():
264 self._common_validate_field(
266 self._collect_custom_iface_operations(name),
269 ExceptionCollector.appendException(
271 what='"interfaces" of template "%s"' %
272 self.name, field=name))
274 def _collect_custom_iface_operations(self, name):
275 allowed_operations = []
276 nodetype_iface_def = self.type_definition.interfaces[name]
277 allowed_operations.extend(nodetype_iface_def.keys())
278 if 'type' in nodetype_iface_def:
279 iface_type = nodetype_iface_def['type']
280 if iface_type in self.type_definition.custom_def:
281 iface_type_def = self.type_definition.custom_def[iface_type]
283 iface_type_def = self.type_definition.TOSCA_DEF[iface_type]
284 allowed_operations.extend(iface_type_def.keys())
285 allowed_operations = [op for op in allowed_operations if
286 op not in INTERFACE_DEF_RESERVED_WORDS]
287 return allowed_operations
289 def _validate_fields(self, nodetemplate):
290 for name in nodetemplate.keys():
291 if name not in self.SECTIONS and name not in self.SPECIAL_SECTIONS:
292 ExceptionCollector.appendException(
293 UnknownFieldError(what='Node template "%s"' % self.name,