6f37dfbeb4881047d4986d06705b124f4e5e5793
[sdc/sdc-distribution-client.git] /
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
4 #
5 #         http://www.apache.org/licenses/LICENSE-2.0
6 #
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
11 #    under the License.
12
13
14 import logging
15
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
34
35 log = logging.getLogger('tosca')
36
37
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],
43                                            'node_type',
44                                            custom_def)
45         self.templates = node_templates
46         self._validate_fields(node_templates[name])
47         self.custom_def = custom_def
48         self.related = {}
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
54
55     @property
56     def relationships(self):
57         if not self._relationships:
58             requires = self.requirements
59             if requires and isinstance(requires, list):
60                 for r in requires:
61                     for r1, value in r.items():
62                         explicit = self._get_explicit_relationship(r, value)
63                         if explicit:
64                             for key, value in explicit.items():
65                                 self._relationships[key] = value
66         return self._relationships
67
68     def _get_explicit_relationship(self, req, value):
69         """Handle explicit relationship
70
71         For example,
72         - req:
73             node: DBMS
74             relationship: tosca.relationships.HostedOn
75         """
76         explicit_relation = {}
77         node = value.get('node') if isinstance(value, dict) else value
78
79         if node:
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))
87                 return
88
89             if node not in self.templates:
90                 ExceptionCollector.appendException(
91                     KeyError(_('Node template "%s" was not found.') % node))
92                 return
93
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
98             if not relationship:
99                 parent_reqs = self.type_definition.get_all_requirements()
100                 if parent_reqs is None:
101                     ExceptionCollector.appendException(
102                         ValidationError(message='parent_req is ' +
103                                         str(parent_reqs)))
104                 else:
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).
109                                                 get('relationship'))
110                                 break
111             if relationship:
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,
118                                                      self.custom_def)
119                             explicit_relation[rtype] = related_tpl
120                             tpl.target = related_tpl
121                             tpl.source = self
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')
129                         if relationship:
130                             if self.available_rel_types and \
131                                relationship in self.available_rel_types.keys():
132                                 pass
133                             elif not relationship.startswith(rel_prfx):
134                                 relationship = rel_prfx + relationship
135                         else:
136                             ExceptionCollector.appendException(
137                                 MissingRequiredFieldError(
138                                     what=_('"relationship" used in template '
139                                            '"%s"') % related_tpl.name,
140                                     required=self.TYPE))
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,
145                                                                    rtype.type,
146                                                                    self)
147                         elif self.available_rel_types:
148                             if relationship in self.available_rel_types.keys():
149                                 rel_type_def = self.available_rel_types.\
150                                     get(relationship)
151                                 if 'derived_from' in rel_type_def:
152                                     super_type = \
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
158                                         related_tpl.\
159                                             _add_relationship_template(
160                                                 req, rtype.type, self)
161         return explicit_relation
162
163     def _add_relationship_template(self, requirement, rtype, source):
164         req = requirement.copy()
165         req['type'] = rtype
166         tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
167         self.relationship_tpl.append(tpl)
168
169     def get_relationship_template(self):
170         return self.relationship_tpl
171
172     def _add_next(self, nodetpl, relationship):
173         self.related[nodetpl] = relationship
174
175     @property
176     def related_nodes(self):
177         if not self.related:
178             for relation, node in self.type_definition.relationship.items():
179                 for tpl in self.templates:
180                     if tpl == node.type:
181                         self.related[NodeTemplate(tpl)] = relation
182         return self.related.keys()
183
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():
190             prop.validate()
191
192     def _validate_requirements(self):
193         type_requires = self.type_definition.get_all_requirements()
194         allowed_reqs = ["template"]
195         if type_requires:
196             for treq in type_requires:
197                 for key, value in treq.items():
198                     allowed_reqs.append(key)
199                     if isinstance(value, dict):
200                         for key in value:
201                             allowed_reqs.append(key)
202
203         requires = self.type_definition.get_value(self.REQUIREMENTS,
204                                                   self.entity_tpl)
205         if requires:
206             if not isinstance(requires, list):
207                 ExceptionCollector.appendException(
208                     TypeMismatchError(
209                         what='"requirements" of template "%s"' % self.name,
210                         type='list'))
211             else:
212                 for req in requires:
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,
219                                                 'requirements')
220
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)
229                 break
230
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)))
239
240     def _validate_requirements_keys(self, requirement):
241         for key in requirement.keys():
242             if key not in self.REQUIREMENTS_SECTION:
243                 ExceptionCollector.appendException(
244                     UnknownFieldError(
245                         what='"requirements" of template "%s"' % self.name,
246                         field=key))
247
248     def _validate_interfaces(self):
249         ifaces = self.type_definition.get_value(self.INTERFACES,
250                                                 self.entity_tpl)
251         if ifaces:
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,
257                         'interfaces')
258                 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
259                     self._common_validate_field(
260                         value, InterfacesDef.
261                         interfaces_relationship_configure_operations,
262                         'interfaces')
263                 elif name in self.type_definition.interfaces.keys():
264                     self._common_validate_field(
265                         value,
266                         self._collect_custom_iface_operations(name),
267                         'interfaces')
268                 else:
269                     ExceptionCollector.appendException(
270                         UnknownFieldError(
271                             what='"interfaces" of template "%s"' %
272                             self.name, field=name))
273
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]
282             else:
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
288
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,
294                                       field=name))