ee7622c7a2af7f712661c99b010e0b6d2fc0837e
[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     @property
65     def relationships(self):
66         if not self._relationships:
67             requires = self.requirements
68             if requires and isinstance(requires, list):
69                 for r in requires:
70                     for r1, value in r.items():
71                         explicit = self._get_explicit_relationship(r, value)
72                         if explicit:
73                             for key, value in explicit.items():
74                                 self._relationships[key] = value
75         return self._relationships
76
77     def _get_explicit_relationship(self, req, value):
78         """Handle explicit relationship
79
80         For example,
81         - req:
82             node: DBMS
83             relationship: tosca.relationships.HostedOn
84         """
85         explicit_relation = {}
86         node = value.get('node') if isinstance(value, dict) else value
87
88         if node:
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))
96                 return
97
98             if node not in self.templates:
99                 ExceptionCollector.appendException(
100                     KeyError(_('Node template "%s" was not found.') % node))
101                 return
102
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
107             if not relationship:
108                 parent_reqs = self.type_definition.get_all_requirements()
109                 if parent_reqs is None:
110                     ExceptionCollector.appendException(
111                         ValidationError(message='parent_req is ' +
112                                         str(parent_reqs)))
113                 else:
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).
118                                                 get('relationship'))
119                                 break
120             if relationship:
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,
127                                                      self.custom_def)
128                             explicit_relation[rtype] = related_tpl
129                             tpl.target = related_tpl
130                             tpl.source = self
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')
138                         if relationship:
139                             if self.available_rel_types and \
140                                relationship in self.available_rel_types.keys():
141                                 pass
142                             elif not relationship.startswith(rel_prfx):
143                                 relationship = rel_prfx + relationship
144                         else:
145                             ExceptionCollector.appendException(
146                                 MissingRequiredFieldError(
147                                     what=_('"relationship" used in template '
148                                            '"%s"') % related_tpl.name,
149                                     required=self.TYPE))
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,
154                                                                    rtype.type,
155                                                                    self)
156                         elif self.available_rel_types:
157                             if relationship in self.available_rel_types.keys():
158                                 rel_type_def = self.available_rel_types.\
159                                     get(relationship)
160                                 if 'derived_from' in rel_type_def:
161                                     super_type = \
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
167                                         related_tpl.\
168                                             _add_relationship_template(
169                                                 req, rtype.type, self)
170         return explicit_relation
171
172     def _add_relationship_template(self, requirement, rtype, source):
173         req = requirement.copy()
174         req['type'] = rtype
175         tpl = RelationshipTemplate(req, rtype, self.custom_def, self, source)
176         self.relationship_tpl.append(tpl)
177
178     def get_relationship_template(self):
179         return self.relationship_tpl
180
181     def _add_next(self, nodetpl, relationship):
182         self.related[nodetpl] = relationship
183
184     @property
185     def related_nodes(self):
186         if not self.related:
187             for relation, node in self.type_definition.relationship.items():
188                 for tpl in self.templates:
189                     if tpl == node.type:
190                         self.related[NodeTemplate(tpl)] = relation
191         return self.related.keys()
192
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():
199             prop.validate()
200
201     def _validate_requirements(self):
202         type_requires = self.type_definition.get_all_requirements()
203         allowed_reqs = ["template"]
204         if type_requires:
205             for treq in type_requires:
206                 for key, value in treq.items():
207                     allowed_reqs.append(key)
208                     if isinstance(value, dict):
209                         for key in value:
210                             allowed_reqs.append(key)
211
212         requires = self.type_definition.get_value(self.REQUIREMENTS,
213                                                   self.entity_tpl)
214         if requires:
215             if not isinstance(requires, list):
216                 ExceptionCollector.appendException(
217                     TypeMismatchError(
218                         what='"requirements" of template "%s"' % self.name,
219                         type='list'))
220             else:
221                 for req in requires:
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,
228                                                 'requirements')
229
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)
238                 break
239
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)))
248
249     def _validate_requirements_keys(self, requirement):
250         for key in requirement.keys():
251             if key not in self.REQUIREMENTS_SECTION:
252                 ExceptionCollector.appendException(
253                     UnknownFieldError(
254                         what='"requirements" of template "%s"' % self.name,
255                         field=key))
256
257     def _validate_interfaces(self):
258         ifaces = self.type_definition.get_value(self.INTERFACES,
259                                                 self.entity_tpl)
260         if ifaces:
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,
266                         'interfaces')
267                 elif name in (CONFIGURE, CONFIGURE_SHORTNAME):
268                     self._common_validate_field(
269                         value, InterfacesDef.
270                         interfaces_relationship_configure_operations,
271                         'interfaces')
272                 elif name in self.type_definition.interfaces.keys():
273                     self._common_validate_field(
274                         value,
275                         self._collect_custom_iface_operations(name),
276                         'interfaces')
277                 else:
278                     ExceptionCollector.appendException(
279                         UnknownFieldError(
280                             what='"interfaces" of template "%s"' %
281                             self.name, field=name))
282
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]
291             else:
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
297
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,
303                                       field=name))