fadc8d07737d9ef9431a680a1d0f89847ef00bf8
[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 toscaparser.utils import validateutils
34 from org.openecomp.sdc.toscaparser.jython import JyNodeTemplate
35
36 log = logging.getLogger('tosca')
37
38
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,
45                                            'node_type',
46                                            custom_def)
47         self.templates = node_templates
48         self._validate_fields(nodeTemplate)
49         self.custom_def = custom_def
50         self.related = {}
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
56         self.meta_data = None
57         if self.METADATA in nodeTemplate:
58             self.meta_data = nodeTemplate.get(self.METADATA)
59             validateutils.validate_map(self.meta_data)
60             
61     def getJyMetadata(self):
62         return self.meta_data             
63
64     def getJySubstitutionMappings(self):
65         return self.sub_mapping_tosca_template             
66
67
68     @property
69     def relationships(self):
70         if not self._relationships:
71             requires = self.requirements
72             if requires and isinstance(requires, list):
73                 for r in requires:
74                     for r1, value in r.items():
75                         explicit = self._get_explicit_relationship(r, value)
76                         if explicit:
77                             for key, value in explicit.items():
78                                 self._relationships[key] = value
79         return self._relationships
80
81     def _get_explicit_relationship(self, req, value):
82         """Handle explicit relationship
83
84         For example,
85         - req:
86             node: DBMS
87             relationship: tosca.relationships.HostedOn
88         """
89         explicit_relation = {}
90         node = value.get('node') if isinstance(value, dict) else value
91
92         if node:
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))
100                 return
101
102             if node not in self.templates:
103                 ExceptionCollector.appendException(
104                     KeyError(_('Node template "%s" was not found.') % node))
105                 return
106
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
111             if not relationship:
112                 parent_reqs = self.type_definition.get_all_requirements()
113                 if parent_reqs is None:
114                     ExceptionCollector.appendException(
115                         ValidationError(message='parent_req is ' +
116                                         str(parent_reqs)))
117                 else:
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).
122                                                 get('relationship'))
123                                 break
124             if relationship:
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,
131                                                      self.custom_def)
132                             explicit_relation[rtype] = related_tpl
133                             tpl.target = related_tpl
134                             tpl.source = self
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')
142                         if relationship:
143                             if self.available_rel_types and \
144                                relationship in self.available_rel_types.keys():
145                                 pass
146                             elif not relationship.startswith(rel_prfx):
147                                 relationship = rel_prfx + relationship
148                         else:
149                             ExceptionCollector.appendException(
150                                 MissingRequiredFieldError(
151                                     what=_('"relationship" used in template '
152                                            '"%s"') % related_tpl.name,
153                                     required=self.TYPE))
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,
158                                                                    rtype.type,
159                                                                    self)
160                         elif self.available_rel_types:
161                             if relationship in self.available_rel_types.keys():
162                                 rel_type_def = self.available_rel_types.\
163                                     get(relationship)
164                                 if 'derived_from' in rel_type_def:
165                                     super_type = \
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
171                                         related_tpl.\
172                                             _add_relationship_template(
173                                                 req, rtype.type, self)
174         return explicit_relation
175
176     def _add_relationship_template(self, requirement, rtype, source):
177         req = requirement.copy()
178         req['type'] = rtype
179         tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
180         self.relationship_tpl.append(tpl)
181
182     def get_relationship_template(self):
183         return self.relationship_tpl
184
185     def _add_next(self, nodetpl, relationship):
186         self.related[nodetpl] = relationship
187
188     @property
189     def related_nodes(self):
190         if not self.related:
191             for relation, node in self.type_definition.relationship.items():
192                 for tpl in self.templates:
193                     if tpl == node.type:
194                         self.related[NodeTemplate(tpl)] = relation
195         return self.related.keys()
196
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():
203             prop.validate()
204
205     def _validate_requirements(self):
206         type_requires = self.type_definition.get_all_requirements()
207         allowed_reqs = ["template"]
208         if type_requires:
209             for treq in type_requires:
210                 for key, value in treq.items():
211                     allowed_reqs.append(key)
212                     if isinstance(value, dict):
213                         for key in value:
214                             allowed_reqs.append(key)
215
216         requires = self.type_definition.get_value(self.REQUIREMENTS,
217                                                   self.entity_tpl)
218         if requires:
219             if not isinstance(requires, list):
220                 ExceptionCollector.appendException(
221                     TypeMismatchError(
222                         what='"requirements" of template "%s"' % self.name,
223                         type='list'))
224             else:
225                 for req in requires:
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,
232                                                 'requirements')
233
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)
242                 break
243
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)))
252
253     def _validate_requirements_keys(self, requirement):
254         for key in requirement.keys():
255             if key not in self.REQUIREMENTS_SECTION:
256                 ExceptionCollector.appendException(
257                     UnknownFieldError(
258                         what='"requirements" of template "%s"' % self.name,
259                         field=key))
260
261     def _validate_interfaces(self):
262         ifaces = self.type_definition.get_value(self.INTERFACES,
263                                                 self.entity_tpl)
264         if ifaces:
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,
270                         'interfaces')
271                 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
272                     self._common_validate_field(
273                         value, InterfacesDef.
274                         interfaces_relationship_configure_operations,
275                         'interfaces')
276                 elif name in self.type_definition.interfaces.keys():
277                     self._common_validate_field(
278                         value,
279                         self._collect_custom_iface_operations(name),
280                         'interfaces')
281                 else:
282                     ExceptionCollector.appendException(
283                         UnknownFieldError(
284                             what='"interfaces" of template "%s"' %
285                             self.name, field=name))
286
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]
295             else:
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
301
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,
307                                       field=name))